diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2009-09-27 22:25:15 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2009-09-27 22:25:15 +0000 |
commit | 2a536013ae18d7f7b684d302256f0bf1c44a3692 (patch) | |
tree | a4a4b62adead04c4df2d9646d4f13080699d8253 | |
parent | 0745c2e7833b1a9290330bd3bbaa9b376d8bdef6 (diff) | |
download | netpbm-mirror-2a536013ae18d7f7b684d302256f0bf1c44a3692.tar.gz netpbm-mirror-2a536013ae18d7f7b684d302256f0bf1c44a3692.tar.xz netpbm-mirror-2a536013ae18d7f7b684d302256f0bf1c44a3692.zip |
Rebase Advanced series to trunk: Release 10.48.00
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@998 9d0c8265-081b-0410-96cb-a4ca84ce46f8
35 files changed, 1562 insertions, 2274 deletions
diff --git a/GNUmakefile b/GNUmakefile index 4b01cec2..780ec870 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -150,6 +150,11 @@ ifeq ($(HAVE_INT64),Y) else echo "#define HAVE_INT64 0" >>$@ endif +ifeq ($(DONT_HAVE_PROCESS_MGMT),Y) + echo "#define HAVE_FORK 0" >>$@ +else + echo "#define HAVE_FORK 1" >>$@ +endif echo '/* pm_config.h.in FOLLOWS ... */' >>$@ cat $(SRCDIR)/pm_config.in.h >>$@ $(ENDIANGEN) >>$@ diff --git a/analyzer/pamsumm.c b/analyzer/pamsumm.c index c9b8a7bf..ffb4a033 100644 --- a/analyzer/pamsumm.c +++ b/analyzer/pamsumm.c @@ -73,7 +73,7 @@ parseCommandLine(int argc, char ** const argv, } else if (maxSpec) { cmdlineP->function = FN_MAX; } else - pm_error("You must specify one of -sum, -min, or -max"); + pm_error("You must specify one of -sum, -min, -max, or -mean"); if (argc-1 > 1) pm_error("Too many arguments (%d). File spec is the only argument.", diff --git a/buildtools/configure.pl b/buildtools/configure.pl index 29887642..e82c229c 100755 --- a/buildtools/configure.pl +++ b/buildtools/configure.pl @@ -93,6 +93,29 @@ sub prompt($$) { +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 tmpdir() { # This is our approximation of File::Spec->tmpdir(), which became part of # basic Perl some time after Perl 5.005_03. @@ -322,20 +345,8 @@ sub askAboutCygwin() { $default = "n"; } - my $retval; + my $retval = promptYesNo($default); - while (!defined($retval)) { - my $response = prompt("(y)es or (n)o", $default); - - if (uc($response) =~ /^(Y|YES)$/) { - $retval = $TRUE; - } elsif (uc($response) =~ /^(N|NO)$/) { - $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; } @@ -353,20 +364,7 @@ sub askAboutDjgpp() { $default = "n"; } - my $retval; - - while (!defined($retval)) { - my $response = prompt("(y)es or (n)o", $default); - - if (uc($response) =~ /^(Y|YES)$/) { - $retval = $TRUE; - } elsif (uc($response) =~ /^(N|NO)$/) { - $retval = $FALSE; - } else { - print("'$response' isn't one of the choices. \n" . - "You must choose 'yes' or 'no' (or 'y' or 'n').\n"); - } - } + my $retval = promptYesNo($default); } @@ -485,6 +483,49 @@ sub getPlatform() { +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')) { + print("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'\n"); + } + print("\n"); + + return $retval; +} + + + sub getCompiler($$$) { my ($platform, $subplatform, $compilerR) = @_; #----------------------------------------------------------------------------- @@ -529,45 +570,23 @@ sub getCompiler($$$) { "DARWIN" => 1, ); - if ($gccUsualPlatform{$platform}) { - $$compilerR = "gcc"; - } elsif ($platform eq "WINDOWS" && $subplatform eq "cygwin") { - $$compilerR = "gcc"; - } elsif ($gccOptionalPlatform{$platform}) { - print("GNU compiler or native operating system compiler (cc)?\n"); - print("\n"); - - my $default; + if (commandExists('x86_64-w64-mingw32-gcc')) { + printf("Do you want to use the Mingw-w64 Cross-Compiler?\n"); - if ($platform eq "SOLARIS" || $platform eq "SCO" ) { - $default = "gcc"; - } else { - $default = "cc"; + if (promptYesNo('y') == "y") { + $$compilerR = 'x86_64-w64-mingw32-gcc'; } - - my $response = prompt("gcc or cc", $default); - - if ($response eq "gcc") { + } + if (!defined($$compilerR)) { + if ($gccUsualPlatform{$platform}) { $$compilerR = "gcc"; - } elsif ($response eq "cc") { - $$compilerR = "cc"; + } elsif ($platform eq "WINDOWS" && $subplatform eq "cygwin") { + $$compilerR = "gcc"; + } elsif ($gccOptionalPlatform{$platform}) { + $$compilerR = getGccChoiceFromUser($platform); } else { - print("'$response' isn't one of the choices. \n" . - "You must choose 'gcc' or 'cc'.\n"); - exit 12; - } - - if ($$compilerR eq 'gcc' && !commandExists('gcc')) { - print("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'\n"); + $$compilerR = 'cc'; } - print("\n"); - } else { - $$compilerR = 'cc'; } } @@ -1135,6 +1154,8 @@ sub getLinuxsvgaLibrary($@) { $default = '/usr/lib/svgalib/libvga.so'; } elsif (system('ldconfig -p | grep libvga &>/dev/null') == 0) { $default = 'libvga.so'; + } elsif (-f('/usr/lib/libvga.a')) { + $default = '/usr/lib/libvga.a'; } else { $default = 'none'; } diff --git a/common.mk b/common.mk index 9e13a7c8..ea7e5807 100644 --- a/common.mk +++ b/common.mk @@ -76,7 +76,15 @@ include $(SRCDIR)/version.mk # fully made. .DELETE_ON_ERROR: -NETPBM_INCLUDES := -Iimportinc -I$(SRCDIR)/$(SUBDIR) +# -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 <netpbm/pam.h>, and therefore all _exported_ header files do say +# "<netpbm/pam.h>. + +NETPBM_INCLUDES := -Iimportinc -Iimportinc/netpbm -I$(SRCDIR)/$(SUBDIR) # -I. is needed when builddir != srcdir INCLUDES = -I. $(COMP_INCLUDES) $(NETPBM_INCLUDES) $(EXTERN_INCLUDES) @@ -133,33 +141,27 @@ IMPORTINC_HEADERS := \ $(IMPORTINC_LIB_HEADERS) \ $(IMPORTINC_LIB_UTIL_HEADERS) -IMPORTINC_ROOT_FILES := $(IMPORTINC_ROOT_HEADERS:%=importinc/%) -IMPORTINC_LIB_FILES := $(IMPORTINC_LIB_HEADERS:%=importinc/%) -IMPORTINC_LIB_UTIL_FILES := $(IMPORTINC_LIB_UTIL_HEADERS:%=importinc/%) +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/netpbm \ $(IMPORTINC_ROOT_FILES) \ $(IMPORTINC_LIB_FILES) \ $(IMPORTINC_LIB_UTIL_FILES) \ -importinc/netpbm: - mkdir -p importinc - rm -f $@ - $(SYMLINK) . $@ - -$(IMPORTINC_ROOT_FILES):importinc/%:$(BUILDDIR)/% - mkdir -p importinc +$(IMPORTINC_ROOT_FILES):importinc/netpbm/%:$(BUILDDIR)/% + mkdir -p importinc/netpbm rm -f $@ $(SYMLINK) $< $@ -$(IMPORTINC_LIB_FILES):importinc/%:$(SRCDIR)/lib/% - mkdir -p importinc +$(IMPORTINC_LIB_FILES):importinc/netpbm/%:$(SRCDIR)/lib/% + mkdir -p importinc/netpbm rm -f $@ $(SYMLINK) $< $@ -$(IMPORTINC_LIB_UTIL_FILES):importinc/%:$(SRCDIR)/lib/util/% - mkdir -p importinc +$(IMPORTINC_LIB_UTIL_FILES):importinc/netpbm/%:$(SRCDIR)/lib/util/% + mkdir -p importinc/netpbm rm -f $@ $(SYMLINK) $< $@ diff --git a/converter/other/Makefile b/converter/other/Makefile index 5231bffa..83676aaa 100644 --- a/converter/other/Makefile +++ b/converter/other/Makefile @@ -96,7 +96,7 @@ endif BINARIES = $(PORTBINARIES) pnmtorast rasttopnm ifeq ($(HAVE_PNGLIB),Y) - BINARIES += pnmtopng pngtopnm pngtopam pamrgbatopng + BINARIES += pnmtopng pngtopam pamrgbatopng endif ifneq ($(JPEGLIB),NONE) BINARIES += jpegtopnm pnmtojpeg @@ -120,6 +120,7 @@ MERGEBINARIES = $(BINARIES) EXTRA_OBJECTS = exif.o rast.o bmepsoe.o ifeq ($(HAVE_PNGLIB),Y) EXTRA_OBJECTS += pngtxt.o + EXTRA_OBJECTS += pngx.o endif ifneq ($(JPEGLIB),NONE) EXTRA_OBJECTS += jpegdatasource.o @@ -158,13 +159,13 @@ else PNGLIB_LIBOPTS = $(shell libpng-config --ldflags) endif -pngtopnm pngtopam: %: %.o $(NETPBMLIB) $(LIBOPT) - $(LD) -o $@ $@.o \ +pngtopam: %: %.o pngx.o $(NETPBMLIB) $(LIBOPT) + $(LD) -o $@ $@.o pngx.o \ $(shell $(LIBOPT) $(NETPBMLIB)) \ $(PNGLIB_LIBOPTS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD) -pnmtopng: %: %.o pngtxt.o $(NETPBMLIB) $(LIBOPT) - $(LD) -o $@ $@.o pngtxt.o \ +pnmtopng: %: %.o pngx.o pngtxt.o $(NETPBMLIB) $(LIBOPT) + $(LD) -o $@ $@.o pngx.o pngtxt.o \ $(shell $(LIBOPT) $(NETPBMLIB)) \ $(PNGLIB_LIBOPTS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD) @@ -244,4 +245,9 @@ ifneq ($(TIFF_PREREQ_MISSING),Y) # In October 2005, pamtotiff replaced pnmtotiff cd $(PKGDIR)/bin ; \ $(SYMLINK) pamtotiff$(EXE) pnmtotiff -endif \ No newline at end of file +endif +ifeq ($(HAVE_PNGLIB),Y) +# In September 2009, pngtopam replaced pngtopnm + cd $(PKGDIR)/bin ; \ + $(SYMLINK) pngtopam$(EXE) pngtopnm +endif diff --git a/converter/other/jpeg2000/libjasper/base/jas_stream.c b/converter/other/jpeg2000/libjasper/base/jas_stream.c index 4c84e6c2..19dc7c11 100644 --- a/converter/other/jpeg2000/libjasper/base/jas_stream.c +++ b/converter/other/jpeg2000/libjasper/base/jas_stream.c @@ -130,7 +130,7 @@ #include <io.h> #endif -#include "pm.h" +#include "netpbm/pm.h" #include "jasper/jas_types.h" #include "jasper/jas_stream.h" diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h index d6456f7c..558f4368 100644 --- a/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h +++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h @@ -123,7 +123,7 @@ * Includes. \******************************************************************************/ -#include "pm_c_util.h" +#include "netpbm/pm_c_util.h" #include <jasper/jas_stream.h> #include <jasper/jas_seq.h> diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h index 4d3a4988..fbcb2ffb 100644 --- a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h +++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h @@ -4,7 +4,7 @@ In Netpbm, we do that with pm_config.h, and the original Jasper method doesn't work. */ -#include "pm_config.h" +#include "netpbm/pm_config.h" /* The below macro is intended to be used for type casts. By using this diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig deleted file mode 100644 index 10c1152d..00000000 --- a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) 1999-2000 Image Power, Inc. and the University of - * British Columbia. - * Copyright (c) 2001-2002 Michael David Adams. - * All rights reserved. - */ - -/* __START_OF_JASPER_LICENSE__ - * - * JasPer Software License - * - * IMAGE POWER JPEG-2000 PUBLIC LICENSE - * ************************************ - * - * GRANT: - * - * Permission is hereby granted, free of charge, to any person (the "User") - * obtaining a copy of this software and associated documentation, to deal - * in the JasPer Software without restriction, including without limitation - * the right to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the JasPer Software (in source and binary forms), - * and to permit persons to whom the JasPer Software is furnished to do so, - * provided further that the License Conditions below are met. - * - * License Conditions - * ****************** - * - * A. Redistributions of source code must retain the above copyright notice, - * and this list of conditions, and the following disclaimer. - * - * B. Redistributions in binary form must reproduce the above copyright - * notice, and this list of conditions, and the following disclaimer in - * the documentation and/or other materials provided with the distribution. - * - * C. Neither the name of Image Power, Inc. nor any other contributor - * (including, but not limited to, the University of British Columbia and - * Michael David Adams) may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * D. User agrees that it shall not commence any action against Image Power, - * Inc., the University of British Columbia, Michael David Adams, or any - * other contributors (collectively "Licensors") for infringement of any - * intellectual property rights ("IPR") held by the User in respect of any - * technology that User owns or has a right to license or sublicense and - * which is an element required in order to claim compliance with ISO/IEC - * 15444-1 (i.e., JPEG-2000 Part 1). "IPR" means all intellectual property - * rights worldwide arising under statutory or common law, and whether - * or not perfected, including, without limitation, all (i) patents and - * patent applications owned or licensable by User; (ii) rights associated - * with works of authorship including copyrights, copyright applications, - * copyright registrations, mask work rights, mask work applications, - * mask work registrations; (iii) rights relating to the protection of - * trade secrets and confidential information; (iv) any right analogous - * to those set forth in subsections (i), (ii), or (iii) and any other - * proprietary rights relating to intangible property (other than trademark, - * trade dress, or service mark rights); and (v) divisions, continuations, - * renewals, reissues and extensions of the foregoing (as and to the extent - * applicable) now existing, hereafter filed, issued or acquired. - * - * E. If User commences an infringement action against any Licensor(s) then - * such Licensor(s) shall have the right to terminate User's license and - * all sublicenses that have been granted hereunder by User to other parties. - * - * F. This software is for use only in hardware or software products that - * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1). No license - * or right to this Software is granted for products that do not comply - * with ISO/IEC 15444-1. The JPEG-2000 Part 1 standard can be purchased - * from the ISO. - * - * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. - * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER - * THIS DISCLAIMER. THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND - * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY - * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, - * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE, - * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING. THOSE INTENDING - * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE - * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING - * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS. - * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE - * IS WITH THE USER. SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE - * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY - * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY - * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING, - * REPAIR OR CORRECTION. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, - * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE - * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC., - * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE - * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO - * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR - * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, - * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR - * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF - * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY - * OF SUCH DAMAGES. THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT - * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR - * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING - * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, - * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT - * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE - * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY - * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE - * ("HIGH RISK ACTIVITIES"). LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS - * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES. USER WILL NOT - * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING - * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS - * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE - * NOTICE SPECIFIED IN THIS SECTION. - * - * __END_OF_JASPER_LICENSE__ - */ - -/* - * Primitive Types - * - * $Id$ - */ - -#ifndef JAS_TYPES_H -#define JAS_TYPES_H - -#if defined(HAVE_STDLIB_H) -#include <stdlib.h> -#endif -#if defined(HAVE_STDDEF_H) -#include <stddef.h> -#endif -#if defined(HAVE_SYS_TYPES_H) -#include <sys/types.h> -#endif - -#if defined(HAVE_STDBOOL_H) -/* - * The C language implementation does correctly provide the standard header - * file "stdbool.h". - */ -#include <stdbool.h> -#else - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * The C language implementation does not provide the standard header file - * "stdbool.h" as required by ISO/IEC 9899:1999. Try to compensate for this - * braindamage below. - */ -#if !defined(bool) -#define bool int -#endif -#if !defined(true) -#define true 1 -#endif -#if !defined(false) -#define false 0 -#endif - -#ifdef __cplusplus -} -#endif - -/* pm_config.h defines the FAST integer types if possible (typically by - including <inttypes.h>. If not, the following try to take care - of it. -*/ -/**********/ -#if !defined(INT_FAST8_MAX) -typedef signed char int_fast8_t; -#define INT_FAST8_MIN (-127) -#define INT_FAST8_MAX 128 -#endif -/**********/ -#if !defined(UINT_FAST8_MAX) -typedef unsigned char uint_fast8_t; -#define UINT_FAST8_MAX 255 -#endif -/**********/ -#if !defined(INT_FAST16_MAX) -typedef short int_fast16_t; -#define INT_FAST16_MIN SHRT_MIN -#define INT_FAST16_MAX SHRT_MAX -#endif -/**********/ -#if !defined(UINT_FAST16_MAX) -typedef unsigned short uint_fast16_t; -#define UINT_FAST16_MAX USHRT_MAX -#endif -/**********/ -#if !defined(INT_FAST32_MAX) -typedef int int_fast32_t; -#define INT_FAST32_MIN INT_MIN -#define INT_FAST32_MAX INT_MAX -#endif -/**********/ -#if !defined(UINT_FAST32_MAX) -typedef unsigned int uint_fast32_t; -#define UINT_FAST32_MAX UINT_MAX -#endif -/**********/ -#if !defined(INT_FAST64_MAX) -typedef int int_fast64_t; -#define INT_FAST64_MIN LLONG_MIN -#define INT_FAST64_MAX LLONG_MAX -#endif -/**********/ -#if !defined(UINT_FAST64_MAX) -typedef unsigned int uint_fast64_t; -#define UINT_FAST64_MAX ULLONG_MAX -#endif -/**********/ -#endif - -/* The below macro is intended to be used for type casts. By using this - macro, type casts can be easily located in the source code with - tools like "grep". */ -#define JAS_CAST(t, e) \ - ((t) (e)) - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c b/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c index 4769c408..cb70fd24 100644 --- a/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c +++ b/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c @@ -123,7 +123,7 @@ #include <assert.h> #include <stdlib.h> -#include "pm_c_util.h" +#include "netpbm/pm_c_util.h" #include "jasper/jas_stream.h" #include "jasper/jas_malloc.h" diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h b/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h index 914f8e8a..5f99e021 100644 --- a/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h +++ b/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h @@ -123,7 +123,7 @@ * Includes. \*****************************************************************************/ -#include "pm_c_util.h" +#include "netpbm/pm_c_util.h" #include "jasper/jas_types.h" /*****************************************************************************\ diff --git a/converter/other/pamtotiff.c b/converter/other/pamtotiff.c index 9fac4b47..59261a99 100644 --- a/converter/other/pamtotiff.c +++ b/converter/other/pamtotiff.c @@ -339,34 +339,6 @@ parseCommandLine(int argc, static void -putSample(sample const s, - sample const maxval, - sample const tiff_maxval, - unsigned int const bitspersample, - unsigned char ** const tPP) { - - xelval s2; - - s2 = s; - if (maxval != tiff_maxval) - s2 = s * tiff_maxval / maxval; - if (bitspersample > 8) { - *((unsigned short *)(*tPP)) = s2; - (*tPP) += sizeof(short); - } else - *(*tPP)++ = s2 & 0xff; -} - - - - -/* Note: PUTSAMPLE doesn't work if bitspersample is 1-4. */ - -#define PUTSAMPLE putSample(s, maxval, tiff_maxval, bitspersample, &tP); - - - -static void fillRowOfSubBytePixels(struct pam * const pamP, const tuple * const tuplerow, unsigned char * const buf, @@ -443,6 +415,36 @@ fillRowOfSubBytePixels(struct pam * const pamP, static void +putSample(sample const s, + sample const maxval, + sample const tiffMaxval, + unsigned int const bitspersample, + bool const minIsWhite, + unsigned char ** const tPP) { + + /* Until release 10.48 (September 2009), we ignored the min-is-white + photometric (i.e. treated it like min-is-black). Nobody has ever + complained, but it seems clear to me that that was wrong, so I + changed it. We have always respected it for sub-byte samples, + and have always respected it going the other direction, in + Tifftopnm. + - Bryan. + */ + + xelval const s2 = maxval == tiffMaxval ? s : s * tiffMaxval / maxval; + + xelval const s3 = minIsWhite ? tiffMaxval - s2 : s2; + + if (bitspersample > 8) { + *((unsigned short *)(*tPP)) = s3; + (*tPP) += sizeof(short); + } else + *(*tPP)++ = s3 & 0xff; +} + + + +static void fillRowOfWholeBytePixels(struct pam * const pamP, tuple * const tuplerow, unsigned char * const buf, @@ -450,6 +452,8 @@ fillRowOfWholeBytePixels(struct pam * const pamP, unsigned short const tiffMaxval, unsigned int const bitsPerSample) { + bool const minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE); + unsigned int col; unsigned char * tP; unsigned int planes; @@ -466,7 +470,7 @@ fillRowOfWholeBytePixels(struct pam * const pamP, unsigned int plane; for (plane = 0; plane < planes; ++plane) { putSample(tuplerow[col][plane], pamP->maxval, - tiffMaxval, bitsPerSample, &tP); + tiffMaxval, bitsPerSample, minIsWhite, &tP); /* Advances tP */ } } @@ -495,15 +499,19 @@ writeScanLines(struct pam * const pamP, it's 'buf' parameter, but here it is: Its format depends on the bits per pixel of the TIFF image. If it's 16, 'buf' is an array of short (16 bit) integers, one per raster column. If - it's 8, 'buf' is an array of characters (8 bit integers), one - per image column. If it's less than 8, it's an array of characters, - each of which represents 1-8 raster columns, packed + it's 8, 'buf' is an array of 8 bit unsigned integers, one + per pixel sample. If it's less than 8, it's an array of bytes, + each of which represents 1-8 pixel samples, packed into it in the order specified by the TIFF image's fill order, with don't-care bits on the right such that each byte contains only - whole pixels. + whole samples. In all cases, the array elements are in order left to right going from low array indices to high array indices. + + The samples form pixel values according to the pixel format indicated + by the TIFF photometric. E.g. if it is MINISWHITE, then a pixel is + one sample and a value of 0 for that sample means white. */ MALLOCARRAY(buf, bytesperrow); diff --git a/converter/other/pngtopam.c b/converter/other/pngtopam.c index 534439d2..e88338c4 100644 --- a/converter/other/pngtopam.c +++ b/converter/other/pngtopam.c @@ -22,25 +22,13 @@ #include <math.h> #include <float.h> #include <png.h> /* includes zlib.h and setjmp.h */ -#define VERSION "2.37.4 (5 December 1999) +netpbm" #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" #include "shhopt.h" #include "pam.h" - -/* A hack until we can remove direct access to png_info from the program */ -#if PNG_LIBPNG_VER >= 10400 -#define trans_values trans_color -#define TRANS_ALPHA trans_alpha -#else -#define TRANS_ALPHA trans -#endif - -typedef struct _jmpbuf_wrapper { - jmp_buf jmpbuf; -} jmpbuf_wrapper; +#include "pngx.h" enum alpha_handling {ALPHA_NONE, ALPHA_ONLY, ALPHA_MIX, ALPHA_IN}; @@ -73,7 +61,6 @@ typedef struct { static png_uint_16 maxval; static bool verbose; -static jmpbuf_wrapper pngtopnm_jmpbuf_struct; static void @@ -162,18 +149,138 @@ parseCommandLine(int argc, +static void +verifyFileIsPng(FILE * const ifP, + size_t * const consumedByteCtP) { + + unsigned char buffer[4]; + size_t bytesRead; + + bytesRead = fread(buffer, 1, sizeof(buffer), ifP); + if (bytesRead != sizeof(buffer)) + pm_error("input file is empty or too short"); + + if (png_sig_cmp(buffer, (png_size_t) 0, (png_size_t) sizeof(buffer)) != 0) + pm_error("input file is not a PNG file " + "(does not have the PNG signature in its first 4 bytes)"); + else + *consumedByteCtP = bytesRead; +} + + + +static unsigned int +computePngLineSize(struct pngx * const pngxP) { + + unsigned int const bytesPerSample = + pngxP->info_ptr->bit_depth == 16 ? 2 : 1; + + unsigned int samplesPerPixel; + + switch (pngxP->info_ptr->color_type) { + case PNG_COLOR_TYPE_GRAY_ALPHA: samplesPerPixel = 2; break; + case PNG_COLOR_TYPE_RGB: samplesPerPixel = 3; break; + case PNG_COLOR_TYPE_RGB_ALPHA: samplesPerPixel = 4; break; + default: samplesPerPixel = 1; + } + + if (UINT_MAX / bytesPerSample / samplesPerPixel < pngxP->info_ptr->width) + pm_error("Width %u of PNG is uncomputably large", + (unsigned int)pngxP->info_ptr->width); + + return pngxP->info_ptr->width * bytesPerSample * samplesPerPixel; +} + + + +static void +allocPngRaster(struct pngx * const pngxP, + png_byte *** const pngImageP) { + + unsigned int const lineSize = computePngLineSize(pngxP); + + png_byte ** pngImage; + unsigned int row; + + MALLOCARRAY(pngImage, pngxP->info_ptr->height); + + if (pngImage == NULL) + pm_error("couldn't allocate space for %u PNG raster rows", + (unsigned int)pngxP->info_ptr->height); + + for (row = 0; row < pngxP->info_ptr->height; ++row) { + MALLOCARRAY(pngImage[row], lineSize); + if (pngImage[row] == NULL) + pm_error("couldn't allocate space for %uth row of PNG raster", + row); + } + *pngImageP = pngImage; +} + + + +static void +freePngRaster(png_byte ** const pngRaster, + struct pngx * const pngxP) { + + unsigned int row; + + for (row = 0; row < pngxP->info_ptr->height; ++row) + free(pngRaster[row]); + + free(pngRaster); +} + + + +static void +readPng(struct pngx * const pngxP, + FILE * const ifP, + png_byte *** const pngRasterP) { + + size_t sigByteCt; + png_byte ** pngRaster; + + verifyFileIsPng(ifP, &sigByteCt); + + /* Declare that we already read the signature bytes */ + png_set_sig_bytes(pngxP->png_ptr, (int)sigByteCt); + + png_init_io(pngxP->png_ptr, ifP); + + png_read_info(pngxP->png_ptr, pngxP->info_ptr); + + allocPngRaster(pngxP, &pngRaster); + + if (pngxP->info_ptr->bit_depth < 8) + png_set_packing(pngxP->png_ptr); + + png_read_image(pngxP->png_ptr, pngRaster); + + png_read_end(pngxP->png_ptr, pngxP->info_ptr); + + /* Note that some of info_ptr is not defined until png_read_end() + completes. That's because it comes from chunks that are at the + end of the stream. + */ + + *pngRasterP = pngRaster; +} + + + static png_uint_16 -get_png_val(const png_byte ** const pp, - int const bit_depth) { +getPngVal(const png_byte ** const pp, + int const bitDepth) { png_uint_16 c; - if (bit_depth == 16) - c = (*((*pp)++)) << 8; + if (bitDepth == 16) + c = *(*pp)++ << 8; else c = 0; - c |= (*((*pp)++)); + c |= *(*pp)++; return c; } @@ -251,8 +358,8 @@ setTuple(const struct pam * const pamP, static png_uint_16 -gamma_correct(png_uint_16 const v, - float const g) { +gammaCorrect(png_uint_16 const v, + float const g) { if (g != -1.0) return (png_uint_16) ROUNDU(pow((double) v / maxval, (1.0 / g)) * @@ -263,84 +370,80 @@ gamma_correct(png_uint_16 const v, -static int iscolor (png_color c) -{ - return c.red != c.green || c.green != c.blue; +static bool +isColor(png_color const c) { + + return c.red != c.green || c.green != c.blue; } -static void save_text (png_info *info_ptr, FILE *tfp) -{ - int i, j, k; - - for (i = 0 ; i < info_ptr->num_text ; i++) { - j = 0; - while (info_ptr->text[i].key[j] != '\0' && info_ptr->text[i].key[j] != ' ') - j++; - if (info_ptr->text[i].key[j] != ' ') { - fprintf (tfp, "%s", info_ptr->text[i].key); - for (j = strlen (info_ptr->text[i].key) ; j < 15 ; j++) - putc (' ', tfp); - } else { - fprintf (tfp, "\"%s\"", info_ptr->text[i].key); - for (j = strlen (info_ptr->text[i].key) ; j < 13 ; j++) - putc (' ', tfp); - } - putc (' ', tfp); /* at least one space between key and text */ + + +static void +saveText(struct pngx * const pngxP, + FILE * const tfP) { + + png_info * const info_ptr = pngxP->info_ptr; + + unsigned int i; + + for (i = 0 ; i < info_ptr->num_text; ++i) { + unsigned int j; + j = 0; + + while (info_ptr->text[i].key[j] != '\0' && + info_ptr->text[i].key[j] != ' ') + ++j; + + if (info_ptr->text[i].key[j] != ' ') { + fprintf(tfP, "%s", info_ptr->text[i].key); + for (j = strlen (info_ptr->text[i].key); j < 15; ++j) + putc(' ', tfP); + } else { + fprintf(tfP, "\"%s\"", info_ptr->text[i].key); + for (j = strlen (info_ptr->text[i].key); j < 13; ++j) + putc(' ', tfP); + } + putc(' ', tfP); /* at least one space between key and text */ - for (j = 0 ; j < info_ptr->text[i].text_length ; j++) { - putc (info_ptr->text[i].text[j], tfp); - if (info_ptr->text[i].text[j] == '\n') - for (k = 0 ; k < 16 ; k++) - putc ((int)' ', tfp); + for (j = 0; j < info_ptr->text[i].text_length; ++j) { + putc(info_ptr->text[i].text[j], tfP); + if (info_ptr->text[i].text[j] == '\n') { + unsigned int k; + for (k = 0; k < 16; ++k) + putc(' ', tfP); + } + } + putc('\n', tfP); } - putc ((int)'\n', tfp); - } } -static void show_time (png_info *info_ptr) -{ + + +static void +showTime(struct pngx * const pngxP) { + static const char * const month[] = { "", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; - if (info_ptr->valid & PNG_INFO_tIME) { - pm_message ("modification time: %02d %s %d %02d:%02d:%02d", - info_ptr->mod_time.day, month[info_ptr->mod_time.month], - info_ptr->mod_time.year, info_ptr->mod_time.hour, - info_ptr->mod_time.minute, info_ptr->mod_time.second); - } -} - -static void pngtopnm_error_handler (png_structp png_ptr, png_const_charp msg) -{ - jmpbuf_wrapper *jmpbuf_ptr; - - /* this function, aside from the extra step of retrieving the "error - * pointer" (below) and the fact that it exists within the application - * rather than within libpng, is essentially identical to libpng's - * default error handler. The second point is critical: since both - * setjmp() and longjmp() are called from the same code, they are - * guaranteed to have compatible notions of how big a jmp_buf is, - * regardless of whether _BSD_SOURCE or anything else has (or has not) - * been defined. */ - - pm_message("fatal libpng error: %s", msg); - - jmpbuf_ptr = png_get_error_ptr(png_ptr); - if (jmpbuf_ptr == NULL) { - /* we are completely hosed now */ - pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating."); - } - - longjmp(jmpbuf_ptr->jmpbuf, 1); + if (pngxP->info_ptr->valid & PNG_INFO_tIME) { + pm_message("modification time: %02d %s %d %02d:%02d:%02d", + pngxP->info_ptr->mod_time.day, + month[pngxP->info_ptr->mod_time.month], + pngxP->info_ptr->mod_time.year, + pngxP->info_ptr->mod_time.hour, + pngxP->info_ptr->mod_time.minute, + pngxP->info_ptr->mod_time.second); + } } static void -dump_png_info(png_info *info_ptr) { +dumpPngInfo(struct pngx * const pngxP) { + png_info * const info_ptr = pngxP->info_ptr; const char *type_string; const char *filter_string; @@ -457,73 +560,27 @@ dump_png_info(png_info *info_ptr) { -static unsigned int -computePngLineSize(png_info * const pngInfoP) { - - unsigned int const bytesPerSample = pngInfoP->bit_depth == 16 ? 2 : 1; - - unsigned int samplesPerPixel; - - switch (pngInfoP->color_type) { - case PNG_COLOR_TYPE_GRAY_ALPHA: samplesPerPixel = 2; break; - case PNG_COLOR_TYPE_RGB: samplesPerPixel = 3; break; - case PNG_COLOR_TYPE_RGB_ALPHA: samplesPerPixel = 4; break; - default: samplesPerPixel = 1; - } - - if (UINT_MAX / bytesPerSample / samplesPerPixel < pngInfoP->width) - pm_error("Width %u of PNG is uncomputably large", - (unsigned int)pngInfoP->width); - - return pngInfoP->width * bytesPerSample * samplesPerPixel; -} - - - -static void -allocPngRaster(png_info * const pngInfoP, - png_byte *** const pngImageP) { - - unsigned int const lineSize = computePngLineSize(pngInfoP); - - png_byte ** pngImage; - unsigned int row; - - MALLOCARRAY(pngImage, pngInfoP->height); - - if (pngImage == NULL) - pm_error("couldn't allocate space for %u PNG raster rows", - (unsigned int)pngInfoP->height); - - for (row = 0; row < pngInfoP->height; ++row) { - MALLOCARRAY(pngImage[row], lineSize); - if (pngImage[row] == NULL) - pm_error("couldn't allocate space for %uth row of PNG raster", - row); - } - *pngImageP = pngImage; -} - - - -static void -freePngRaster(png_byte ** const pngRaster, - png_info * const pngInfoP) { +static const png_color_16 * +transColor(struct pngx * const pngxP) { - unsigned int row; + png_bytep trans; + int numTrans; + png_color_16 * transColorP; - for (row = 0; row < pngInfoP->height; ++row) - free(pngRaster[row]); + assert(pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)); + + png_get_tRNS(pngxP->png_ptr, pngxP->info_ptr, + &trans, &numTrans, &transColorP); - free(pngRaster); + return transColorP; } static bool -isTransparentColor(pngcolor const color, - png_info * const pngInfoP, - double const totalgamma) { +isTransparentColor(pngcolor const color, + struct pngx * const pngxP, + double const totalgamma) { /*---------------------------------------------------------------------------- Return TRUE iff pixels of color 'color' are supposed to be transparent everywhere they occur. Assume it's an RGB image. @@ -532,8 +589,8 @@ isTransparentColor(pngcolor const color, -----------------------------------------------------------------------------*/ bool retval; - if (pngInfoP->valid & PNG_INFO_tRNS) { - const png_color_16 * const transColorP = &pngInfoP->trans_values; + if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) { + const png_color_16 * const transColorP = transColor(pngxP); /* It seems odd that libpng lets you get gamma-corrected pixel values, but not gamma-corrected transparency or background @@ -549,15 +606,15 @@ isTransparentColor(pngcolor const color, pixels, and just do it ourselves. */ - switch (pngInfoP->color_type) { + switch (pngxP->info_ptr->color_type) { case PNG_COLOR_TYPE_GRAY: - retval = color.r == gamma_correct(transColorP->gray, totalgamma); + retval = color.r == gammaCorrect(transColorP->gray, totalgamma); break; default: retval = - color.r == gamma_correct(transColorP->red, totalgamma) && - color.g == gamma_correct(transColorP->green, totalgamma) && - color.b == gamma_correct(transColorP->blue, totalgamma); + color.r == gammaCorrect(transColorP->red, totalgamma) && + color.g == gammaCorrect(transColorP->green, totalgamma) && + color.b == gammaCorrect(transColorP->blue, totalgamma); } } else retval = FALSE; @@ -567,37 +624,17 @@ isTransparentColor(pngcolor const color, -#define SIG_CHECK_SIZE 4 - static void -read_sig_buf(FILE * const ifP) { - - unsigned char sig_buf[SIG_CHECK_SIZE]; - size_t bytesRead; - - bytesRead = fread(sig_buf, 1, SIG_CHECK_SIZE, ifP); - if (bytesRead != SIG_CHECK_SIZE) - pm_error ("input file is empty or too short"); - - if (png_sig_cmp(sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE) - != 0) - pm_error ("input file is not a PNG file"); -} - - - -static void -setupGammaCorrection(png_struct * const png_ptr, - png_info * const info_ptr, - float const displaygamma, - float * const totalgammaP) { +setupGammaCorrection(struct pngx * const pngxP, + float const displaygamma, + float * const totalgammaP) { if (displaygamma == -1.0) *totalgammaP = -1.0; else { float imageGamma; - if (info_ptr->valid & PNG_INFO_gAMA) - imageGamma = info_ptr->gamma; + if (pngxP->info_ptr->valid & PNG_INFO_gAMA) + imageGamma = pngxP->info_ptr->gamma; else { if (verbose) pm_message("PNG doesn't specify image gamma. Assuming 1.0"); @@ -611,12 +648,12 @@ setupGammaCorrection(png_struct * const png_ptr, "display gamma %4.2f. No conversion.", imageGamma, displaygamma); } else { - png_set_gamma(png_ptr, displaygamma, imageGamma); + png_set_gamma(pngxP->png_ptr, displaygamma, imageGamma); *totalgammaP = imageGamma * displaygamma; /* in case of gamma-corrections, sBIT's as in the PNG-file are not valid anymore */ - info_ptr->valid &= ~PNG_INFO_sBIT; + pngxP->info_ptr->valid &= ~PNG_INFO_sBIT; if (verbose) pm_message("image gamma is %4.2f, " "converted for display gamma of %4.2f", @@ -628,20 +665,25 @@ setupGammaCorrection(png_struct * const png_ptr, static bool -paletteHasPartialTransparency(png_info * const info_ptr) { +paletteHasPartialTransparency(struct pngx * const pngxP) { bool retval; - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - if (info_ptr->valid & PNG_INFO_tRNS) { + if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { + if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) { bool foundGray; unsigned int i; + png_bytep trans; + int numTrans; + png_color_16 * transColorP; + png_get_tRNS(pngxP->png_ptr, pngxP->info_ptr, + &trans, &numTrans, &transColorP); + for (i = 0, foundGray = FALSE; - i < info_ptr->num_trans && !foundGray; + i < numTrans && !foundGray; ++i) { - if (info_ptr->TRANS_ALPHA[i] != 0 && - info_ptr->TRANS_ALPHA[i] != maxval) { + if (trans[i] != 0 && trans[i] != maxval) { foundGray = TRUE; } } @@ -657,9 +699,11 @@ paletteHasPartialTransparency(png_info * const info_ptr) { static void -getComponentSbitFg(png_info * const pngInfoP, - png_byte * const fgSbitP, - bool * const notUniformP) { +getComponentSbitFg(struct pngx * const pngxP, + png_byte * const fgSbitP, + bool * const notUniformP) { + + png_info * const pngInfoP = pngxP->info_ptr; if (pngInfoP->color_type == PNG_COLOR_TYPE_RGB || pngInfoP->color_type == PNG_COLOR_TYPE_RGB_ALPHA || @@ -680,7 +724,7 @@ getComponentSbitFg(png_info * const pngInfoP, static void -getComponentSbit(png_info * const pngInfoP, +getComponentSbit(struct pngx * const pngxP, enum alpha_handling const alphaHandling, png_byte * const componentSbitP, bool * const notUniformP) { @@ -692,26 +736,26 @@ getComponentSbit(png_info * const pngInfoP, the alpha Sbit */ *notUniformP = false; - *componentSbitP = pngInfoP->sig_bit.alpha; + *componentSbitP = pngxP->info_ptr->sig_bit.alpha; break; case ALPHA_NONE: case ALPHA_MIX: /* We aren't going to produce an alpha channel, so we care only about the uniformity of the foreground channels. */ - getComponentSbitFg(pngInfoP, componentSbitP, notUniformP); + getComponentSbitFg(pngxP, componentSbitP, notUniformP); break; case ALPHA_IN: { /* We care about both the foreground and the alpha */ bool fgNotUniform; png_byte fgSbit; - getComponentSbitFg(pngInfoP, &fgSbit, &fgNotUniform); + getComponentSbitFg(pngxP, &fgSbit, &fgNotUniform); if (fgNotUniform) *notUniformP = true; else { - if (fgSbit == pngInfoP->sig_bit.alpha) { + if (fgSbit == pngxP->info_ptr->sig_bit.alpha) { *notUniformP = false; *componentSbitP = fgSbit; } else @@ -724,8 +768,8 @@ getComponentSbit(png_info * const pngInfoP, static void -shiftPalette(png_info * const pngInfoP, - unsigned int const shift) { +shiftPalette(struct pngx * const pngxP, + unsigned int const shift) { /*---------------------------------------------------------------------------- Shift every component of every color in the PNG palette right by 'shift' bits because sBIT chunk says only those are significant. @@ -738,10 +782,10 @@ shiftPalette(png_info * const pngInfoP, else { unsigned int i; - for (i = 0; i < pngInfoP->num_palette; ++i) { - pngInfoP->palette[i].red >>= (8 - shift); - pngInfoP->palette[i].green >>= (8 - shift); - pngInfoP->palette[i].blue >>= (8 - shift); + for (i = 0; i < pngxP->info_ptr->num_palette; ++i) { + pngxP->info_ptr->palette[i].red >>= (8 - shift); + pngxP->info_ptr->palette[i].green >>= (8 - shift); + pngxP->info_ptr->palette[i].blue >>= (8 - shift); } } } @@ -749,12 +793,11 @@ shiftPalette(png_info * const pngInfoP, static void -computeMaxvalFromSbit(png_struct * const pngP, - png_info * const pngInfoP, +computeMaxvalFromSbit(struct pngx * const pngxP, enum alpha_handling const alphaHandling, png_uint_16 * const maxvalP, bool * const succeededP, - int * const errorlevelP) { + int * const errorLevelP) { /* sBIT handling is very tricky. If we are extracting only the image, we can use the sBIT info for grayscale and color images, @@ -775,37 +818,38 @@ computeMaxvalFromSbit(png_struct * const pngP, Meaningless if they aren't all the same (i.e. 'notUniform') */ - getComponentSbit(pngInfoP, alphaHandling, &componentSigBit, ¬Uniform); + getComponentSbit(pngxP, alphaHandling, &componentSigBit, ¬Uniform); if (notUniform) { pm_message("This program cannot handle " "different bit depths for color channels"); - pm_message("writing file with %u bit resolution", pngInfoP->bit_depth); + pm_message("writing file with %u bit resolution", + pngxP->info_ptr->bit_depth); *succeededP = false; - *errorlevelP = PNMTOPNG_WARNING_LEVEL; + *errorLevelP = PNMTOPNG_WARNING_LEVEL; } else if (componentSigBit > 15) { pm_message("Invalid PNG: says %u significant bits for a component; " "max possible is 16. Ignoring sBIT chunk.", componentSigBit); *succeededP = false; - *errorlevelP = PNMTOPNG_WARNING_LEVEL; + *errorLevelP = PNMTOPNG_WARNING_LEVEL; } else { if (alphaHandling == ALPHA_MIX && - (pngInfoP->color_type == PNG_COLOR_TYPE_RGB_ALPHA || - pngInfoP->color_type == PNG_COLOR_TYPE_GRAY_ALPHA || - paletteHasPartialTransparency(pngInfoP))) + (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA || + paletteHasPartialTransparency(pngxP))) *succeededP = false; else { - if (componentSigBit < pngInfoP->bit_depth) { + if (componentSigBit < pngxP->info_ptr->bit_depth) { pm_message("Image has fewer significant bits, " "writing file with %u bits", componentSigBit); *maxvalP = (1l << componentSigBit) - 1; *succeededP = true; - if (pngInfoP->color_type == PNG_COLOR_TYPE_PALETTE) - shiftPalette(pngInfoP, componentSigBit); + if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + shiftPalette(pngxP, componentSigBit); else - png_set_shift(pngP, &pngInfoP->sig_bit); + png_set_shift(pngxP->png_ptr, &pngxP->info_ptr->sig_bit); } else *succeededP = false; } @@ -815,25 +859,26 @@ computeMaxvalFromSbit(png_struct * const pngP, static void -setupSignificantBits(png_struct * const pngP, - png_info * const pngInfoP, +setupSignificantBits(struct pngx * const pngxP, enum alpha_handling const alphaHandling, png_uint_16 * const maxvalP, - int * const errorlevelP) { + int * const errorLevelP) { /*---------------------------------------------------------------------------- Figure out what maxval would best express the information in the PNG - described by *pngP and *pngInfoP, with 'alpha' telling which - information in the PNG we care about (image or alpha mask). + described by *pngxP, with 'alpha' telling which information in the PNG we + care about (image or alpha mask). Return the result as *maxvalP. - Also set up *pngP for the corresponding significant bits. + Also set up *pngxP for the corresponding significant bits. -----------------------------------------------------------------------------*/ + png_info * const pngInfoP = pngxP->info_ptr; + bool gotItFromSbit; if (pngInfoP->valid & PNG_INFO_sBIT) - computeMaxvalFromSbit(pngP, pngInfoP, alphaHandling, - maxvalP, &gotItFromSbit, errorlevelP); + computeMaxvalFromSbit(pngxP, alphaHandling, + maxvalP, &gotItFromSbit, errorLevelP); else gotItFromSbit = false; @@ -846,7 +891,7 @@ setupSignificantBits(png_struct * const pngP, is plenty */ *maxvalP = 1; - else if (paletteHasPartialTransparency(pngInfoP)) + else if (paletteHasPartialTransparency(pngxP)) /* Use same maxval as PNG transparency palette for simplicity */ @@ -866,22 +911,22 @@ setupSignificantBits(png_struct * const pngP, static bool -imageHasColor(png_info * const info_ptr) { +imageHasColor(struct pngx * const pngxP) { bool retval; - if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY || - info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY || + pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) retval = FALSE; - else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { + else if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { bool foundColor; unsigned int i; for (i = 0, foundColor = FALSE; - i < info_ptr->num_palette && !foundColor; + i < pngxP->info_ptr->num_palette && !foundColor; ++i) { - if (iscolor(info_ptr->palette[i])) + if (isColor(pngxP->info_ptr->palette[i])) foundColor = TRUE; } retval = foundColor; @@ -894,7 +939,7 @@ imageHasColor(png_info * const info_ptr) { static void -determineOutputType(png_info * const pngInfoP, +determineOutputType(struct pngx * const pngxP, enum alpha_handling const alphaHandling, pngcolor const bgColor, xelval const maxval, @@ -909,7 +954,7 @@ determineOutputType(png_info * const pngInfoP, } else { /* The output is a normal Netpbm image */ bool const outputIsColor = - imageHasColor(pngInfoP) || !isGrayscale(bgColor); + imageHasColor(pngxP) || !isGrayscale(bgColor); if (alphaHandling == ALPHA_IN) { *formatP = PAM_FORMAT; @@ -935,11 +980,11 @@ determineOutputType(png_info * const pngInfoP, static void -getBackgroundColor(png_info * const info_ptr, - const char * const requestedColor, - float const totalgamma, - xelval const maxval, - pngcolor * const bgColorP) { +getBackgroundColor(struct pngx * const pngxP, + const char * const requestedColor, + float const totalgamma, + xelval const maxval, + pngcolor * const bgColorP) { /*---------------------------------------------------------------------------- Figure out what the background color should be. If the user requested a particular color ('requestedColor' not null), that's the one. @@ -957,31 +1002,31 @@ getBackgroundColor(png_info * const info_ptr, bgColorP->g = PPM_GETG(backcolor); bgColorP->b = PPM_GETB(backcolor); - } else if (info_ptr->valid & PNG_INFO_bKGD) { + } else if (pngxP->info_ptr->valid & PNG_INFO_bKGD) { /* didn't manage to get libpng to work (bugs?) concerning background processing, therefore we do our own. */ - switch (info_ptr->color_type) { + switch (pngxP->info_ptr->color_type) { case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: bgColorP->r = bgColorP->g = bgColorP->b = - gamma_correct(info_ptr->background.gray, totalgamma); + gammaCorrect(pngxP->info_ptr->background.gray, totalgamma); break; case PNG_COLOR_TYPE_PALETTE: { png_color const rawBgcolor = - info_ptr->palette[info_ptr->background.index]; - bgColorP->r = gamma_correct(rawBgcolor.red, totalgamma); - bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma); - bgColorP->b = gamma_correct(rawBgcolor.blue, totalgamma); + pngxP->info_ptr->palette[pngxP->info_ptr->background.index]; + bgColorP->r = gammaCorrect(rawBgcolor.red, totalgamma); + bgColorP->g = gammaCorrect(rawBgcolor.green, totalgamma); + bgColorP->b = gammaCorrect(rawBgcolor.blue, totalgamma); } break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: { - png_color_16 const rawBgcolor = info_ptr->background; + png_color_16 const rawBgcolor = pngxP->info_ptr->background; - bgColorP->r = gamma_correct(rawBgcolor.red, totalgamma); - bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma); - bgColorP->b = gamma_correct(rawBgcolor.blue, totalgamma); + bgColorP->r = gammaCorrect(rawBgcolor.red, totalgamma); + bgColorP->g = gammaCorrect(rawBgcolor.green, totalgamma); + bgColorP->b = gammaCorrect(rawBgcolor.blue, totalgamma); } break; } @@ -992,14 +1037,62 @@ getBackgroundColor(png_info * const info_ptr, -#define GET_PNG_VAL(p) get_png_val(&(p), pngInfoP->bit_depth) +static void +warnNonsquarePixels(struct pngx * const pngxP, + int * const errorLevelP) { + + if (pngxP->info_ptr->valid & PNG_INFO_pHYs) { + float const r = + (float)pngxP->info_ptr->x_pixels_per_unit / + pngxP->info_ptr->y_pixels_per_unit; + + if (r != 1.0) { + pm_message ("warning - non-square pixels; " + "to fix do a 'pamscale -%cscale %g'", + r < 1.0 ? 'x' : 'y', + r < 1.0 ? 1.0 / r : r ); + *errorLevelP = PNMTOPNG_WARNING_LEVEL; + } + } +} + + + +static png_uint_16 +paletteAlpha(struct pngx * const pngxP, + png_uint_16 const index, + sample const maxval) { + + png_uint_16 retval; + + if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) { + png_bytep trans; + int numTrans; + png_color_16 * transColorP; + + png_get_tRNS(pngxP->png_ptr, pngxP->info_ptr, + &trans, &numTrans, &transColorP); + + if (index < numTrans) + retval = trans[index]; + else + retval = maxval; + } else + retval = maxval; + + return retval; +} + + + +#define GET_PNG_VAL(p) getPngVal(&(p), pngxP->info_ptr->bit_depth) static void makeTupleRow(const struct pam * const pamP, const tuple * const tuplerow, - png_info * const pngInfoP, + struct pngx * const pngxP, const png_byte * const pngRasterRow, pngcolor const bgColor, enum alpha_handling const alphaHandling, @@ -1009,13 +1102,13 @@ makeTupleRow(const struct pam * const pamP, unsigned int col; pngPixelP = &pngRasterRow[0]; /* initial value */ - for (col = 0; col < pngInfoP->width; ++col) { - switch (pngInfoP->color_type) { + for (col = 0; col < pngxP->info_ptr->width; ++col) { + switch (pngxP->info_ptr->color_type) { case PNG_COLOR_TYPE_GRAY: { pngcolor fgColor; fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP); setTuple(pamP, tuplerow[col], fgColor, bgColor, alphaHandling, - isTransparentColor(fgColor, pngInfoP, totalgamma) ? + isTransparentColor(fgColor, pngxP, totalgamma) ? 0 : maxval); } break; @@ -1033,7 +1126,7 @@ makeTupleRow(const struct pam * const pamP, case PNG_COLOR_TYPE_PALETTE: { png_uint_16 const index = GET_PNG_VAL(pngPixelP); - png_color const paletteColor = pngInfoP->palette[index]; + png_color const paletteColor = pngxP->info_ptr->palette[index]; pngcolor fgColor; @@ -1042,9 +1135,7 @@ makeTupleRow(const struct pam * const pamP, fgColor.b = paletteColor.blue; setTuple(pamP, tuplerow[col], fgColor, bgColor, alphaHandling, - (pngInfoP->valid & PNG_INFO_tRNS) && - index < pngInfoP->num_trans ? - pngInfoP->TRANS_ALPHA[index] : maxval); + paletteAlpha(pngxP, index, maxval)); } break; @@ -1055,7 +1146,7 @@ makeTupleRow(const struct pam * const pamP, fgColor.g = GET_PNG_VAL(pngPixelP); fgColor.b = GET_PNG_VAL(pngPixelP); setTuple(pamP, tuplerow[col], fgColor, bgColor, alphaHandling, - isTransparentColor(fgColor, pngInfoP, totalgamma) ? + isTransparentColor(fgColor, pngxP, totalgamma) ? 0 : maxval); } break; @@ -1074,7 +1165,8 @@ makeTupleRow(const struct pam * const pamP, break; default: - pm_error("unknown PNG color type: %d", pngInfoP->color_type); + pm_error("unknown PNG color type: %d", + pngxP->info_ptr->color_type); } } } @@ -1108,15 +1200,15 @@ reportOutputFormat(const struct pam * const pamP) { static void writeNetpbm(struct pam * const pamP, - png_info * const pngInfoP, + struct pngx * const pngxP, png_byte ** const pngRaster, pngcolor const bgColor, enum alpha_handling const alphaHandling, double const totalgamma) { /*---------------------------------------------------------------------------- Write a Netpbm image of either the image or the alpha mask, according to - 'alphaHandling' that is in the PNG image described by 'pngInfoP' and - pngRaster. + 'alphaHandling' that is in the PNG image described by *pngxP and + pngRaster[][]. *pamP describes the required output image and is consistent with *pngInfoP. @@ -1134,8 +1226,8 @@ writeNetpbm(struct pam * const pamP, tuplerow = pnm_allocpamrow(pamP); - for (row = 0; row < pngInfoP->height; ++row) { - makeTupleRow(pamP, tuplerow, pngInfoP, pngRaster[row], bgColor, + for (row = 0; row < pngxP->info_ptr->height; ++row) { + makeTupleRow(pamP, tuplerow, pngxP, pngRaster[row], bgColor, alphaHandling, totalgamma); pnm_writepamrow(pamP, tuplerow); @@ -1146,97 +1238,62 @@ writeNetpbm(struct pam * const pamP, static void -convertpng(FILE * const ifp, - FILE * const tfp, +convertpng(FILE * const ifP, + FILE * const tfP, struct cmdlineInfo const cmdline, - int * const errorlevelP) { + int * const errorLevelP) { - png_struct * png_ptr; - png_info * info_ptr; - png_byte ** png_image; + png_byte ** pngRaster; pngcolor bgColor; float totalgamma; struct pam pam; + jmp_buf jmpbuf; + struct pngx * pngxP; - *errorlevelP = 0; - - read_sig_buf(ifp); - - png_ptr = png_create_read_struct( - PNG_LIBPNG_VER_STRING, - &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL); - if (png_ptr == NULL) - pm_error("cannot allocate main libpng structure (png_ptr)"); + *errorLevelP = 0; - info_ptr = png_create_info_struct (png_ptr); - if (info_ptr == NULL) - pm_error("cannot allocate LIBPNG structures"); - - if (setjmp(pngtopnm_jmpbuf_struct.jmpbuf)) + if (setjmp(jmpbuf)) pm_error ("setjmp returns error condition"); - png_init_io (png_ptr, ifp); - png_set_sig_bytes (png_ptr, SIG_CHECK_SIZE); - png_read_info (png_ptr, info_ptr); - - allocPngRaster(info_ptr, &png_image); - - if (info_ptr->bit_depth < 8) - png_set_packing (png_ptr); - - setupGammaCorrection(png_ptr, info_ptr, cmdline.gamma, &totalgamma); - - setupSignificantBits(png_ptr, info_ptr, cmdline.alpha, - &maxval, errorlevelP); - - getBackgroundColor(info_ptr, cmdline.background, totalgamma, maxval, - &bgColor); + pngx_create(&pngxP, PNGX_READ, &jmpbuf); - png_read_image(png_ptr, png_image); - png_read_end(png_ptr, info_ptr); + readPng(pngxP, ifP, &pngRaster); if (verbose) - /* Note that some of info_ptr is not defined until png_read_end() - completes. That's because it comes from chunks that are at the - end of the stream. - */ - dump_png_info(info_ptr); + dumpPngInfo(pngxP); if (cmdline.time) - show_time(info_ptr); - if (tfp) - save_text(info_ptr, tfp); + showTime(pngxP); + if (tfP) + saveText(pngxP, tfP); - if (info_ptr->valid & PNG_INFO_pHYs) { - float const r = - (float)info_ptr->x_pixels_per_unit / info_ptr->y_pixels_per_unit; - if (r != 1.0) { - pm_message ("warning - non-square pixels; " - "to fix do a 'pamscale -%cscale %g'", - r < 1.0 ? 'x' : 'y', - r < 1.0 ? 1.0 / r : r ); - *errorlevelP = PNMTOPNG_WARNING_LEVEL; - } - } + warnNonsquarePixels(pngxP, errorLevelP); + + setupGammaCorrection(pngxP, cmdline.gamma, &totalgamma); + + setupSignificantBits(pngxP, cmdline.alpha, &maxval, errorLevelP); + getBackgroundColor(pngxP, cmdline.background, totalgamma, maxval, + &bgColor); + pam.size = sizeof(pam); pam.len = PAM_STRUCT_SIZE(maxval); pam.file = stdout; pam.plainformat = 0; - pam.height = info_ptr->height; - pam.width = info_ptr->width; + pam.height = pngxP->info_ptr->height; + pam.width = pngxP->info_ptr->width; pam.maxval = maxval; - determineOutputType(info_ptr, cmdline.alpha, bgColor, maxval, + determineOutputType(pngxP, cmdline.alpha, bgColor, maxval, &pam.format, &pam.depth, pam.tuple_type); - writeNetpbm(&pam, info_ptr, png_image, bgColor, cmdline.alpha, totalgamma); + writeNetpbm(&pam, pngxP, pngRaster, bgColor, cmdline.alpha, totalgamma); fflush(stdout); - freePngRaster(png_image, info_ptr); + freePngRaster(pngRaster, pngxP); - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + pngx_destroy(pngxP); } @@ -1247,7 +1304,7 @@ main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; FILE * ifP; FILE * tfP; - int errorlevel; + int errorLevel; pm_proginit(&argc, argv); @@ -1262,7 +1319,7 @@ main(int argc, const char *argv[]) { else tfP = NULL; - convertpng(ifP, tfP, cmdline, &errorlevel); + convertpng(ifP, tfP, cmdline, &errorLevel); if (tfP) pm_close(tfP); @@ -1270,5 +1327,5 @@ main(int argc, const char *argv[]) { pm_close(ifP); pm_close(stdout); - return errorlevel; + return errorLevel; } diff --git a/converter/other/pngtopnm.c b/converter/other/pngtopnm.c deleted file mode 100644 index ffb98ef7..00000000 --- a/converter/other/pngtopnm.c +++ /dev/null @@ -1,1234 +0,0 @@ -/* -** pngtopnm.c - -** read a Portable Network Graphics file and produce a PNM. -** -** Copyright (C) 1995,1998 by Alexander Lehmann <alex@hal.rhein-main.de> -** and Willem van Schaik <willem@schaik.com> -** -** 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. -** -** modeled after giftopnm by David Koblas and -** with lots of bits pasted from libpng.txt by Guy Eric Schalnat -*/ - -#ifndef PNMTOPNG_WARNING_LEVEL -# define PNMTOPNG_WARNING_LEVEL 0 /* use 0 for backward compatibility, */ -#endif /* 2 for warnings (1 == error) */ - -#include <assert.h> -#include <math.h> -#include <float.h> -#include <png.h> /* includes zlib.h and setjmp.h */ -#define VERSION "2.37.4 (5 December 1999) +netpbm" - -#include "pm_c_util.h" -#include "mallocvar.h" -#include "nstring.h" -#include "shhopt.h" -#include "pnm.h" - -enum alpha_handling {ALPHA_NONE, ALPHA_ONLY, ALPHA_MIX}; - -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; - enum alpha_handling alpha; - const char * background; - float gamma; /* -1.0 means unspecified */ - const char * text; - unsigned int time; -}; - - -typedef struct { -/*---------------------------------------------------------------------------- - A color in a format compatible with the PNG library. - - Note that the PNG library declares types png_color and png_color_16 - which are similar. ------------------------------------------------------------------------------*/ - png_uint_16 r; - png_uint_16 g; - png_uint_16 b; -} pngcolor; - - -static png_uint_16 maxval; -static bool verbose; - - -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 optParseOptions3 on how to parse our options. - */ - optStruct3 opt; - - unsigned int option_def_index; - - unsigned int alphaSpec, mixSpec, backgroundSpec, gammaSpec, textSpec; - - MALLOCARRAY(option_def, 100); - - option_def_index = 0; /* incremented by OPTENT3 */ - OPTENT3(0, "verbose", OPT_FLAG, NULL, - &cmdlineP->verbose, 0); - OPTENT3(0, "alpha", OPT_FLAG, NULL, - &alphaSpec, 0); - OPTENT3(0, "mix", OPT_FLAG, NULL, - &mixSpec, 0); - OPTENT3(0, "background", OPT_STRING, &cmdlineP->background, - &backgroundSpec, 0); - OPTENT3(0, "gamma", OPT_FLOAT, &cmdlineP->gamma, - &gammaSpec, 0); - OPTENT3(0, "text", OPT_STRING, &cmdlineP->text, - &textSpec, 0); - OPTENT3(0, "time", OPT_FLAG, NULL, - &cmdlineP->time, 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 */ - - optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); - /* Uses and sets argc, argv, and some of *cmdlineP and others. */ - - - if (alphaSpec && mixSpec) - pm_error("You cannot specify both -alpha and -mix"); - else if (alphaSpec) - cmdlineP->alpha = ALPHA_ONLY; - else if (mixSpec) - cmdlineP->alpha = ALPHA_MIX; - else - cmdlineP->alpha = ALPHA_NONE; - - if (backgroundSpec && !mixSpec) - pm_error("-background is useless without -mix"); - - if (!backgroundSpec) - cmdlineP->background = NULL; - - if (!gammaSpec) - cmdlineP->gamma = -1.0; - - if (!textSpec) - cmdlineP->text = NULL; - - if (argc-1 < 1) - cmdlineP->inputFilespec = "-"; - else if (argc-1 == 1) - cmdlineP->inputFilespec = argv[1]; - else - pm_error("Program takes at most one argument: input file name. " - "you specified %d", argc-1); -} - - - -static void -pngtopnmErrorHandler(png_structp const png_ptr, - png_const_charp const msg) { - - jmp_buf * jmpbufP; - - /* this function, aside from the extra step of retrieving the "error - pointer" (below) and the fact that it exists within the application - rather than within libpng, is essentially identical to libpng's - default error handler. The second point is critical: since both - setjmp() and longjmp() are called from the same code, they are - guaranteed to have compatible notions of how big a jmp_buf is, - regardless of whether _BSD_SOURCE or anything else has (or has not) - been defined. - */ - - pm_message("fatal libpng error: %s", msg); - - jmpbufP = png_get_error_ptr(png_ptr); - - if (!jmpbufP) { - /* we are completely hosed now */ - pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating."); - } - - longjmp(*jmpbufP, 1); -} - - - -struct pngx { - png_structp png_ptr; - png_infop info_ptr; -}; - - - -static void -pngx_createRead(struct pngx ** const pngxPP, - jmp_buf * const jmpbufP) { - - struct pngx * pngxP; - - MALLOCVAR(pngxP); - - if (!pngxP) - pm_error("Failed to allocate memory for PNG object"); - else { - pngxP->png_ptr = png_create_read_struct( - PNG_LIBPNG_VER_STRING, - jmpbufP, pngtopnmErrorHandler, NULL); - - if (!pngxP->png_ptr) - pm_error("cannot allocate main libpng structure (png_ptr)"); - else { - pngxP->info_ptr = png_create_info_struct(pngxP->png_ptr); - - if (!pngxP->info_ptr) - pm_error("cannot allocate libpng info structure (info_ptr)"); - else - *pngxPP = pngxP; - } - } -} - - - -static void -pngx_destroy(struct pngx * const pngxP) { - - png_destroy_read_struct(&pngxP->png_ptr, &pngxP->info_ptr, NULL); - - free(pngxP); -} - - - -static bool -pngx_chunkIsPresent(struct pngx * const pngxP, - uint32_t const chunkType) { - - return png_get_valid(pngxP->png_ptr, pngxP->info_ptr, chunkType); -} - - - -static void -verifyFileIsPng(FILE * const ifP, - size_t * const consumedByteCtP) { - - unsigned char buffer[4]; - size_t bytesRead; - - bytesRead = fread(buffer, 1, sizeof(buffer), ifP); - if (bytesRead != sizeof(buffer)) - pm_error("input file is empty or too short"); - - if (png_sig_cmp(buffer, (png_size_t) 0, (png_size_t) sizeof(buffer)) != 0) - pm_error("input file is not a PNG file " - "(does not have the PNG signature in its first 4 bytes)"); - else - *consumedByteCtP = bytesRead; -} - - - -static unsigned int -computePngLineSize(struct pngx * const pngxP) { - - unsigned int const bytesPerSample = - pngxP->info_ptr->bit_depth == 16 ? 2 : 1; - - unsigned int samplesPerPixel; - - switch (pngxP->info_ptr->color_type) { - case PNG_COLOR_TYPE_GRAY_ALPHA: samplesPerPixel = 2; break; - case PNG_COLOR_TYPE_RGB: samplesPerPixel = 3; break; - case PNG_COLOR_TYPE_RGB_ALPHA: samplesPerPixel = 4; break; - default: samplesPerPixel = 1; - } - - if (UINT_MAX / bytesPerSample / samplesPerPixel < pngxP->info_ptr->width) - pm_error("Width %u of PNG is uncomputably large", - (unsigned int)pngxP->info_ptr->width); - - return pngxP->info_ptr->width * bytesPerSample * samplesPerPixel; -} - - - -static void -allocPngRaster(struct pngx * const pngxP, - png_byte *** const pngImageP) { - - unsigned int const lineSize = computePngLineSize(pngxP); - - png_byte ** pngImage; - unsigned int row; - - MALLOCARRAY(pngImage, pngxP->info_ptr->height); - - if (pngImage == NULL) - pm_error("couldn't allocate space for %u PNG raster rows", - (unsigned int)pngxP->info_ptr->height); - - for (row = 0; row < pngxP->info_ptr->height; ++row) { - MALLOCARRAY(pngImage[row], lineSize); - if (pngImage[row] == NULL) - pm_error("couldn't allocate space for %uth row of PNG raster", - row); - } - *pngImageP = pngImage; -} - - - -static void -freePngRaster(png_byte ** const pngRaster, - struct pngx * const pngxP) { - - unsigned int row; - - for (row = 0; row < pngxP->info_ptr->height; ++row) - free(pngRaster[row]); - - free(pngRaster); -} - - - -static void -readPng(struct pngx * const pngxP, - FILE * const ifP, - png_byte *** const pngRasterP) { - - size_t sigByteCt; - png_byte ** pngRaster; - - verifyFileIsPng(ifP, &sigByteCt); - - /* Declare that we already read the signature bytes */ - png_set_sig_bytes(pngxP->png_ptr, (int)sigByteCt); - - png_init_io(pngxP->png_ptr, ifP); - - png_read_info(pngxP->png_ptr, pngxP->info_ptr); - - allocPngRaster(pngxP, &pngRaster); - - if (pngxP->info_ptr->bit_depth < 8) - png_set_packing(pngxP->png_ptr); - - png_read_image(pngxP->png_ptr, pngRaster); - - png_read_end(pngxP->png_ptr, pngxP->info_ptr); - - /* Note that some of info_ptr is not defined until png_read_end() - completes. That's because it comes from chunks that are at the - end of the stream. - */ - - *pngRasterP = pngRaster; -} - - - -static png_uint_16 -get_png_val(const png_byte ** const pp, - int const bit_depth) { - - png_uint_16 c; - - if (bit_depth == 16) - c = (*((*pp)++)) << 8; - else - c = 0; - - c |= (*((*pp)++)); - - return c; -} - - - -static bool -isGrayscale(pngcolor const color) { - - return color.r == color.g && color.r == color.b; -} - - - -static void -setXel(xel * const xelP, - pngcolor const foreground, - pngcolor const background, - enum alpha_handling const alpha_handling, - png_uint_16 const alpha) { - - if (alpha_handling == ALPHA_ONLY) { - PNM_ASSIGN1(*xelP, alpha); - } else { - if ((alpha_handling == ALPHA_MIX) && (alpha != maxval)) { - double const opacity = (double)alpha / maxval; - double const transparency = 1.0 - opacity; - - pngcolor mix; - - mix.r = foreground.r * opacity + background.r * transparency + 0.5; - mix.g = foreground.g * opacity + background.g * transparency + 0.5; - mix.b = foreground.b * opacity + background.b * transparency + 0.5; - PPM_ASSIGN(*xelP, mix.r, mix.g, mix.b); - } else - PPM_ASSIGN(*xelP, foreground.r, foreground.g, foreground.b); - } -} - - - -static png_uint_16 -gamma_correct(png_uint_16 const v, - float const g) { - - if (g != -1.0) - return (png_uint_16) ROUNDU(pow((double) v / maxval, (1.0 / g)) * - maxval); - else - return v; -} - - - -static bool -iscolor(png_color const c) { - - return c.red != c.green || c.green != c.blue; -} - - - -static void -saveText(struct pngx * const pngxP, - FILE * const tfP) { - - png_info * const info_ptr = pngxP->info_ptr; - - unsigned int i; - - for (i = 0 ; i < info_ptr->num_text; ++i) { - unsigned int j; - j = 0; - - while (info_ptr->text[i].key[j] != '\0' && - info_ptr->text[i].key[j] != ' ') - ++j; - - if (info_ptr->text[i].key[j] != ' ') { - fprintf(tfP, "%s", info_ptr->text[i].key); - for (j = strlen (info_ptr->text[i].key); j < 15; ++j) - putc(' ', tfP); - } else { - fprintf(tfP, "\"%s\"", info_ptr->text[i].key); - for (j = strlen (info_ptr->text[i].key); j < 13; ++j) - putc(' ', tfP); - } - putc(' ', tfP); /* at least one space between key and text */ - - for (j = 0; j < info_ptr->text[i].text_length; ++j) { - putc(info_ptr->text[i].text[j], tfP); - if (info_ptr->text[i].text[j] == '\n') { - unsigned int k; - for (k = 0; k < 16; ++k) - putc(' ', tfP); - } - } - putc('\n', tfP); - } -} - - - -static void -showTime(struct pngx * const pngxP) { - - static const char * const month[] = { - "", "January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December" - }; - - if (pngxP->info_ptr->valid & PNG_INFO_tIME) { - pm_message("modification time: %02d %s %d %02d:%02d:%02d", - pngxP->info_ptr->mod_time.day, - month[pngxP->info_ptr->mod_time.month], - pngxP->info_ptr->mod_time.year, - pngxP->info_ptr->mod_time.hour, - pngxP->info_ptr->mod_time.minute, - pngxP->info_ptr->mod_time.second); - } -} - - - -static void -dumpPngInfo(struct pngx * const pngxP) { - - png_info * const info_ptr = pngxP->info_ptr; - const char *type_string; - const char *filter_string; - - switch (info_ptr->color_type) { - case PNG_COLOR_TYPE_GRAY: - type_string = "gray"; - break; - - case PNG_COLOR_TYPE_GRAY_ALPHA: - type_string = "gray+alpha"; - break; - - case PNG_COLOR_TYPE_PALETTE: - type_string = "palette"; - break; - - case PNG_COLOR_TYPE_RGB: - type_string = "truecolor"; - break; - - case PNG_COLOR_TYPE_RGB_ALPHA: - type_string = "truecolor+alpha"; - break; - } - - switch (info_ptr->filter_type) { - case PNG_FILTER_TYPE_BASE: - asprintfN(&filter_string, "base filter"); - break; - default: - asprintfN(&filter_string, "unknown filter type %d", - info_ptr->filter_type); - } - - pm_message("reading a %ldw x %ldh image, %d bit%s", - info_ptr->width, info_ptr->height, - info_ptr->bit_depth, info_ptr->bit_depth > 1 ? "s" : ""); - pm_message("%s, %s, %s", - type_string, - info_ptr->interlace_type ? - "Adam7 interlaced" : "not interlaced", - filter_string); - pm_message("background {index, gray, red, green, blue} = " - "{%d, %d, %d, %d, %d}", - info_ptr->background.index, - info_ptr->background.gray, - info_ptr->background.red, - info_ptr->background.green, - info_ptr->background.blue); - - strfree(filter_string); - - if (info_ptr->valid & PNG_INFO_tRNS) - pm_message("tRNS chunk (transparency): %u entries", - info_ptr->num_trans); - else - pm_message("tRNS chunk (transparency): not present"); - - if (info_ptr->valid & PNG_INFO_gAMA) - pm_message("gAMA chunk (image gamma): gamma = %4.2f", info_ptr->gamma); - else - pm_message("gAMA chunk (image gamma): not present"); - - if (info_ptr->valid & PNG_INFO_sBIT) - pm_message("sBIT chunk: present"); - else - pm_message("sBIT chunk: not present"); - - if (info_ptr->valid & PNG_INFO_cHRM) - pm_message("cHRM chunk: present"); - else - pm_message("cHRM chunk: not present"); - - if (info_ptr->valid & PNG_INFO_PLTE) - pm_message("PLTE chunk: %d entries", info_ptr->num_palette); - else - pm_message("PLTE chunk: not present"); - - if (info_ptr->valid & PNG_INFO_bKGD) - pm_message("bKGD chunk: present"); - else - pm_message("bKGD chunk: not present"); - - if (info_ptr->valid & PNG_INFO_PLTE) - pm_message("hIST chunk: present"); - else - pm_message("hIST chunk: not present"); - - if (info_ptr->valid & PNG_INFO_pHYs) - pm_message("pHYs chunk: present"); - else - pm_message("pHYs chunk: not present"); - - if (info_ptr->valid & PNG_INFO_oFFs) - pm_message("oFFs chunk: present"); - else - pm_message("oFFs chunk: not present"); - - if (info_ptr->valid & PNG_INFO_tIME) - pm_message("tIME chunk: present"); - else - pm_message("tIME chunk: not present"); - - if (info_ptr->valid & PNG_INFO_pCAL) - pm_message("pCAL chunk: present"); - else - pm_message("pCAL chunk: not present"); - - if (info_ptr->valid & PNG_INFO_sRGB) - pm_message("sRGB chunk: present"); - else - pm_message("sRGB chunk: not present"); -} - - - -static const png_color_16 * -transColor(struct pngx * const pngxP) { - - png_bytep trans; - int numTrans; - png_color_16 * transColor; - - assert(pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)); - - png_get_tRNS(pngxP->png_ptr, pngxP->info_ptr, - &trans, &numTrans, &transColor); - - return transColor; -} - - - -static bool -isTransparentColor(pngcolor const color, - struct pngx * const pngxP, - double const totalgamma) { -/*---------------------------------------------------------------------------- - Return TRUE iff pixels of color 'color' are supposed to be transparent - everywhere they occur. Assume it's an RGB image. - - 'color' has been gamma-corrected. ------------------------------------------------------------------------------*/ - bool retval; - - if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) { - const png_color_16 * const transColorP = transColor(pngxP); - - /* It seems odd that libpng lets you get gamma-corrected pixel - values, but not gamma-corrected transparency or background - values. But as that is the case, we have to gamma-correct - the transparency values. - - Note that because we compare the gamma-corrected values and - there may be many-to-one mapping of uncorrected to corrected - values, more pixels may be transparent than what the user - intended. - - We could fix this by not letting libpng gamma-correct the - pixels, and just do it ourselves. - */ - - switch (pngxP->info_ptr->color_type) { - case PNG_COLOR_TYPE_GRAY: - retval = color.r == gamma_correct(transColorP->gray, totalgamma); - break; - default: - retval = - color.r == gamma_correct(transColorP->red, totalgamma) && - color.g == gamma_correct(transColorP->green, totalgamma) && - color.b == gamma_correct(transColorP->blue, totalgamma); - } - } else - retval = FALSE; - - return retval; -} - - - -static void -setupGammaCorrection(struct pngx * const pngxP, - float const displaygamma, - float * const totalgammaP) { - - if (displaygamma == -1.0) - *totalgammaP = -1.0; - else { - float imageGamma; - if (pngxP->info_ptr->valid & PNG_INFO_gAMA) - imageGamma = pngxP->info_ptr->gamma; - else { - if (verbose) - pm_message("PNG doesn't specify image gamma. Assuming 1.0"); - imageGamma = 1.0; - } - - if (fabs(displaygamma * imageGamma - 1.0) < .01) { - *totalgammaP = -1.0; - if (verbose) - pm_message("image gamma %4.2f matches " - "display gamma %4.2f. No conversion.", - imageGamma, displaygamma); - } else { - png_set_gamma(pngxP->png_ptr, displaygamma, imageGamma); - *totalgammaP = imageGamma * displaygamma; - /* in case of gamma-corrections, sBIT's as in the - PNG-file are not valid anymore - */ - pngxP->info_ptr->valid &= ~PNG_INFO_sBIT; - if (verbose) - pm_message("image gamma is %4.2f, " - "converted for display gamma of %4.2f", - imageGamma, displaygamma); - } - } -} - - - -static bool -paletteHasPartialTransparency(png_info * const info_ptr) { - - bool retval; - - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - if (info_ptr->valid & PNG_INFO_tRNS) { - bool foundGray; - unsigned int i; - - for (i = 0, foundGray = FALSE; - i < info_ptr->num_trans && !foundGray; - ++i) { - if (info_ptr->trans[i] != 0 && - info_ptr->trans[i] != maxval) { - foundGray = TRUE; - } - } - retval = foundGray; - } else - retval = FALSE; - } else - retval = FALSE; - - return retval; -} - - - -static void -setupSignificantBits(struct pngx * const pngxP, - enum alpha_handling const alpha, - png_uint_16 * const maxvalP, - int * const errorLevelP) { -/*---------------------------------------------------------------------------- - Figure out what maxval would best express the information in the PNG - described by *pngxP, with 'alpha' telling which information in the PNG we - care about (image or alpha mask). - - Return the result as *maxvalP. ------------------------------------------------------------------------------*/ - png_info * const info_ptr = pngxP->info_ptr; - - /* Initial assumption of maxval */ - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - if (alpha == ALPHA_ONLY) { - if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY || - info_ptr->color_type == PNG_COLOR_TYPE_RGB) - /* The alpha mask will be all opaque, so maxval 1 is plenty */ - *maxvalP = 1; - else if (paletteHasPartialTransparency(info_ptr)) - /* Use same maxval as PNG transparency palette for simplicity*/ - *maxvalP = 255; - else - /* A common case, so we conserve bits */ - *maxvalP = 1; - } else - /* Use same maxval as PNG palette for simplicity */ - *maxvalP = 255; - } else { - *maxvalP = (1l << info_ptr->bit_depth) - 1; - } - - /* sBIT handling is very tricky. If we are extracting only the - image, we can use the sBIT info for grayscale and color images, - if the three values agree. If we extract the transparency/alpha - mask, sBIT is irrelevant for trans and valid for alpha. If we - mix both, the multiplication may result in values that require - the normal bit depth, so we will use the sBIT info only for - transparency, if we know that only solid and fully transparent - is used - */ - - if (info_ptr->valid & PNG_INFO_sBIT) { - switch (alpha) { - case ALPHA_MIX: - if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || - info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - break; - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - (info_ptr->valid & PNG_INFO_tRNS)) { - - bool trans_mix; - unsigned int i; - trans_mix = TRUE; - for (i = 0; i < info_ptr->num_trans; ++i) - if (info_ptr->trans[i] != 0 && info_ptr->trans[i] != 255) { - trans_mix = FALSE; - break; - } - if (!trans_mix) - break; - } - - /* else fall though to normal case */ - - case ALPHA_NONE: - if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE || - info_ptr->color_type == PNG_COLOR_TYPE_RGB || - info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) && - (info_ptr->sig_bit.red != info_ptr->sig_bit.green || - info_ptr->sig_bit.red != info_ptr->sig_bit.blue) && - alpha == ALPHA_NONE) { - pm_message("This program cannot handle " - "different bit depths for color channels"); - pm_message("writing file with %d bit resolution", - info_ptr->bit_depth); - *errorLevelP = PNMTOPNG_WARNING_LEVEL; - } else { - if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) && - (info_ptr->sig_bit.red < 255)) { - unsigned int i; - for (i = 0; i < info_ptr->num_palette; ++i) { - info_ptr->palette[i].red >>= - (8 - info_ptr->sig_bit.red); - info_ptr->palette[i].green >>= - (8 - info_ptr->sig_bit.green); - info_ptr->palette[i].blue >>= - (8 - info_ptr->sig_bit.blue); - } - *maxvalP = (1l << info_ptr->sig_bit.red) - 1; - if (verbose) - pm_message ("image has fewer significant bits, " - "writing file with %d bits per channel", - info_ptr->sig_bit.red); - } else - if ((info_ptr->color_type == PNG_COLOR_TYPE_RGB || - info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) && - (info_ptr->sig_bit.red < info_ptr->bit_depth)) { - png_set_shift(pngxP->png_ptr, &(info_ptr->sig_bit)); - *maxvalP = (1l << info_ptr->sig_bit.red) - 1; - if (verbose) - pm_message("image has fewer significant bits, " - "writing file with %d " - "bits per channel", - info_ptr->sig_bit.red); - } else - if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY || - info_ptr->color_type == - PNG_COLOR_TYPE_GRAY_ALPHA) && - (info_ptr->sig_bit.gray < info_ptr->bit_depth)) { - png_set_shift(pngxP->png_ptr, &info_ptr->sig_bit); - *maxvalP = (1l << info_ptr->sig_bit.gray) - 1; - if (verbose) - pm_message("image has fewer significant bits, " - "writing file with %d bits", - info_ptr->sig_bit.gray); - } - } - break; - - case ALPHA_ONLY: - if ((info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || - info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) && - (info_ptr->sig_bit.gray < info_ptr->bit_depth)) { - png_set_shift(pngxP->png_ptr, &info_ptr->sig_bit); - if (verbose) - pm_message ("image has fewer significant bits, " - "writing file with %d bits", - info_ptr->sig_bit.alpha); - *maxvalP = (1l << info_ptr->sig_bit.alpha) - 1; - } - break; - - } - } -} - - - -static bool -imageHasColor(struct pngx * const pngxP) { - - bool retval; - - if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY || - pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - - retval = FALSE; - else if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - bool foundColor; - unsigned int i; - - for (i = 0, foundColor = FALSE; - i < pngxP->info_ptr->num_palette && !foundColor; - ++i) { - if (iscolor(pngxP->info_ptr->palette[i])) - foundColor = TRUE; - } - retval = foundColor; - } else - retval = TRUE; - - return retval; -} - - - -static void -determineOutputType(struct pngx * const pngxP, - enum alpha_handling const alphaHandling, - pngcolor const bgColor, - xelval const maxval, - int * const pnmTypeP) { - - if (alphaHandling != ALPHA_ONLY && - (imageHasColor(pngxP) || !isGrayscale(bgColor))) - *pnmTypeP = PPM_TYPE; - else { - if (maxval > 1) - *pnmTypeP = PGM_TYPE; - else - *pnmTypeP = PBM_TYPE; - } -} - - - -static void -getBackgroundColor(struct pngx * const pngxP, - const char * const requestedColor, - float const totalgamma, - xelval const maxval, - pngcolor * const bgColorP) { -/*---------------------------------------------------------------------------- - Figure out what the background color should be. If the user requested - a particular color ('requestedColor' not null), that's the one. - Otherwise, if the PNG specifies a background color, that's the one. - And otherwise, it's white. ------------------------------------------------------------------------------*/ - if (requestedColor) { - /* Background was specified from the command-line; we always - use that. I chose to do no gamma-correction in this case; - which is a bit arbitrary. - */ - pixel const backcolor = ppm_parsecolor(requestedColor, maxval); - - bgColorP->r = PPM_GETR(backcolor); - bgColorP->g = PPM_GETG(backcolor); - bgColorP->b = PPM_GETB(backcolor); - - } else if (pngxP->info_ptr->valid & PNG_INFO_bKGD) { - /* didn't manage to get libpng to work (bugs?) concerning background - processing, therefore we do our own. - */ - switch (pngxP->info_ptr->color_type) { - case PNG_COLOR_TYPE_GRAY: - case PNG_COLOR_TYPE_GRAY_ALPHA: - bgColorP->r = bgColorP->g = bgColorP->b = - gamma_correct(pngxP->info_ptr->background.gray, totalgamma); - break; - case PNG_COLOR_TYPE_PALETTE: { - png_color const rawBgcolor = - pngxP->info_ptr->palette[pngxP->info_ptr->background.index]; - bgColorP->r = gamma_correct(rawBgcolor.red, totalgamma); - bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma); - bgColorP->b = gamma_correct(rawBgcolor.blue, totalgamma); - } - break; - case PNG_COLOR_TYPE_RGB: - case PNG_COLOR_TYPE_RGB_ALPHA: { - png_color_16 const rawBgcolor = pngxP->info_ptr->background; - - bgColorP->r = gamma_correct(rawBgcolor.red, totalgamma); - bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma); - bgColorP->b = gamma_correct(rawBgcolor.blue, totalgamma); - } - break; - } - } else - /* when no background given, we use white [from version 2.37] */ - bgColorP->r = bgColorP->g = bgColorP->b = maxval; -} - - - -static void -warnNonsquarePixels(struct pngx * const pngxP, - int * const errorLevelP) { - - if (pngxP->info_ptr->valid & PNG_INFO_pHYs) { - float const r = - (float)pngxP->info_ptr->x_pixels_per_unit / - pngxP->info_ptr->y_pixels_per_unit; - - if (r != 1.0) { - pm_message ("warning - non-square pixels; " - "to fix do a 'pamscale -%cscale %g'", - r < 1.0 ? 'x' : 'y', - r < 1.0 ? 1.0 / r : r ); - *errorLevelP = PNMTOPNG_WARNING_LEVEL; - } - } -} - - - -#define GET_PNG_VAL(p) get_png_val(&(p), pngxP->info_ptr->bit_depth) - - - -static void -makeXelRow(xel * const xelrow, - xelval const maxval, - int const pnmType, - struct pngx * const pngxP, - const png_byte * const pngRasterRow, - pngcolor const bgColor, - enum alpha_handling const alphaHandling, - double const totalgamma) { - - const png_byte * pngPixelP; - unsigned int col; - - pngPixelP = &pngRasterRow[0]; /* initial value */ - for (col = 0; col < pngxP->info_ptr->width; ++col) { - switch (pngxP->info_ptr->color_type) { - case PNG_COLOR_TYPE_GRAY: { - pngcolor fgColor; - fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP); - setXel(&xelrow[col], fgColor, bgColor, alphaHandling, - isTransparentColor(fgColor, pngxP, totalgamma) ? - 0 : maxval); - } - break; - - case PNG_COLOR_TYPE_GRAY_ALPHA: { - pngcolor fgColor; - png_uint_16 alpha; - - fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP); - alpha = GET_PNG_VAL(pngPixelP); - setXel(&xelrow[col], fgColor, bgColor, alphaHandling, alpha); - } - break; - - case PNG_COLOR_TYPE_PALETTE: { - png_uint_16 const index = GET_PNG_VAL(pngPixelP); - png_color const paletteColor = pngxP->info_ptr->palette[index]; - - pngcolor fgColor; - - fgColor.r = paletteColor.red; - fgColor.g = paletteColor.green; - fgColor.b = paletteColor.blue; - - setXel(&xelrow[col], fgColor, bgColor, alphaHandling, - (pngxP->info_ptr->valid & PNG_INFO_tRNS) && - index < pngxP->info_ptr->num_trans ? - pngxP->info_ptr->trans[index] : maxval); - } - break; - - case PNG_COLOR_TYPE_RGB: { - pngcolor fgColor; - - fgColor.r = GET_PNG_VAL(pngPixelP); - fgColor.g = GET_PNG_VAL(pngPixelP); - fgColor.b = GET_PNG_VAL(pngPixelP); - setXel(&xelrow[col], fgColor, bgColor, alphaHandling, - isTransparentColor(fgColor, pngxP, totalgamma) ? - 0 : maxval); - } - break; - - case PNG_COLOR_TYPE_RGB_ALPHA: { - pngcolor fgColor; - png_uint_16 alpha; - - fgColor.r = GET_PNG_VAL(pngPixelP); - fgColor.g = GET_PNG_VAL(pngPixelP); - fgColor.b = GET_PNG_VAL(pngPixelP); - alpha = GET_PNG_VAL(pngPixelP); - setXel(&xelrow[col], fgColor, bgColor, alphaHandling, alpha); - } - break; - - default: - pm_error("unknown PNG color type: %d", - pngxP->info_ptr->color_type); - } - } -} - - - -static void -writePnm(FILE * const ofP, - xelval const maxval, - int const pnmType, - struct pngx * const pngxP, - png_byte ** const pngRaster, - pngcolor const bgColor, - enum alpha_handling const alphaHandling, - double const totalgamma) { -/*---------------------------------------------------------------------------- - Write a PNM of either the image or the alpha mask, according to - 'alphaHandling' that is in the PNG image described by *pngxP and - pngRaster[][]. - - 'pnmType' and 'maxval' are of the output image. - - Use background color 'bgColor' in the output if the PNG is such that a - background color is needed. ------------------------------------------------------------------------------*/ - int const plainFalse = 0; - - xel * xelrow; - unsigned int row; - - if (verbose) - pm_message("writing a %s file (maxval=%u)", - pnmType == PBM_TYPE ? "PBM" : - pnmType == PGM_TYPE ? "PGM" : - pnmType == PPM_TYPE ? "PPM" : - "UNKNOWN!", - maxval); - - xelrow = pnm_allocrow(pngxP->info_ptr->width); - - pnm_writepnminit(stdout, - pngxP->info_ptr->width, pngxP->info_ptr->height, maxval, - pnmType, plainFalse); - - for (row = 0; row < pngxP->info_ptr->height; ++row) { - makeXelRow(xelrow, maxval, pnmType, pngxP, pngRaster[row], bgColor, - alphaHandling, totalgamma); - - pnm_writepnmrow(ofP, xelrow, pngxP->info_ptr->width, maxval, - pnmType, plainFalse); - } - pnm_freerow (xelrow); -} - - - -static void -convertpng(FILE * const ifP, - FILE * const tfP, - struct cmdlineInfo const cmdline, - int * const errorLevelP) { - - png_byte ** pngRaster; - int pnmType; - pngcolor bgColor; - float totalgamma; - jmp_buf jmpbuf; - struct pngx * pngxP; - - *errorLevelP = 0; - - if (setjmp(jmpbuf)) - pm_error ("setjmp returns error condition"); - - pngx_createRead(&pngxP, &jmpbuf); - - readPng(pngxP, ifP, &pngRaster); - - if (verbose) - dumpPngInfo(pngxP); - - if (cmdline.time) - showTime(pngxP); - if (tfP) - saveText(pngxP, tfP); - - warnNonsquarePixels(pngxP, errorLevelP); - - setupGammaCorrection(pngxP, cmdline.gamma, &totalgamma); - - setupSignificantBits(pngxP, cmdline.alpha, &maxval, errorLevelP); - - getBackgroundColor(pngxP, cmdline.background, totalgamma, maxval, - &bgColor); - - determineOutputType(pngxP, cmdline.alpha, bgColor, maxval, &pnmType); - - writePnm(stdout, maxval, pnmType, pngxP, pngRaster, bgColor, - cmdline.alpha, totalgamma); - - fflush(stdout); - - freePngRaster(pngRaster, pngxP); - - pngx_destroy(pngxP); -} - - - -int -main(int argc, const char *argv[]) { - - struct cmdlineInfo cmdline; - FILE * ifP; - FILE * tfP; - int errorLevel; - - pm_proginit(&argc, argv); - - parseCommandLine(argc, argv, &cmdline); - - verbose = cmdline.verbose; - - ifP = pm_openr(cmdline.inputFilespec); - - if (cmdline.text) - tfP = pm_openw(cmdline.text); - else - tfP = NULL; - - convertpng(ifP, tfP, cmdline, &errorLevel); - - if (tfP) - pm_close(tfP); - - pm_close(ifP); - pm_close(stdout); - - return errorLevel; -} diff --git a/converter/other/pngx.c b/converter/other/pngx.c new file mode 100644 index 00000000..6d79bcfe --- /dev/null +++ b/converter/other/pngx.c @@ -0,0 +1,95 @@ +#include <png.h> +#include "pm_c_util.h" +#include "mallocvar.h" +#include "pm.h" +#include "pngx.h" + + +static void +errorHandler(png_structp const png_ptr, + png_const_charp const msg) { + + jmp_buf * jmpbufP; + + /* this function, aside from the extra step of retrieving the "error + pointer" (below) and the fact that it exists within the application + rather than within libpng, is essentially identical to libpng's + default error handler. The second point is critical: since both + setjmp() and longjmp() are called from the same code, they are + guaranteed to have compatible notions of how big a jmp_buf is, + regardless of whether _BSD_SOURCE or anything else has (or has not) + been defined. + */ + + pm_message("fatal libpng error: %s", msg); + + jmpbufP = png_get_error_ptr(png_ptr); + + if (!jmpbufP) { + /* we are completely hosed now */ + pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating."); + } + + longjmp(*jmpbufP, 1); +} + + + +void +pngx_create(struct pngx ** const pngxPP, + pngx_rw const rw, + jmp_buf * const jmpbufP) { + + struct pngx * pngxP; + + MALLOCVAR(pngxP); + + if (!pngxP) + pm_error("Failed to allocate memory for PNG object"); + else { + switch(rw) { + case PNGX_READ: + pngxP->png_ptr = png_create_write_struct( + PNG_LIBPNG_VER_STRING, + jmpbufP, errorHandler, NULL); + break; + case PNGX_WRITE: + pngxP->png_ptr = png_create_write_struct( + PNG_LIBPNG_VER_STRING, + jmpbufP, errorHandler, NULL); + break; + } + if (!pngxP->png_ptr) + pm_error("cannot allocate main libpng structure (png_ptr)"); + else { + pngxP->info_ptr = png_create_info_struct(pngxP->png_ptr); + + if (!pngxP->info_ptr) + pm_error("cannot allocate libpng info structure (info_ptr)"); + else + *pngxPP = pngxP; + } + } +} + + + +void +pngx_destroy(struct pngx * const pngxP) { + + png_destroy_write_struct(&pngxP->png_ptr, &pngxP->info_ptr); + + free(pngxP); +} + + + +bool +pngx_chunkIsPresent(struct pngx * const pngxP, + uint32_t const chunkType) { + + return png_get_valid(pngxP->png_ptr, pngxP->info_ptr, chunkType); +} + + + diff --git a/converter/other/pngx.h b/converter/other/pngx.h new file mode 100644 index 00000000..81487ca1 --- /dev/null +++ b/converter/other/pngx.h @@ -0,0 +1,29 @@ +#ifndef PNGX_H_INCLUDED +#define PNGX_H_INCLUDED + +/* pngx is designed to be an extension of the PNG library to make using + the PNG library easier and cleaner. +*/ + +struct pngx { + png_structp png_ptr; + png_infop info_ptr; +}; + +typedef enum {PNGX_READ, PNGX_WRITE} pngx_rw; + + +void +pngx_create(struct pngx ** const pngxPP, + pngx_rw const rw, + jmp_buf * const jmpbufP); + +void +pngx_destroy(struct pngx * const pngxP); + + +bool +pngx_chunkIsPresent(struct pngx * const pngxP, + uint32_t const chunkType); + +#endif diff --git a/converter/other/pnmtopng.c b/converter/other/pnmtopng.c index 5107c33a..6cef96ad 100644 --- a/converter/other/pnmtopng.c +++ b/converter/other/pnmtopng.c @@ -1,6 +1,5 @@ /* -** pnmtopng.c - -** read a portable anymap and produce a Portable Network Graphics file +** read a PNM image and produce a Portable Network Graphics file ** ** derived from pnmtorast.c (c) 1990,1991 by Jef Poskanzer and some ** parts derived from ppmtogif.c by Marcel Wijkstra <wijkstra@fwi.uva.nl> @@ -63,21 +62,13 @@ #include "pm_c_util.h" #include "pnm.h" +#include "pngx.h" #include "pngtxt.h" #include "shhopt.h" #include "mallocvar.h" #include "nstring.h" #include "version.h" -/* A hack until we can remove direct access to png_info from the program */ -#if PNG_LIBPNG_VER >= 10400 -#define trans_values trans_color -#define TRANS_ALPHA trans_alpha -#else -#define TRANS_ALPHA trans -#endif - - struct zlibCompression { /* These are parameters that describe a form of zlib compression. Values have the same meaning as the similarly named arguments to @@ -634,36 +625,6 @@ lookupColorAlpha(coloralphahash_table const caht, -static void -pnmtopng_error_handler(png_structp const png_ptr, - png_const_charp const msg) { - - jmpbuf_wrapper *jmpbuf_ptr; - - /* this function, aside from the extra step of retrieving the "error - * pointer" (below) and the fact that it exists within the application - * rather than within libpng, is essentially identical to libpng's - * default error handler. The second point is critical: since both - * setjmp() and longjmp() are called from the same code, they are - * guaranteed to have compatible notions of how big a jmp_buf is, - * regardless of whether _BSD_SOURCE or anything else has (or has not) - * been defined. */ - - fprintf(stderr, "pnmtopng: fatal libpng error: %s\n", msg); - fflush(stderr); - - jmpbuf_ptr = png_get_error_ptr(png_ptr); - if (jmpbuf_ptr == NULL) { /* we are completely hosed now */ - fprintf(stderr, - "pnmtopng: EXTREMELY fatal error: jmpbuf unrecoverable; terminating.\n"); - fflush(stderr); - exit(99); - } - - longjmp(jmpbuf_ptr->jmpbuf, 1); -} - - /* The following variables belong to getChv() and freeChv() */ static bool getChv_computed = FALSE; static colorhist_vector getChv_chv; @@ -1031,6 +992,123 @@ analyzeAlpha(FILE * const ifp, static void +determineTransparency(struct cmdlineInfo const cmdline, + FILE * const ifP, + pm_filepos const rasterPos, + unsigned int const cols, + unsigned int const rows, + xelval const maxval, + int const format, + FILE * const afP, + bool * const alphaP, + int * const transparentP, + pixel * const transColorP, + bool * const transExactP, + gray *** const alphaMaskP, + gray * const alphaMaxvalP) { +/*---------------------------------------------------------------------------- + Determine the various aspects of transparency we need to generate the + PNG. + + Note that there are two kinds of transparency: pixel-by-pixel + transparency/translucency with an alpha mask and all pixels of a certain + color being transparent. Both these exist both in input from the user and + as representations in the PNG -- i.e. user may supply an alpha mask, + or identify a transparent color and the PNG may contain an alpha mask + or identify a transparent color. + + We return as *transparentP: + + -1 PNG is not to have single-color transparency + 1 PNG is to have single-color transparency as directed by user + 2 PNG is to have single-color transparency that effects an alpha + mask that the user supplied. + + In the cases where there is to be single-color transparency, *transColorP + is that color. +-----------------------------------------------------------------------------*/ + if (cmdline.alpha) { + pixel alphaTranscolor; + bool alphaCanBeTransparencyIndex; + bool allOpaque; + int alphaCols, alphaRows; + gray alphaMaxval; + gray ** alphaMask; + + if (verbose) + pm_message("reading alpha-channel image..."); + alphaMask = pgm_readpgm(afP, &alphaCols, &alphaRows, &alphaMaxval); + + if (alphaCols != cols || alphaRows != rows) { + pm_error("dimensions for image and alpha mask do not agree"); + } + analyzeAlpha(ifP, rasterPos, cols, rows, maxval, format, + alphaMask, alphaMaxval, &allOpaque, + &alphaCanBeTransparencyIndex, &alphaTranscolor); + + if (alphaCanBeTransparencyIndex && !cmdline.force) { + if (verbose) + pm_message("converting alpha mask to transparency index"); + *alphaP = FALSE; + *transparentP = 2; + *transColorP = alphaTranscolor; + } else if (allOpaque) { + *alphaP = FALSE; + *transparentP = -1; + } else { + *alphaP = TRUE; + *transparentP = -1; + } + *alphaMaxvalP = alphaMaxval; + *alphaMaskP = alphaMask; + } else { + /* Though there's no alpha_mask, we still need an alpha_maxval for + use with trans[], which can have stuff in it if the user specified + a transparent color. + */ + *alphaP = FALSE; + *alphaMaxvalP = 255; + + if (cmdline.transparent) { + const char * transstring2; + /* The -transparent value, but with possible leading '=' removed */ + if (cmdline.transparent[0] == '=') { + *transExactP = TRUE; + transstring2 = &cmdline.transparent[1]; + } else { + *transExactP = FALSE; + transstring2 = cmdline.transparent; + } + /* We do this funny PPM_DEPTH thing instead of just passing 'maxval' + to ppm_parsecolor() because ppm_parsecolor() does a cheap maxval + scaling, and this is more precise. + */ + PPM_DEPTH(*transColorP, + ppm_parsecolor(transstring2, PNM_OVERALLMAXVAL), + PNM_OVERALLMAXVAL, maxval); + + *transparentP = 1; + } else + *transparentP = -1; + } +} + + + +static void +determineBackground(struct cmdlineInfo const cmdline, + xelval const maxval, + xel * const backColorP) { + + if (cmdline.background) + PPM_DEPTH(*backColorP, + ppm_parsecolor(cmdline.background, PNM_OVERALLMAXVAL), + PNM_OVERALLMAXVAL, maxval);; +} + + + +static void findRedundantBits(FILE * const ifp, int const rasterPos, int const cols, @@ -2148,8 +2226,7 @@ makePngLine(png_byte * const line, static void -writeRaster(png_struct * const png_ptr, - png_info * const info_ptr, +writeRaster(struct pngx * const pngxP, FILE * const ifP, pm_filepos const rasterPos, unsigned int const cols, @@ -2182,7 +2259,7 @@ writeRaster(png_struct * const png_ptr, if (line == NULL) pm_error("out of memory allocating PNG row buffer"); - for (pass = 0; pass < png_set_interlace_handling(png_ptr); ++pass) { + for (pass = 0; pass < png_set_interlace_handling(pngxP->png_ptr); ++pass) { unsigned int row; pm_seek2(ifP, &rasterPos, sizeof(rasterPos)); for (row = 0; row < rows; ++row) { @@ -2192,9 +2269,9 @@ writeRaster(png_struct * const png_ptr, makePngLine(line, xelrow, cols, maxval, alpha, alpha ? alpha_mask[row] : NULL, - cht, caht, info_ptr, png_maxval, depth); + cht, caht, pngxP->info_ptr, png_maxval, depth); - png_write_row(png_ptr, line); + png_write_row(pngxP->png_ptr, line); } } pnm_freerow(xelrow); @@ -2203,6 +2280,77 @@ writeRaster(png_struct * const png_ptr, static void +doHistChunk(bool const histRequested, + pixel const palettePnm[], + FILE * const ifP, + pm_filepos const rasterPos, + unsigned int const cols, + unsigned int const rows, + xelval const maxval, + int const format, + png_info * const info_ptr, + bool const verbose) { + + if (histRequested) { + colorhist_vector chv; + unsigned int colorCt; + colorhash_table cht; + + getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, + &chv, &colorCt); + + cht = ppm_colorhisttocolorhash(chv, colorCt); + + { + png_uint_16 * histogram; /* malloc'ed */ + + MALLOCARRAY(histogram, MAXCOLORS); + + if (!histogram) + pm_error("Failed to allocate memory for %u-color histogram", + MAXCOLORS); + else { + unsigned int i; + for (i = 0 ; i < MAXCOLORS; ++i) { + int const chvIndex = ppm_lookupcolor(cht, &palettePnm[i]); + if (chvIndex == -1) + histogram[i] = 0; + else + histogram[i] = chv[chvIndex].value; + } + + info_ptr->valid |= PNG_INFO_hIST; + info_ptr->hist = histogram; + if (verbose) + pm_message("histogram created in PNG stream"); + } + } + ppm_freecolorhash(cht); + } +} + + + +static void +setColorType(struct pngx * const pngxP, + bool const colorMapped, + int const pnmType, + bool const alpha) { + + if (colorMapped) + pngxP->info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; + else if (pnmType == PPM_TYPE) + pngxP->info_ptr->color_type = PNG_COLOR_TYPE_RGB; + else + pngxP->info_ptr->color_type = PNG_COLOR_TYPE_GRAY; + + if (alpha && pngxP->info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + pngxP->info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; +} + + + +static void doGamaChunk(struct cmdlineInfo const cmdline, png_info * const info_ptr) { @@ -2268,6 +2416,96 @@ doTimeChunk(struct cmdlineInfo const cmdline, static void +reportTrans(struct pngx * const pngxP) { + + if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) { + png_bytep trans; + int numTrans; + png_color_16 * transColorP; + + png_get_tRNS(pngxP->png_ptr, pngxP->info_ptr, + &trans, &numTrans, &transColorP); + + pm_message("Transparent color {gray, red, green, blue} = " + "{%d, %d, %d, %d}", + transColorP->gray, + transColorP->red, + transColorP->green, + transColorP->blue); + } else + pm_message("No transparent color"); +} + + +static void +doTrnsChunk(struct pngx * const pngxP, + png_byte const transPalette[], + unsigned int const transPaletteSize, + int const transparent, + pixel const transColor, + xelval const maxval, + xelval const pngMaxval) { + + switch (pngxP->info_ptr->color_type) { + case PNG_COLOR_TYPE_PALETTE: + if (transPaletteSize > 0) { + png_set_tRNS(pngxP->png_ptr, pngxP->info_ptr, + (png_byte *)transPalette, + transPaletteSize /* omit opaque values */, + 0); + } + break; + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_RGB: + if (transparent > 0) { + png_color_16 pngTransColor = + xelToPngColor_16(transColor, maxval, pngMaxval); + png_set_tRNS(pngxP->png_ptr, pngxP->info_ptr, + NULL, 0, &pngTransColor); + } + break; + default: + /* This is PNG_COLOR_MASK_ALPHA. Transparency will be handled + by the alpha channel, not a transparency color. + */ + {} + } + if (verbose) + reportTrans(pngxP); +} + + + +static void +doBkgdChunk(bool const bkgdRequested, + png_info * const info_ptr, + unsigned int const backgroundIndex, + pixel const backColor, + xelval const maxval, + xelval const pngMaxval, + bool const verbose) { + + if (bkgdRequested) { + info_ptr->valid |= PNG_INFO_bKGD; + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { + info_ptr->background.index = backgroundIndex; + } else { + info_ptr->background = + xelToPngColor_16(backColor, maxval, pngMaxval); + if (verbose) + pm_message("Writing bKGD chunk with background color " + " {gray, red, green, blue} = {%d, %d, %d, %d}", + info_ptr->background.gray, + info_ptr->background.red, + info_ptr->background.green, + info_ptr->background.blue ); + } + } +} + + + +static void doSbitChunk(png_info * const pngInfoP, xelval const pngMaxval, xelval const maxval, @@ -2322,6 +2560,7 @@ doSbitChunk(png_info * const pngInfoP, static void convertpnm(struct cmdlineInfo const cmdline, FILE * const ifp, + FILE * const ofp, FILE * const afp, FILE * const pfp, FILE * const tfp, @@ -2357,8 +2596,8 @@ convertpnm(struct cmdlineInfo const cmdline, /* The background color, with maxval equal to that of the input image. */ - png_struct *png_ptr; - png_info *info_ptr; + jmp_buf jmpbuf; + struct pngx * pngxP; bool colorMapped; pixel palette_pnm[MAXCOLORS]; @@ -2381,10 +2620,7 @@ convertpnm(struct cmdlineInfo const cmdline, unsigned int background_index; /* Index into palette[] of the background color. */ - png_uint_16 histogram[MAXCOLORS]; gray alpha_maxval; - int alpha_rows; - int alpha_cols; const char * noColormapReason; /* The reason that we shouldn't make a colormapped PNG, or NULL if we should. malloc'ed null-terminated string. @@ -2406,38 +2642,21 @@ convertpnm(struct cmdlineInfo const cmdline, /* The row of the input image currently being processed */ int pnm_type; - xelval maxmaxval; gray ** alpha_mask; - /* these guys are initialized to quiet compiler warnings: */ - maxmaxval = 255; - alpha_mask = NULL; + /* We initialize these guys to quiet compiler warnings: */ depth = 0; - errorlevel = 0; - png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, - &pnmtopng_jmpbuf_struct, pnmtopng_error_handler, NULL); - if (png_ptr == NULL) { - pm_closer (ifp); - pm_error ("cannot allocate main libpng structure (png_ptr)"); - } + errorlevel = 0; - info_ptr = png_create_info_struct (png_ptr); - if (info_ptr == NULL) { - png_destroy_write_struct (&png_ptr, (png_infopp)NULL); - pm_closer (ifp); - pm_error ("cannot allocate libpng info structure (info_ptr)"); - } + if (setjmp(jmpbuf)) + pm_error ("setjmp returns error condition"); - if (setjmp (pnmtopng_jmpbuf_struct.jmpbuf)) { - png_destroy_write_struct (&png_ptr, &info_ptr); - pm_closer (ifp); - pm_error ("setjmp returns error condition (1)"); - } + pngx_create(&pngxP, PNGX_WRITE, &jmpbuf); - pnm_readpnminit (ifp, &cols, &rows, &maxval, &format); + pnm_readpnminit(ifp, &cols, &rows, &maxval, &format); pm_tell2(ifp, &rasterPos, sizeof(rasterPos)); - pnm_type = PNM_FORMAT_TYPE (format); + pnm_type = PNM_FORMAT_TYPE(format); xelrow = pnm_allocrow(cols); @@ -2450,71 +2669,12 @@ convertpnm(struct cmdlineInfo const cmdline, pm_message ("reading a PPM file (maxval=%d)", maxval); } - if (pnm_type == PGM_TYPE) - maxmaxval = PGM_OVERALLMAXVAL; - else if (pnm_type == PPM_TYPE) - maxmaxval = PPM_OVERALLMAXVAL; - - if (cmdline.transparent) { - const char * transstring2; - /* The -transparent value, but with possible leading '=' removed */ - if (cmdline.transparent[0] == '=') { - transexact = 1; - transstring2 = &cmdline.transparent[1]; - } else { - transexact = 0; - transstring2 = cmdline.transparent; - } - /* We do this funny PPM_DEPTH thing instead of just passing 'maxval' - to ppm_parsecolor() because ppm_parsecolor() does a cheap maxval - scaling, and this is more precise. - */ - PPM_DEPTH(transcolor, ppm_parsecolor(transstring2, maxmaxval), - maxmaxval, maxval); - } - if (cmdline.alpha) { - pixel alpha_transcolor; - bool alpha_can_be_transparency_index; - bool all_opaque; + determineTransparency(cmdline, ifp, rasterPos, cols, rows, maxval, format, + afp, + &alpha, &transparent, &transcolor, &transexact, + &alpha_mask, &alpha_maxval); - if (verbose) - pm_message ("reading alpha-channel image..."); - alpha_mask = pgm_readpgm (afp, &alpha_cols, &alpha_rows, &alpha_maxval); - - if (alpha_cols != cols || alpha_rows != rows) { - png_destroy_write_struct (&png_ptr, &info_ptr); - pm_closer (ifp); - pm_error ("dimensions for image and alpha mask do not agree"); - } - analyzeAlpha(ifp, rasterPos, cols, rows, maxval, format, - alpha_mask, alpha_maxval, &all_opaque, - &alpha_can_be_transparency_index, &alpha_transcolor); - - if (alpha_can_be_transparency_index && !cmdline.force) { - if (verbose) - pm_message ("converting alpha mask to transparency index"); - alpha = FALSE; - transparent = 2; - transcolor = alpha_transcolor; - } else if (all_opaque) { - alpha = FALSE; - transparent = -1; - } else { - alpha = TRUE; - transparent = -1; - } - } else { - /* Though there's no alpha_mask, we still need an alpha_maxval for - use with trans[], which can have stuff in it if the user specified - a transparent color. - */ - alpha = FALSE; - alpha_maxval = 255; - transparent = cmdline.transparent ? 1 : -1; - } - if (cmdline.background) - PPM_DEPTH(backcolor, ppm_parsecolor(cmdline.background, maxmaxval), - maxmaxval, maxval);; + determineBackground(cmdline, maxval, &backcolor); /* first of all, check if we have a grayscale image written as PPM */ @@ -2540,8 +2700,6 @@ convertpnm(struct cmdlineInfo const cmdline, /* handle `odd' maxvalues */ if (maxval > 65535 && !cmdline.downscale) { - png_destroy_write_struct(&png_ptr, &info_ptr); - pm_closer(ifp); pm_error("can only handle files up to 16-bit " "(use -downscale to override"); } @@ -2589,174 +2747,82 @@ convertpnm(struct cmdlineInfo const cmdline, png_maxval = pm_bitstomaxval(depth); if (setjmp (pnmtopng_jmpbuf_struct.jmpbuf)) { - png_destroy_write_struct (&png_ptr, &info_ptr); - pm_closer (ifp); pm_error ("setjmp returns error condition (2)"); } - png_init_io (png_ptr, stdout); - info_ptr->width = cols; - info_ptr->height = rows; - info_ptr->bit_depth = depth; + pngxP->info_ptr->width = cols; + pngxP->info_ptr->height = rows; + pngxP->info_ptr->bit_depth = depth; - if (colorMapped) - info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; - else if (pnm_type == PPM_TYPE) - info_ptr->color_type = PNG_COLOR_TYPE_RGB; - else - info_ptr->color_type = PNG_COLOR_TYPE_GRAY; + setColorType(pngxP, colorMapped, pnm_type, alpha); - if (alpha && info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) - info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; + pngxP->info_ptr->interlace_type = cmdline.interlace; - info_ptr->interlace_type = cmdline.interlace; + doGamaChunk(cmdline, pngxP->info_ptr); - doGamaChunk(cmdline, info_ptr); + doChrmChunk(cmdline, pngxP->info_ptr); - doChrmChunk(cmdline, info_ptr); + doPhysChunk(cmdline, pngxP->info_ptr); - doPhysChunk(cmdline, info_ptr); + if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - - /* creating PNG palette (PLTE and tRNS chunks) */ + /* creating PNG palette (Not counting the transparency palette) */ createPngPalette(palette_pnm, palette_size, maxval, trans_pnm, trans_size, alpha_maxval, palette, trans); - info_ptr->valid |= PNG_INFO_PLTE; - info_ptr->palette = palette; - info_ptr->num_palette = palette_size; - if (trans_size > 0) { - info_ptr->valid |= PNG_INFO_tRNS; - info_ptr->TRANS_ALPHA = trans; - info_ptr->num_trans = trans_size; /* omit opaque values */ - } - /* creating hIST chunk */ - if (cmdline.hist) { - colorhist_vector chv; - unsigned int colors; - colorhash_table cht; - - getChv(ifp, rasterPos, cols, rows, maxval, format, MAXCOLORS, - &chv, &colors); + pngxP->info_ptr->valid |= PNG_INFO_PLTE; + pngxP->info_ptr->palette = palette; + pngxP->info_ptr->num_palette = palette_size; - cht = ppm_colorhisttocolorhash (chv, colors); - - { - unsigned int i; - for (i = 0 ; i < MAXCOLORS; ++i) { - int const chvIndex = ppm_lookupcolor(cht, &palette_pnm[i]); - if (chvIndex == -1) - histogram[i] = 0; - else - histogram[i] = chv[chvIndex].value; - } - } - - ppm_freecolorhash(cht); - - info_ptr->valid |= PNG_INFO_hIST; - info_ptr->hist = histogram; - if (verbose) - pm_message("histogram created"); - } - } else { /* color_type != PNG_COLOR_TYPE_PALETTE */ - if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY || - info_ptr->color_type == PNG_COLOR_TYPE_RGB) { - if (transparent > 0) { - info_ptr->valid |= PNG_INFO_tRNS; - info_ptr->trans_values = - xelToPngColor_16(transcolor, maxval, png_maxval); - } - } else { - /* This is PNG_COLOR_MASK_ALPHA. Transparency will be handled - by the alpha channel, not a transparency color. - */ - } - if (verbose) { - if (info_ptr->valid && PNG_INFO_tRNS) - pm_message("Transparent color {gray, red, green, blue} = " - "{%d, %d, %d, %d}", - info_ptr->trans_values.gray, - info_ptr->trans_values.red, - info_ptr->trans_values.green, - info_ptr->trans_values.blue); - else - pm_message("No transparent color"); - } + doHistChunk(cmdline.hist, palette_pnm, ifp, rasterPos, + cols, rows, maxval, format, + pngxP->info_ptr, cmdline.verbose); } - /* bKGD chunk */ - if (cmdline.background) { - info_ptr->valid |= PNG_INFO_bKGD; - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - info_ptr->background.index = background_index; - } else { - info_ptr->background = - xelToPngColor_16(backcolor, maxval, png_maxval); - if (verbose) - pm_message("Writing bKGD chunk with background color " - " {gray, red, green, blue} = {%d, %d, %d, %d}", - info_ptr->background.gray, - info_ptr->background.red, - info_ptr->background.green, - info_ptr->background.blue ); - } - } + doTrnsChunk(pngxP, trans, trans_size, + transparent, transcolor, maxval, png_maxval); + + doBkgdChunk(!!cmdline.background, pngxP->info_ptr, + background_index, backcolor, + maxval, png_maxval, cmdline.verbose); - doSbitChunk(info_ptr, png_maxval, maxval, alpha, alpha_maxval); + doSbitChunk(pngxP->info_ptr, png_maxval, maxval, alpha, alpha_maxval); /* tEXT and zTXT chunks */ if (cmdline.text || cmdline.ztxt) - pnmpng_read_text(info_ptr, tfp, !!cmdline.ztxt, cmdline.verbose); + pnmpng_read_text(pngxP->info_ptr, tfp, !!cmdline.ztxt, cmdline.verbose); - doTimeChunk(cmdline, info_ptr); + doTimeChunk(cmdline, pngxP->info_ptr); if (cmdline.filterSet != 0) - png_set_filter(png_ptr, 0, cmdline.filterSet); + png_set_filter(pngxP->png_ptr, 0, cmdline.filterSet); - setZlibCompression(png_ptr, cmdline.zlibCompression); + setZlibCompression(pngxP->png_ptr, cmdline.zlibCompression); + + png_init_io(pngxP->png_ptr, ofp); /* write the png-info struct */ - png_write_info(png_ptr, info_ptr); + png_write_info(pngxP->png_ptr, pngxP->info_ptr); if (cmdline.text || cmdline.ztxt) /* prevent from being written twice with png_write_end */ - info_ptr->num_text = 0; + pngxP->info_ptr->num_text = 0; if (cmdline.modtime) /* prevent from being written twice with png_write_end */ - info_ptr->valid &= ~PNG_INFO_tIME; + pngxP->info_ptr->valid &= ~PNG_INFO_tIME; /* let libpng take care of, e.g., bit-depth conversions */ - png_set_packing (png_ptr); + png_set_packing(pngxP->png_ptr); - writeRaster(png_ptr, info_ptr, ifp, rasterPos, cols, rows, maxval, format, + writeRaster(pngxP, ifp, rasterPos, + cols, rows, maxval, format, png_maxval, depth, alpha, alpha_mask, cht, caht); - png_write_end (png_ptr, info_ptr); - + png_write_end(pngxP->png_ptr, pngxP->info_ptr); -#if 0 - /* The following code may be intended to solve some segfault problem - that arises with png_destroy_write_struct(). The latter is the - method recommended in the libpng documentation and this program - will not compile under Cygwin because the Windows DLL for libpng - does not contain png_write_destroy() at all. Since the author's - comment below does not make it clear what the segfault issue is, - we cannot consider it. -Bryan 00.09.15 -*/ - - png_write_destroy (png_ptr); - /* flush first because free(png_ptr) can segfault due to jmpbuf problems - in png_write_destroy */ - fflush (stdout); - free (png_ptr); - free (info_ptr); -#else - png_destroy_write_struct(&png_ptr, &info_ptr); -#endif + pngx_destroy(pngxP); pnm_freerow(xelrow); @@ -2837,7 +2903,7 @@ main(int argc, char *argv[]) { else tfP = NULL; - convertpnm(cmdline, ifP, afP, pfP, tfP, &errorlevel); + convertpnm(cmdline, ifP, stdout, afP, pfP, tfP, &errorlevel); if (afP) pm_close(afP); diff --git a/converter/other/tifftopnm.c b/converter/other/tifftopnm.c index 6665c7fd..1cf869fb 100644 --- a/converter/other/tifftopnm.c +++ b/converter/other/tifftopnm.c @@ -52,7 +52,6 @@ #include <assert.h> #include <string.h> #include <stdio.h> -#include <sys/wait.h> #include "pm_c_util.h" #include "shhopt.h" @@ -381,8 +380,8 @@ readscanline(TIFF * const tif, "TIFFReadScanline() failed.", row, plane); else if (bps == 8) { - int sample; - for (sample = 0; sample < cols*spp; sample++) + unsigned int sample; + for (sample = 0; sample < cols * spp; ++sample) samplebuf[sample] = scanbuf[sample]; } else if (bps < 8) { /* Note that in this format, samples do not span bytes. Rather, @@ -390,12 +389,12 @@ readscanline(TIFF * const tif, At least that's how I infer the format from reading pnmtotiff.c -Bryan 00.11.18 */ - int sample; - int bitsleft; + unsigned int sample; + unsigned int bitsleft; unsigned char * inP; - for (sample = 0, bitsleft=8, inP=scanbuf; - sample < cols*spp; + for (sample = 0, bitsleft = 8, inP = scanbuf; + sample < cols * spp; ++sample) { if (bitsleft == 0) { ++inP; @@ -412,6 +411,7 @@ readscanline(TIFF * const tif, pm_error("Internal error: invalid value for fillorder: %u", fillorder); } + assert(bitsleft >= bps); bitsleft -= bps; if (bitsleft < bps) /* Don't count dregs at end of byte */ @@ -433,10 +433,10 @@ readscanline(TIFF * const tif, for (sample = 0; sample < cols*spp; ++sample) samplebuf[sample] = scanbuf16[sample]; } else if (bps == 32) { - uint32 * const scanbuf32 = (uint32 *) scanbuf; + const uint32 * const scanbuf32 = (const uint32 *) scanbuf; unsigned int sample; - for (sample = 0; sample < cols*spp; ++sample) + for (sample = 0; sample < cols * spp; ++sample) samplebuf[sample] = scanbuf32[sample]; } else pm_error("Internal error: invalid bits per sample passed to " @@ -739,38 +739,34 @@ spawnWithInputPipe(const char * const shellCmd, asprintfN(errorP, "Failed to create pipe for process input. " "Errno=%d (%s)", errno, strerror(errno)); else { - int rc; - - rc = fork(); - - if (rc < 0) { - asprintfN(errorP, "Failed to fork a process. errno=%d (%s)", - errno, strerror(errno)); - } else if (rc == 0) { - /* This is the child */ - int rc; - close(fd[PIPE_WRITE]); - close(STDIN_FILENO); - dup2(fd[PIPE_READ], STDIN_FILENO); + int iAmParent; + pid_t childPid; - rc = system(shellCmd); + pm_fork(&iAmParent, &childPid, errorP); - exit(rc); - } else { - /* Parent */ - pid_t const childPid = rc; + if (!*errorP) { + if (iAmParent) { + close(fd[PIPE_READ]); - close(fd[PIPE_READ]); + *pidP = childPid; + *pipePP = fdopen(fd[PIPE_WRITE], "w"); + + if (*pipePP == NULL) + asprintfN(errorP,"Unable to create stream from pipe. " + "fdopen() fails with errno=%d (%s)", + errno, strerror(errno)); + else + *errorP = NULL; + } else { + int rc; + close(fd[PIPE_WRITE]); + close(STDIN_FILENO); + dup2(fd[PIPE_READ], STDIN_FILENO); - *pidP = childPid; - *pipePP = fdopen(fd[PIPE_WRITE], "w"); + rc = system(shellCmd); - if (*pipePP == NULL) - asprintfN(errorP,"Unable to create stream from pipe. " - "fdopen() fails with errno=%d (%s)", - errno, strerror(errno)); - else - *errorP = NULL; + exit(rc); + } } } } @@ -787,7 +783,7 @@ createFlipProcess(FILE * const outFileP, Create a process that runs the program Pamflip and writes its output to *imageoutFileP. - The process takes it input from a pipe that we create. We return as + The process takes its input from a pipe that we create. We return as *inPipePP a file stream connected to the other end of that pipe. I.e. Caller will write a Netpbm file stream to **inPipePP and a flipped @@ -973,12 +969,12 @@ pnmOut_init(FILE * const imageoutFileP, data, pnmOut get 'cols' x 'rows' data, but its output file may be 'rows x cols'. - Because the pnmOut object must be set up to receive flipped or not - flipped input, we have *flipOkP and *noflipOkP outputs that tell - Caller whether he has to flip or not. In the unique case that - the TIFF matrix is already oriented the way the output PNM file needs - to be, flipping is idempotent, so both *flipOkP and *noflipOkP are - true. + Because we must set up the pnmOut object either to receive flipped or not + flipped input, we have *flipOkP and *noflipOkP outputs that tell Caller + whether he has to flip or not. Note that Caller also influences which way + we set up pnmOut, with his 'flipIfNeeded' argument. In the unique case + that the TIFF matrix is already oriented the way the output PNM file needs + to be, flipping is idempotent, so both *flipOkP and *noflipOkP are true. -----------------------------------------------------------------------------*/ pnmOutP->imageoutFileP = imageoutFileP; pnmOutP->alphaFileP = alphaFileP; @@ -1040,14 +1036,12 @@ pnmOut_term(pnmOut * const pnmOutP, "waiting for Pamflip to terminate"); if (pnmOutP->imagePipeP) { - int status; fclose(pnmOutP->imagePipeP); - waitpid(pnmOutP->imageFlipPid, &status, 0); + pm_waitpidSimple(pnmOutP->imageFlipPid); } if (pnmOutP->alphaPipeP) { - int status; fclose(pnmOutP->alphaPipeP); - waitpid(pnmOutP->alphaFlipPid, &status, 0); + pm_waitpidSimple(pnmOutP->alphaFlipPid); } } else { if (pnmOutP->imageoutFileP) diff --git a/converter/pbm/Makefile b/converter/pbm/Makefile index c859b105..0461eedc 100644 --- a/converter/pbm/Makefile +++ b/converter/pbm/Makefile @@ -7,10 +7,11 @@ VPATH=.:$(SRCDIR)/$(SUBDIR) include $(BUILDDIR)/config.mk -PORTBINARIES = atktopbm brushtopbm cmuwmtopbm ddbugtopbm g3topbm escp2topbm \ +PORTBINARIES = atktopbm brushtopbm cistopbm cmuwmtopbm \ + ddbugtopbm g3topbm escp2topbm \ icontopbm macptopbm mdatopbm mgrtopbm mrftopbm \ pbmto10x pbmto4425 pbmtoascii pbmtoatk \ - pbmtobbnbg pbmtocmuwm pbmtodjvurle \ + pbmtobbnbg pbmtocis pbmtocmuwm pbmtodjvurle \ pbmtoepsi pbmtoepson pbmtoescp2 \ pbmtog3 pbmtogem pbmtogo pbmtoibm23xx pbmtoicon pbmtolj \ pbmtoln03 pbmtolps \ diff --git a/converter/pbm/cistopbm.c b/converter/pbm/cistopbm.c new file mode 100644 index 00000000..591e2aa5 --- /dev/null +++ b/converter/pbm/cistopbm.c @@ -0,0 +1,180 @@ +/* + * cistopbm: Convert images in the CompuServe RLE format to PBM + * Copyright (C) 2009 John Elliott + * + * 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 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "pbm.h" + + +static void syntax(const char *prog) +{ + pm_usage(" { options } { input }\n\n" + "Input file should be in CompuServe RLE format.\n" + "Output file will be in PBM format.\n" + "Options:\n" + "-i, --inverse: Reverse black and white.\n" + "--: End of options\n\n" +"cistopbm v1.01, Copyright 2009 John Elliott <jce@seasip.demon.co.uk>\n" +"This program is redistributable under the terms of the GNU General Public\n" +"License, version 2 or later.\n" + ); +} + +int main(int argc, const char **argv) +{ + FILE *ifP; + int c[3]; + int inoptions = 1; + int n, x, y; + int bw = PBM_BLACK; /* Default colouring is white on black */ + const char *inpname = NULL; + int height, width; + bit **bits; + + pm_proginit(&argc, argv); + + for (n = 1; n < argc; n++) + { + if (!strcmp(argv[n], "--")) + { + inoptions = 0; + continue; + } + if (inoptions) + { + if (pm_keymatch(argv[n], "-h", 2) || + pm_keymatch(argv[n], "-H", 2) || + pm_keymatch(argv[n], "--help", 6)) + { + syntax(argv[0]); + return EXIT_SUCCESS; + } + if (pm_keymatch(argv[n], "-i", 2) || + pm_keymatch(argv[n], "-I", 2) || + pm_keymatch(argv[n], "--inverse", 9)) + { + bw ^= (PBM_WHITE ^ PBM_BLACK); + continue; + } + if (argv[n][0] == '-' && argv[n][1] != 0) + { + pm_message("Unknown option: %s", argv[n]); + syntax(argv[0]); + return EXIT_FAILURE; + } + } + + if (inpname == NULL) inpname = argv[n]; + else { syntax(argv[0]); return EXIT_FAILURE; } + } + if (inpname == NULL) inpname = "-"; + ifP = pm_openr(inpname); + + /* There may be junk before the magic number. If so, skip it. */ + x = 0; + c[0] = c[1] = c[2] = EOF; + + /* Read until the array c[] holds the magic number. */ + do + { + c[0] = c[1]; + c[1] = c[2]; + c[2] = fgetc(ifP); + + /* If character read was EOF, end of file was reached and magic number + * not found. + */ + if (c[2] == EOF) + { + pm_error("Input file is not in CompuServe RLE format"); + } + ++x; + } while (c[0] != 0x1B || c[1] != 0x47); + + /* x = number of bytes read. Should be 3 if signature is at the start */ + if (x > 3) + { + pm_message("Warning: %d bytes of junk skipped before image", + x - 3); + } + /* Parse the resolution */ + switch(c[2]) + { + case 0x48: height = 192; width = 256; break; + case 0x4D: height = 96; width = 128; break; + default: pm_error("Unknown resolution 0x%02x", c[2]); + break; + } + /* Convert the data */ + bits = pbm_allocarray(width, height); + x = y = 0; + do + { + c[0] = fgetc(ifP); + + /* Stop if we hit EOF or Escape */ + if (c[0] == EOF) break; /* EOF */ + if (c[0] == 0x1B) break; /* End of graphics */ + /* Other non-printing characters are ignored; some files contain a + * BEL + */ + if (c[0] < 0x20) continue; + + /* Each character gives the number of pixels to draw in the appropriate + * colour. + */ + for (n = 0x20; n < c[0]; n++) + { + if (x < width && y < height) bits[y][x] = bw; + x++; + /* Wrap at end of line */ + if (x >= width) + { + x = 0; + y++; + } + } + /* And toggle colours */ + bw ^= (PBM_WHITE ^ PBM_BLACK); + } + while (1); + + /* See if the end-graphics signature (ESC G N) is present. */ + c[1] = EOF; + if (c[0] == 0x1B) + { + c[1] = fgetc(ifP); + c[2] = fgetc(ifP); + } + if (c[0] != 0x1B || c[1] != 0x47 || c[2] != 0x4E) + { + pm_message("Warning: End-graphics signature not found"); + } + /* See if we decoded the right number of pixels */ + if (x != 0 || y != height) + { + pm_message("Warning: %d pixels found, should be %d", + y * width + x, width * height); + } + pbm_writepbm(stdout, bits, width, height, 0); + pm_close(ifP); + return 0; +} diff --git a/converter/pbm/pbmtocis.c b/converter/pbm/pbmtocis.c new file mode 100644 index 00000000..9bb42c56 --- /dev/null +++ b/converter/pbm/pbmtocis.c @@ -0,0 +1,170 @@ +/* + * cistopbm: Convert images in the CompuServe RLE format to PBM + * Copyright (C) 2009 John Elliott + * + * 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 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "pbm.h" + +/* The maximum length of a run. Limit it to 0x5E bytes so that it is always + * represented by a printable character 0x20-0x7E */ +#define MAXRUNLENGTH 0x5E + +static void syntax(const char *prog) +{ + pm_usage(" { options } { input } }\n\n" + "Input file should be in PBM format.\n" + "Output will be in CompuServe RLE format.\n" + "Options:\n" + "-i, --inverse: Reverse black and white.\n" + "-w, --whitebg: White background.\n" + "--: End of options\n\n" +"pbmtocis v1.00, Copyright 2009 John Elliott <jce@seasip.demon.co.uk>\n" +"This program is redistributable under the terms of the GNU General Public\n" +"License, version 2 or later.\n" + ); +} + +int main(int argc, const char **argv) +{ + FILE *ofP = stdout; + FILE *ifP; + int inoptions = 1; + int n, x, y; + int bg = PBM_BLACK; /* Default colouring is white on black */ + int inverse = 0; + int cell, last, run; + const char *inpname = NULL; + + int outh, outw; + int height, width; + bit **bits; + + pm_proginit(&argc, argv); + + for (n = 1; n < argc; n++) + { + if (!strcmp(argv[n], "--")) + { + inoptions = 0; + continue; + } + if (inoptions) + { + if (pm_keymatch(argv[n], "-h", 2) || + pm_keymatch(argv[n], "-H", 2) || + pm_keymatch(argv[n], "--help", 6)) + { + syntax(argv[0]); + return EXIT_SUCCESS; + } + if (pm_keymatch(argv[n], "-i", 2) || + pm_keymatch(argv[n], "-I", 2) || + pm_keymatch(argv[n], "--inverse", 9)) + { + inverse = 1; + continue; + } + if (pm_keymatch(argv[n], "-w", 2) || + pm_keymatch(argv[n], "-W", 2) || + pm_keymatch(argv[n], "--whitebg", 9)) + { + bg = PBM_WHITE; + continue; + } + if (argv[n][0] == '-' && argv[n][1] != 0) + { + pm_message("Unknown option: %s", argv[n]); + syntax(argv[0]); + return EXIT_FAILURE; + } + } + + if (inpname == NULL) inpname = argv[n]; + else { syntax(argv[0]); return EXIT_FAILURE; } + } + if (inpname == NULL) inpname = "-"; + ifP = pm_openr(inpname); + + /* Load the PBM */ + bits = pbm_readpbm(ifP, &width, &height); + + if (width <= 128 && height <= 96) { outw = 128; outh = 96; } + else if (width <= 256 && height <= 192) { outw = 256; outh = 192; } + else + { + outw = 256; + outh = 192; + pm_message("Warning: Input file is larger than 256x192. " + "It will be cropped."); + } + /* Write the magic number */ + fputc(0x1B, ofP); + fputc(0x47, ofP); + fputc((outw == 128) ? 0x4D : 0x48, ofP); + + /* And now start encoding */ + y = x = 0; + last = PBM_BLACK; + run = 0; + while (y < outh) + { + if (x < width && y < height) + { + cell = bits[y][x]; + if (inverse) cell ^= (PBM_BLACK ^ PBM_WHITE); + } + else cell = bg; + + if (cell == last) /* Cell is part of current run */ + { + ++run; + if (run > MAXRUNLENGTH) + { + fputc(0x20 + MAXRUNLENGTH, ofP); + fputc(0x20, ofP); + run -= MAXRUNLENGTH; + } + } + else /* change */ + { + fputc(run + 0x20, ofP); + last = last ^ (PBM_BLACK ^ PBM_WHITE); + run = 1; + } + ++x; + if (x >= outw) { x = 0; ++y; } + } + if (last == bg) /* Last cell written was background. Write foreground */ + { + fputc(run + 0x20, ofP); + } + else if (run) /* Write background and foreground */ + { + fputc(run + 0x20, ofP); + fputc(0x20, ofP); + } + /* Write the end-graphics signature */ + fputc(0x1B, ofP); + fputc(0x47, ofP); + fputc(0x4E, ofP); + pm_close(ifP); + return 0; +} diff --git a/doc/HISTORY b/doc/HISTORY index 41d36953..b4ba92d2 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -4,39 +4,53 @@ Netpbm. CHANGE HISTORY -------------- -09.09.18 BJH Release 10.47.04 +09.09.27 BJH Release 10.48.00 - pambayer: fix unconditional crash/failure when you aren't using - Standard Input. + 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. - Build: use AR from config.mk instead of "ar" to build static - libraries: libnetpbm, librle, libjasper. + tifftopnm: wait for Pamflip processes to terminate before + exiting. - Build: compiles with libpng 1.4 beta. + Remove pngtopnm from the package. Pngtopnm is now an alias + for Pngtopam. - Build: don't use "uint". + pngtopam, pnmtopng: Compiles with libpng 1.4 beta. - Package: fix installation of pnmplain on Windows (.exe suffix). - -09.09.03 BJH Release 10.47.03 + pamtotiff: do miniswhite properly with 8 and 16 bit samples. - Build: fix failure to recognize static library in omitting - -R from link. + pamsumm: fix syntax error message. -09.08.17 BJH Release 10.47.02 + pambayer: fix unconditional crash/failure when you aren't using + Standard Input. - tifftopnm: wait for Pamflip processes to terminate before - exiting. + Add pbmtocis, cistopbm. Thanks John Elliott + <jce@seasip.demon.co.uk>. - Build: Compiles with libpng 1.4 beta. + Build: tifftopnm builds on systems without fork(). Build: work with JPEG library Version 7. -09.07.23 BJH Release 10.47.01 + 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. diff --git a/editor/pamscale.c b/editor/pamscale.c index 69861e0e..35c76b17 100644 --- a/editor/pamscale.c +++ b/editor/pamscale.c @@ -610,11 +610,11 @@ parseCommandLine(int argc, OPTENT3(0, "xysize", OPT_FLAG, NULL, &xyfit, 0); OPTENT3(0, "xyfit", OPT_FLAG, NULL, &xyfit, 0); OPTENT3(0, "xyfill", OPT_FLAG, NULL, &xyfill, 0); - OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); OPTENT3(0, "filter", OPT_STRING, &filterOpt, &filterSpec, 0); OPTENT3(0, "window", OPT_STRING, &window, &windowSpec, 0); - OPTENT3(0, "nomix", OPT_FLAG, NULL, &cmdlineP->nomix, 0); - OPTENT3(0, "linear", OPT_FLAG, NULL, &cmdlineP->linear, 0); + OPTENT3(0, "nomix", OPT_FLAG, NULL, &cmdlineP->nomix, 0); + OPTENT3(0, "linear", OPT_FLAG, NULL, &cmdlineP->linear, 0); /* Set the defaults. -1 = unspecified */ diff --git a/lib/Makefile b/lib/Makefile index 67fac5b2..5632b8f0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -67,8 +67,6 @@ BINARIES = OMIT_LIBRARY_RULE = 1 include $(SRCDIR)/common.mk -INCLUDES = -I$(SRCDIR)/$(SUBDIR) -I. -Iimportinc - # The following must go after common.mk because $(LIBNETPBM) may # contain a reference to $(NETPBM_MAJOR_RELEASE). .PHONY: libnetpbm diff --git a/lib/libpm.c b/lib/libpm.c index 1089da3c..099d4bf7 100644 --- a/lib/libpm.c +++ b/lib/libpm.c @@ -10,6 +10,9 @@ #define _XOPEN_SOURCE 500 /* Make sure ftello, fseeko are defined */ +#include "netpbm/pm_config.h" + +#include <assert.h> #include <unistd.h> #include <stdio.h> #include <stdarg.h> @@ -18,15 +21,19 @@ #include <setjmp.h> #include <time.h> #include <limits.h> +#if HAVE_FORK +#include <sys/wait.h> +#endif +#include <sys/types.h> -#include "pm_c_util.h" -#include "mallocvar.h" -#include "version.h" +#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 "nstring.h" -#include "shhopt.h" -#include "pm.h" +#include "netpbm/pm.h" /* The following are set by pm_init(), then used by subsequent calls to other pm_xxx() functions. @@ -95,6 +102,86 @@ pm_longjmp(void) { 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) { + asprintfN(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 + asprintfN(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) { + asprintfN(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); + strfree(error); + pm_longjmp(); + } else { + assert(exitedPid != 0); + } +} + + + +void pm_setusererrormsgfn(pm_usererrormsgfn * fn) { userErrorMsgFn = fn; diff --git a/lib/libsystem_dummy.c b/lib/libsystem_dummy.c index 2787ce67..c89b4d5d 100644 --- a/lib/libsystem_dummy.c +++ b/lib/libsystem_dummy.c @@ -4,7 +4,7 @@ This is a dummy version of libsystem.c, for use on systems that don't have the kind of process control that libsystem.c needs. - With this module, program will build cleanly, but if a program actually + 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. =============================================================================*/ diff --git a/lib/pm.h b/lib/pm.h index 4ba20aa5..55ea4f29 100644 --- a/lib/pm.h +++ b/lib/pm.h @@ -164,6 +164,21 @@ pm_setjmpbufsave(jmp_buf * const jmpbufP, 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); diff --git a/lib/util/Makefile b/lib/util/Makefile index 8b3fa1c1..f5f0e04c 100644 --- a/lib/util/Makefile +++ b/lib/util/Makefile @@ -17,8 +17,6 @@ all: $(UTILOBJECTS) include $(SRCDIR)/common.mk -INCLUDES = -I$(SRCDIR)/$(SUBDIR) -I. -Iimportinc - $(UTILOBJECTS):%.o:%.c importinc $(CC) -c $(INCLUDES) -DNDEBUG $(CPPFLAGS) $(CFLAGS) $(CFLAGS_SHLIB) \ $(CFLAGS_PERSONAL) $(CADD) -o $@ $< diff --git a/other/ppmsvgalib.c b/other/ppmsvgalib.c index 5bcabdc1..8598cef2 100644 --- a/other/ppmsvgalib.c +++ b/other/ppmsvgalib.c @@ -253,10 +253,6 @@ main(int argc, char *argv[]) { ppm_init(&argc, argv); - rc = vga_init(); /* Initialize. */ - if (rc < 0) - pm_error("Svgalib unable to allocate a virtual console."); - parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); @@ -269,6 +265,14 @@ main(int argc, char *argv[]) { &checkResult); } + /* We wait to initialize Svgalib to prevent it from interfering with + error messages the code above might issue, and leaving the console + in an undesirable state if the above code aborts the program. + */ + rc = vga_init(); /* Initialize. */ + if (rc < 0) + pm_error("Svgalib unable to allocate a virtual console."); + if (vga_hasmode(cmdline.mode)) display(ifP, cols, rows, maxval, format, cmdline.mode, cmdline.verbose); diff --git a/urt/rle_addhist.c b/urt/rle_addhist.c index 04e26206..0df9642d 100644 --- a/urt/rle_addhist.c +++ b/urt/rle_addhist.c @@ -37,7 +37,7 @@ #include <sys/time.h> #endif -#include "mallocvar.h" +#include "netpbm/mallocvar.h" /***************************************************************** * TAG( rle_addhist ) diff --git a/urt/rle_getrow.c b/urt/rle_getrow.c index fadf5441..bd7d1c8e 100644 --- a/urt/rle_getrow.c +++ b/urt/rle_getrow.c @@ -33,8 +33,8 @@ #include <string.h> #include <stdio.h> -#include "pm.h" -#include "mallocvar.h" +#include "netpbm/pm.h" +#include "netpbm/mallocvar.h" #include "rle.h" #include "rle_code.h" diff --git a/urt/rle_open_f.c b/urt/rle_open_f.c index 07dbea01..7234afe9 100644 --- a/urt/rle_open_f.c +++ b/urt/rle_open_f.c @@ -15,8 +15,8 @@ #include <unistd.h> #include <fcntl.h> -#include "pm_c_util.h" -#include "nstring.h" +#include "netpbm/pm_c_util.h" +#include "netpbm/nstring.h" #include "rle.h" diff --git a/urt/rle_putcom.c b/urt/rle_putcom.c index b1215661..ab2eb208 100644 --- a/urt/rle_putcom.c +++ b/urt/rle_putcom.c @@ -27,8 +27,8 @@ #include <stdio.h> -#include "mallocvar.h" -#include "pm.h" +#include "netpbm/mallocvar.h" +#include "netpbm/pm.h" #include "rle.h" /***************************************************************** diff --git a/urt/scanargs.c b/urt/scanargs.c index b91f3e37..e1f15047 100644 --- a/urt/scanargs.c +++ b/urt/scanargs.c @@ -49,8 +49,8 @@ #include <stdarg.h> #endif -#include "pm_c_util.h" -#include "nstring.h" +#include "netpbm/pm_c_util.h" +#include "netpbm/nstring.h" /* * An explicit assumption is made in this code that all pointers look diff --git a/version.mk b/version.mk index f1196705..d01fdb60 100644 --- a/version.mk +++ b/version.mk @@ -1,4 +1,4 @@ NETPBM_MAJOR_RELEASE = 10 -NETPBM_MINOR_RELEASE = 47 -NETPBM_POINT_RELEASE = 4 +NETPBM_MINOR_RELEASE = 48 +NETPBM_POINT_RELEASE = 0 |