Use built-in compiler defines to detect SSE support.

Remove SSE tests from SConstruct.
Minimize libimagequant files.
This commit is contained in:
Daniel Patterson 2015-05-20 16:17:47 -07:00
parent d0c1bed5a5
commit 40be65c947
33 changed files with 38 additions and 3063 deletions

View file

@ -297,7 +297,6 @@ opts.AddVariables(
('CUSTOM_DEFINES', 'Custom Compiler DEFINES, e.g. -DENABLE_THIS', ''), ('CUSTOM_DEFINES', 'Custom Compiler DEFINES, e.g. -DENABLE_THIS', ''),
('CUSTOM_CFLAGS', 'Custom C flags, e.g. -I<include dir> if you have headers in a nonstandard directory <include dir> (only used for configure checks)', ''), ('CUSTOM_CFLAGS', 'Custom C flags, e.g. -I<include dir> if you have headers in a nonstandard directory <include dir> (only used for configure checks)', ''),
('CUSTOM_LDFLAGS', 'Custom linker flags, e.g. -L<lib dir> if you have libraries in a nonstandard directory <lib dir>', ''), ('CUSTOM_LDFLAGS', 'Custom linker flags, e.g. -L<lib dir> if you have libraries in a nonstandard directory <lib dir>', ''),
EnumVariable('USE_SSE', "Build with SSE instruction set (will default to 'no' if cross-compiling)",'detect', ['detect','yes','no']),
EnumVariable('LINKING', "Set library format for libmapnik",'shared', ['shared','static']), EnumVariable('LINKING', "Set library format for libmapnik",'shared', ['shared','static']),
EnumVariable('RUNTIME_LINK', "Set preference for linking dependencies",'shared', ['shared','static']), EnumVariable('RUNTIME_LINK', "Set preference for linking dependencies",'shared', ['shared','static']),
EnumVariable('OPTIMIZATION','Set compiler optimization level','3', ['0','1','2','3','4','s']), EnumVariable('OPTIMIZATION','Set compiler optimization level','3', ['0','1','2','3','4','s']),
@ -1216,38 +1215,6 @@ if not preconfigured:
env.Append(LINKFLAGS = DEFAULT_CXX11_LINKFLAGS) env.Append(LINKFLAGS = DEFAULT_CXX11_LINKFLAGS)
env.Append(LINKFLAGS = env['CUSTOM_LDFLAGS']) env.Append(LINKFLAGS = env['CUSTOM_LDFLAGS'])
### platform specific bits
#
# libimagequant includes SSE optimizations, so detect if flag is set (USE_SSE)
# or if 'detect', check current machine for support
SSE_DETECTED = False
if "darwin" in sys.platform:
import subprocess
process = subprocess.Popen(["sysctl","-a"], stdout=subprocess.PIPE)
result = process.communicate()[0]
for line in result.split("\n"):
if "cpu.feature" in line and "SSE" in line:
if "USE_SSE" in env and env["USE_SSE"] == "detect":
color_print(4,'SSE detected on Darwin host')
SSE_DETECTED = True
break
elif "linux" in sys.platform:
for line in open("/proc/cpuinfo"):
if "flags" in line and "sse" in line:
if "USE_SSE" in env and env["USE_SSE"] == "detect":
color_print(4,'SSE detected on Linux host')
SSE_DETECTED = True
break
else:
if "USE_SSE" in env and env["USE_SSE"] == "detect":
color_print(4,'SSE detection not implemented on this platform, building without SSE')
# Only enable SSE when explitily set, or when not cross compiling and current host supports it
if (not env["HOST"] and (env["USE_SSE"] == "detect" and SSE_DETECTED)) or env["USE_SSE"] == "yes":
env.Append(CFLAGS = '-DUSE_SSE -msse')
if 'gcc' in env['CC']:
lib_env.Append(CFLAGS='-mfpmath=sse')
thread_suffix = 'mt' thread_suffix = 'mt'
if env['PLATFORM'] == 'FreeBSD': if env['PLATFORM'] == 'FreeBSD':
thread_suffix = '' thread_suffix = ''
@ -1626,7 +1593,7 @@ if not preconfigured:
# prepend to make sure we link locally # prepend to make sure we link locally
env.Prepend(CPPPATH = '#deps/agg/include') env.Prepend(CPPPATH = '#deps/agg/include')
env.Prepend(LIBPATH = '#deps/agg') env.Prepend(LIBPATH = '#deps/agg')
env.Prepend(CPPPATH = '#deps/pngquant/lib') env.Prepend(CPPPATH = '#deps/pngquant')
env.Prepend(CPPPATH = '#deps/clipper/include') env.Prepend(CPPPATH = '#deps/clipper/include')
# prepend deps dir for auxillary headers # prepend deps dir for auxillary headers
env.Prepend(CPPPATH = '#deps') env.Prepend(CPPPATH = '#deps')

View file

@ -1,11 +0,0 @@
*.dylib
*.mode1v3
*.o
*.pbxproj
*.pbxuser
build
DerivedData
pngquant
build_configuration
lib/libimagequant.a
config.mk

View file

@ -1,125 +0,0 @@
version 2.4
-----------
- fixed remapping of bright colors when dithering
- added libimagequant API to add fixed preset colors to the palette
version 2.3
-----------
- added ./configure script for better support of Intel C compiler and dependencies [thanks to pdknsk]
- tweaked quality metric to better estimate quality of images with large solid backgrounds [thanks to Rolf Timmermans]
- atomic file saves and fixed --skip-if-larger
- avoid applying quality setting to images that use palette already
- preserving standard PNG chunks (requires libpng 1.6)
- deprecated libpng 1.2 support
version 2.2
-----------
- preserving of unknown PNG chunks (enables optimized Android 9-patch images)
- improved color profile support: cHRM & gAMA as alternative to ICC profiles, OpenMP acceleration
- improved support for Intel C Compiler, speedup in 32-bit GCC, and some workarounds for Visual Studio's incomplete C support
version 2.1
-----------
- option to save files only if they're compressed better than the original
- option to generate posterized output (for use with 16-bit textures)
- support for ICC profiles via Little CMS library
version 2.0
-----------
- refactored codebase into pngquant and standalone libimagequant
- reduced memory usage by further 30% (and more for very large images)
- less precise remapping improving speed by 25% in higher speed settings
- --output option for writing converted file under the given path
- light dithering with --floyd=0.5
- fixed regression in dithering of alpha channel
version 1.8
-----------
- min/max quality option (number of colors is automatically adjusted for desired quality level)
- switched option parsing to getopt_long (syntax such as -s1 and --ext=ext is supported)
- significantly improved performance thanks to custom partial sorting
- optional Cocoa (Mac OS X) image reader for color profile support
- reduced memory usage by 20%
- remapping improved for very low number of colors
version 1.7
-----------
- new, accurate RGBA color similarity algorithm
- change of optional SSE3 code to SSE2 that is always enabled on x86-64
- optional OpenMP-based parallelisation of remapping
- changed long options to use double hyphen (-force to --force) [thanks to Jari Aalto]
version 1.6
-----------
- novel dithering algorithm that doesn't add noise unless necessary
- perceptual weighting of colors taking into account edges and noise
- much faster remapping
- improved portability, makefiles and man page
version 1.5
-----------
- palettes postprocessed with Voronoi iteration
- better RGBA color similarity algorithm and Floyd-Steinberg remapping
- SSE optimisations
version 1.4
-----------
- median cut is applied many times in a feedback loop
- speed/quality trade-off option
- faster remap of transparent areas
version 1.3
-----------
- significant changes to the algorithm: use of variance
to find largest dimensioin and to split most varying boxes
- use of premultiplied alpha for color blending
- conversion of output to gamma 2.2
version 1.2
-----------
- color computation done in floating point
- gamma correction applied
- dropped support for very old systems & compilers
version 1.1
-----------
- alpha-sensitive color reduction and dithering
- support -- and - arguments in command line
- number of colors optional (defaults to 256)
- increased maximum number of colors in histogram
version 1.0
-----------
- cleaned up Makefile.unx (better gcc optimizations, "clean" target)
- recompiled binaries with zlib 1.1.4
version 0.95
------------
- fixed Win32 filter bug (binary mode for stdin/stdout)
- fixed cosmetic "choosing colors" verbosity buglet
- fixed palette-size bug when number of colors in image < number requested
- fixed sample-depth bug (png_set_packing() not retroactively smart)
version 0.91
------------
- fixed some verbose/non-verbose oopers
- fixed Win32 (MSVC) portability issues (getpid(), random(), srandom())
- added Makefile.w32 for MSVC (tested with 5.0)
version 0.90
------------
- added support for multiple files on command line
- changed stdin support to write PNG stream to stdout (not "stdin-fs8.png")
version 0.75
------------
- added support for any type of input file [Glenn Randers-Pehrson]
- fixed palette-(re)scaling bug
- added -verbose and -quiet options (default now is -quiet)
- added palette-remapping to minimize size of tRNS chunk
- made Floyd-Steinberg dithering default
- changed output naming scheme to -fs8.png and -or8.png (FS or ordered dither)
version 0.70
------------
- first public release

View file

@ -1,56 +1,36 @@
The quantization and dithering code in pngquant is lifted from Jef Poskanzer's © 1997-2002 by Greg Roelofs; based on an idea by Stefan Schneider.
'ppmquant', part of his wonderful PBMPLUS tool suite. © 2009-2014 by Kornel Lesiński.
Greg Roelofs hacked it into a (in his words) "slightly cheesy" 'pamquant' back All rights reserved.
in 1997 (see http://pobox.com/~newt/greg_rgba.html) and finally he ripped out
the cheesy file-I/O parts and replaced them with nice PNG code in December
2000. The PNG reading and writing code is a merged and slightly simplified
version of readpng, readpng2, and writepng from his book "PNG: The Definitive
Guide."
In 2014 Greg has relicensed the code under the simplified BSD license.
Note that both licenses are basically BSD-like; that is, use the code however Redistribution and use in source and binary forms, with or without modification,
you like, as long as you acknowledge its origins. are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
pngquant.c: © 1989, 1991 by Jef Poskanzer.
© 1989, 1991 by Jef Poskanzer. Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, provided
Permission to use, copy, modify, and distribute this software and its that the above copyright notice appear in all copies and that both that
documentation for any purpose and without fee is hereby granted, provided copyright notice and this permission notice appear in supporting
that the above copyright notice appear in all copies and that both that documentation. This software is provided "as is" without express or
copyright notice and this permission notice appear in supporting implied warranty.
documentation. This software is provided "as is" without express or
implied warranty.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
pngquant.c and rwpng.c/h:
© 1997-2002 by Greg Roelofs; based on an idea by Stefan Schneider.
© 2009-2014 by Kornel Lesiński.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

55
deps/pngquant/INSTALL vendored
View file

@ -1,55 +0,0 @@
To build pngquant from source on Mac OS X and most Linux distributions,
simply run:
$ make
It will create pngquant executable in the current directory. If you'd like
to install it system-wide:
$ sudo make install
By default it will be installed in /usr/local/bin. To install it in another
directory run `./configure --prefix=dir && make`.
pngquant uses GNU Makefile. To compile on FreeBSD you will need to use gmake,
and on Windows the MinGW compiler (MSVC does not support C99).
pngquant will compile with libpng 1.2, but you should use 1.5 or later.
##Compilation with OpenMP
$ ./configure --with-openmp && make
This makes pngquant faster in wall-clock time on multicore machines when one
image at a time is processed.
However, it increases total CPU time used, and thus it's not most optimal
for server-side and parallelized batch jobs which run many pngquant
instances at a time.
On OS X you may need to install gcc and add `CC=gcc-4.9` to `./configure`,
because clang doesn't support OpenMP (yet).
##Compilation without Cocoa image reader
Mac OS X version uses Cocoa to read images. This adds support for color profiles
and other image formats as input. `./configure --without-cocoa` switches back
to libpng.
##Compilation with Little CMS 2
$ ./configure --with-lcms2 && make
Enables support for ICC v2/v4 color profiles when reading images.
Requires Little CMS library available via `pgk-config` (e.g. install `liblcms2-dev`).
##Compilation of `libimagequant.a` only
If you want to use pngquant's conversion algorithm without loading/saving PNG
files, then you can run `make` in the `lib/` directory.
The library doesn't need libpng or zlib.

View file

@ -1,75 +0,0 @@
-include config.mk
BIN ?= pngquant
BINPREFIX = $(DESTDIR)$(PREFIX)/bin
OBJS = pngquant.o rwpng.o
COCOA_OBJS = rwpng_cocoa.o
ifeq (1, $(COCOA_READER))
OBJS += $(COCOA_OBJS)
endif
STATICLIB = lib/libimagequant.a
DISTFILES = *.[chm] pngquant.1 Makefile configure README.md INSTALL CHANGELOG COPYRIGHT
TARNAME = pngquant-$(VERSION)
TARFILE = $(TARNAME)-src.tar.bz2
LIBDISTFILES = lib/*.[ch] lib/COPYRIGHT lib/MANUAL.md lib/configure lib/Makefile
DLL=libimagequant.dll
DLLIMP=libimagequant_dll.a
DLLDEF=libimagequant_dll.def
all: $(BIN)
staticlib:
$(MAKE) -C lib static
$(STATICLIB): config.mk staticlib
$(OBJS): $(wildcard *.h) config.mk
rwpng_cocoa.o: rwpng_cocoa.m
$(CC) -Wno-enum-conversion -c $(CFLAGS) -o $@ $< || clang -Wno-enum-conversion -c -O3 $(CFLAGS) -o $@ $<
$(BIN): $(OBJS) $(STATICLIB)
$(CC) $^ $(LDFLAGS) -o $@
test: $(BIN)
./test/test.sh ./test $(BIN)
dist: $(TARFILE)
$(TARFILE): $(DISTFILES)
rm -rf $(TARFILE) $(TARNAME)
mkdir -p $(TARNAME)/lib
cp $(DISTFILES) $(TARNAME)
cp $(LIBDISTFILES) $(TARNAME)/lib
tar -cjf $(TARFILE) --numeric-owner --exclude='._*' $(TARNAME)
rm -rf $(TARNAME)
-shasum $(TARFILE)
install: $(BIN)
-mkdir -p '$(BINPREFIX)'
install -m 0755 -p '$(BIN)' '$(BINPREFIX)/$(BIN)'
uninstall:
rm -f '$(BINPREFIX)/$(BIN)'
clean:
$(MAKE) -C lib clean
rm -f '$(BIN)' $(OBJS) $(COCOA_OBJS) $(STATICLIB) $(TARFILE)
distclean: clean
$(MAKE) -C lib distclean
rm -f config.mk pngquant-*-src.tar.bz2
config.mk:
ifeq ($(filter %clean %distclean, $(MAKECMDGOALS)), )
./configure
endif
.PHONY: all clean dist distclean dll install uninstall test staticlib
.DELETE_ON_ERROR:

View file

@ -1,97 +0,0 @@
#pngquant 2
This is the official `pngquant` and `libimagequant`.
[pngquant](http://pngquant.org) converts 24/32-bit RGBA PNGs to 8-bit palette with *alpha channel preserved*.
Such images are compatible with all modern browsers, and a special compatibility setting exists which helps transparency degrade well in Internet Explorer 6.
Quantized files are often 40-70% smaller than their 24/32-bit version.
This utility works on Linux, Mac OS X and Windows.
##Usage
- batch conversion of multiple files: `pngquant *.png`
- Unix-style stdin/stdout chaining: `… | pngquant - | …`
To further reduce file size, try [optipng](http://optipng.sourceforge.net) or [ImageOptim](http://imageoptim.pornel.net).
##Improvements since 1.0
Generated files are both smaller and look much better.
* Significantly better quality of quantisation
- more accurate remapping of semitransparent colors
- special dithering algorithm that does not add noise in well-quantized areas of the image
- uses variance instead of popularity for box selection (improvement suggested in the original median cut paper)
- feedback loop that repeats median cut for poorly quantized colors
- additional colormap improvement using Voronoi iteration
- supports much larger number of colors in input images without degradation of quality
- gamma correction (output is always generated with gamma 2.2 for web compatibility)
* More flexible commandline usage
- number of colors defaults to 256
- long options and standard switches like `--` and `-` are allowed
* Refactored and modernised code
- C99 with no workarounds for old systems
- floating-point math used throughout
- Intel SSE optimisations
- multicore support via OpenMP
- quantization moved to standalone libimagequant
##Options
See `pngquant -h` for full list.
###`--quality min-max`
`min` and `max` are numbers in range 0 (worst) to 100 (perfect), similar to JPEG. pngquant will use the least amount of colors required to meet or exceed the `max` quality. If conversion results in quality below the `min` quality the image won't be saved (if outputting to stdin, 24-bit original will be output) and pngquant will exit with status code 99.
pngquant --quality=65-80 image.png
###`--ext new.png`
Set custom extension (suffix) for output filename. By default `-or8.png` or `-fs8.png` is used. If you use `--ext=.png --force` options pngquant will overwrite input files in place (use with caution).
###`-o out.png` or `--output out.png`
Writes converted file to the given path. When this option is used only single input file is allowed.
###`--skip-if-larger`
Don't write converted files if the conversion isn't worth it.
###`--speed N`
Speed/quality trade-off from 1 (brute-force) to 11 (fastest). The default is 3. Speed 10 has 5% lower quality, but is 8 times faster than the default. Speed 11 disables dithering and lowers compression level.
###`--nofs`
Disables Floyd-Steinberg dithering.
###`--floyd=0.5`
Controls level of dithering (0 = none, 1 = full).
###`--posterize bits`
Reduce precision of the palette by number of bits. Use when the image will be displayed on low-depth screens (e.g. 16-bit displays or compressed textures in ARGB444 format).
###`--version`
Print version information to stdout.
###`-`
Read image from stdin and send result to stdout.
###`--`
Stops processing of arguments. This allows use of file names that start with `-`. If you're using pngquant in a script, it's advisable to put this before file names:
pngquant $OPTIONS -- "$FILE"

View file

@ -1,427 +0,0 @@
#!/usr/bin/env bash
CONFIG="config.mk"
PREFIX="/usr/local"
VERSION=$(grep LIQ_VERSION_STRING lib/libimagequant.h | grep -Eo "2\.[0-9.]+")
DEBUG=
SSE=auto
OPENMP=
if [[ "$OSTYPE" =~ "darwin" ]]; then
COCOA_READER=auto
LCMS2=0
else
COCOA_READER=0
LCMS2=auto
fi
EXTRA_CFLAGS=
EXTRA_LDFLAGS=
# make gcc default compiler unless CC is already set
CC=${CC:-gcc}
help() {
printf "%4s %s\n" "" "$1"
}
for i in "$@"; do
case $i in
--help)
echo
help "--prefix= installation directory [$PREFIX]"
help "--extra-cflags= append to CFLAGS"
help "--extra-ldflags= append to LDFLAGS"
echo
help "--enable-debug"
help "--enable-sse/--disable-sse enable/disable SSE instructions"
echo
help "--with-openmp compile with multicore support"
help "--with-lcms2/--without-lcms2 compile with color profile support"
if [[ "$OSTYPE" =~ "darwin" ]]; then
help "--with-cocoa/--without-cocoa use Cocoa framework to read images"
fi
echo
help "CC=<compiler> use given compiler command"
help "CFLAGS=<flags> pass options to the compiler"
help "LDFLAGS=<flags> pass options to the linker"
echo
exit 0
;;
# Can be set before or after configure. Latter overrides former.
CC=*)
CC=${i#*=}
;;
CFLAGS=*)
CFLAGS=${i#*=}
;;
LDFLAGS=*)
LDFLAGS=${i#*=}
;;
--enable-debug)
DEBUG=1
;;
--enable-sse)
SSE=1
;;
--disable-sse)
SSE=0
;;
--with-openmp)
OPENMP=1
;;
--with-lcms2)
LCMS2=1
COCOA_READER=0
;;
--without-lcms2)
LCMS2=0
;;
--with-cocoa)
COCOA_READER=1
LCMS2=0
;;
--without-cocoa)
COCOA_READER=0
;;
--prefix=*)
PREFIX=${i#*=}
;;
# can be used multiple times or in quotes to set multiple flags
--extra-cflags=*)
EXTRA_CFLAGS="$EXTRA_CFLAGS ${i#*=}"
;;
--extra-ldflags=*)
EXTRA_LDFLAGS="$EXTRA_LDFLAGS ${i#*=}"
;;
*)
echo "error: unknown switch ${i%%=*} (see $0 --help for the list)"
exit 1
;;
esac
done
# If someone runs sudo make install as very first command, and configure later,
# $CONFIG cannot be overwritten, and must be deleted before continuing.
if [[ -f "$CONFIG" && ! -w "$CONFIG" ]]; then
echo "Cannot overwrite file $CONFIG! Please delete it."
exit 1
fi
cflags() {
CFLAGS="$CFLAGS $1"
}
lflags() {
LDFLAGS="$LDFLAGS $1"
}
status() {
printf "%10s: %s\n" "$1" "$2"
}
# Append to CFLAGS if compiler supports flag, with optional prerequisite.
# Fails on errors and warnings.
conditional_cflags() {
if [ -z "echo | $("$CC" -xc -S -o /dev/null $2 $1 2>&1)" ]; then
cflags "$1"
fi
}
# returns first matching file in directory
find_f() {
echo $(find "$1" -not -type d -name "$2" -print -quit 2> /dev/null)
}
# returns first matching file in directory (no symlinks)
find_h() {
echo $(find "$1" -type f -name "$2" -print -quit 2> /dev/null)
}
find_pkgconfig() {
LIBNAME=$1
if pkg-config --exists "$LIBNAME" &> /dev/null; then
cflags "$(pkg-config --cflags "$LIBNAME")"
lflags "$(pkg-config --libs "$LIBNAME")"
status "$LIBNAME" "shared ($(pkg-config --modversion "$LIBNAME"))"
return 0
fi
return 1
}
find_static() {
LIBNAME=$1
HEADERPATTERN=$2
STATICPATTERN=$3
HPATH=$(find_h . "$HEADERPATTERN")
if [ -n "$HPATH" ]; then
APATH=$(find_f . "$STATICPATTERN")
if [ -n "$APATH" ]; then
cflags "-I${HPATH%/*}"
lflags "${APATH}"
status "$LIBNAME" "static"
return 0
fi
fi
return 1
}
find_library() {
LIBNAME=$1
DYNAMICLIBNAME=$2
HEADERPATTERN=$3
STATICPATTERN=$4
DYNAMICPATTERN=$5
# try static in current directory first
if find_static "$LIBNAME" "$HEADERPATTERN" "$STATICPATTERN"; then
return 0;
fi
# try shared
if find_pkgconfig "$LIBNAME"; then
return 0
fi
for i in "${DIRS[@]}"; do
DIR=($i)
HPATH=$(find_h "${DIR[0]}" "$HEADERPATTERN")
if [ -n "$HPATH" ]; then
SOPATH=$(find_f "${DIR[1]}" "$DYNAMICPATTERN")
if [ -n "$SOPATH" ]; then
cflags "-I${HPATH%/*}"
lflags "-L${SOPATH%/*} -l$DYNAMICLIBNAME"
status "$LIBNAME" "shared ... $SOPATH"
return 0
fi
fi
done
return 1
}
# returns full png.h version string
pngh_string() {
echo "$(grep -m1 "define PNG_LIBPNG_VER_STRING" "$1" | \
grep -Eo '"[^"]+"' | grep -Eo '[^"]+')"
}
# returns major minor version numbers from png.h
pngh_majmin() {
local MAJ=$(grep -m1 "define PNG_LIBPNG_VER_MAJOR" "$1" | grep -Eo "[0-9]+")
local MIN=$(grep -m1 "define PNG_LIBPNG_VER_MINOR" "$1" | grep -Eo "[0-9]+")
echo "${MAJ}${MIN}"
}
error() {
status "$1" "error ... $2"
echo
exit 1
}
echo
# basic check
if ! echo "int main(){}" | "$CC" -xc -std=c99 -o /dev/null - &> /dev/null; then
error "Compiler" "$CC failed to compile anything (make sure it's installed and supports C99)"
fi
status "Compiler" "$CC"
# init flags
CFLAGS=${CFLAGS:--O3 -fno-math-errno -funroll-loops -fomit-frame-pointer -Wall}
cflags "-std=c99 -I."
# DEBUG
if [ -z "$DEBUG" ]; then
cflags "-DNDEBUG"
status "Debug" "no"
else
cflags "-g"
status "Debug" "yes"
fi
# SSE
if [ "$SSE" = 'auto' ]; then
if [[ "$(uname -m)" =~ (amd|x86_)64 ||
"$(grep -E -m1 "^flags" /proc/cpuinfo)" =~ "sse" ]]; then
SSE=1
fi
fi
if [ "$SSE" -eq 1 ]; then
status "SSE" "yes"
cflags "-DUSE_SSE=1"
cflags "-msse"
# Silence a later ICC warning due to -msse working slightly different.
conditional_cflags "-wd10121"
# Must be set explicitly for GCC on x86_32. Other compilers imply it.
conditional_cflags "-mfpmath=sse" "-msse"
elif [ "$SSE" -eq 0 ]; then
status "SSE" "no"
cflags "-DUSE_SSE=0"
fi
# OpenMP
if [ -n "$OPENMP" ]; then
if [[ "$("$CC" -xc -E -fopenmp <(echo "#ifdef _OPENMP
#include <omp.h>
#endif") 2>&1)" =~ "omp_get_thread_num" ]]; then
cflags "-fopenmp"
lflags "-fopenmp"
status "OpenMP" "yes"
else
error "OpenMP" "not supported by compiler (please install a compiler that supports OpenMP (e.g. gcc) and specify it with the CC= argument)"
fi
else
# silence warnings about omp pragmas
cflags "-Wno-unknown-pragmas"
conditional_cflags "-wd3180" # ICC
status "OpenMP" "no"
fi
# Cocoa
if [[ "$OSTYPE" =~ "darwin" ]]; then
cflags "-mmacosx-version-min=10.6"
lflags "-mmacosx-version-min=10.6"
if [ "$COCOA_READER" != 0 ] && "$CC" 2>/dev/null 1>/dev/null -xc -E <(echo "#import <Cocoa/Cocoa.h>"); then
COCOA_READER=1
cflags "-DUSE_COCOA=1"
lflags "-framework Cocoa"
status "Cocoa" "yes"
else
status "Cocoa" "no"
fi
fi
# pairs of possible *.h and lib*.so locations
DIRS=("/usr/local/include /usr/local/lib"
"/usr/include /usr/lib"
"/opt/local/include /opt/local/lib" # macports
)
if [[ "$OSTYPE" =~ "darwin" ]]; then
SOLIBSUFFIX=dylib
# Search Developer SDK paths, since Apple seems to have dropped the standard Unixy ones
XCODE_CMD="xcode-select"
XCODE_PATH=$($XCODE_CMD -p)
DIRS+=("$XCODE_PATH/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include $XCODE_PATH/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib")
DIRS+=("$XCODE_PATH/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/usr/include $XCODE_PATH/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/usr/lib")
else
SOLIBSUFFIX=so
fi
# libpng
SUCCESS=0
# try static in current directory first
PNGH=$(find_h . "png.h")
if [ -n "$PNGH" ]; then
PNGH_STRING=$(pngh_string "$PNGH")
PNGH_MAJMIN=$(pngh_majmin "$PNGH")
if [[ -n "$PNGH_STRING" && -n "$PNGH_MAJMIN" ]]; then
LIBPNGA=$(find_f . "libpng${PNGH_MAJMIN}.a")
if [ -n "$LIBPNGA" ]; then
cflags "-I${PNGH%/*}"
lflags "${LIBPNGA}"
status "libpng" "static (${PNGH_STRING})"
SUCCESS=1
fi
fi
fi
# try shared
if [ "$SUCCESS" -eq 0 ]; then
if find_pkgconfig libpng; then
SUCCESS=1
else
for i in "${DIRS[@]}"; do
DIR=($i)
PNGH=$(find_h "${DIR[0]}" "png.h")
if [ -n "$PNGH" ]; then
PNGH_STRING=$(pngh_string "$PNGH")
PNGH_MAJMIN=$(pngh_majmin "$PNGH")
if [[ -n "$PNGH_STRING" && -n "$PNGH_MAJMIN" ]]; then
LIBPNGSO=$(find_f "${DIR[1]}" "libpng${PNGH_MAJMIN}.$SOLIBSUFFIX*")
if [ -n "$LIBPNGSO" ]; then
cflags "-I${PNGH%/*}"
lflags "-L${LIBPNGSO%/*} -lpng${PNGH_MAJMIN}"
status "libpng" "shared (${PNGH_STRING})"
SUCCESS=1
break
fi
fi
fi
done
fi
fi
if [ "$SUCCESS" -eq 0 ]; then
if [[ "$OSTYPE" =~ "darwin" ]]; then
LIBPNG_CMD='`brew install libpng`'
else
LIBPNG_CMD='`apt-get install libpng-dev` or `yum install libpng-devel`'
fi
error "libpng" "not found (try: $LIBPNG_CMD)"
fi
# zlib
if ! find_library "zlib" "z" "zlib.h" "libz.a" "libz.$SOLIBSUFFIX*"; then
error "zlib" "not found (please install zlib-devel package)"
fi
# lcms2
if [ "$LCMS2" != 0 ]; then
if find_library "lcms2" "lcms2" "lcms2.h" "liblcms2.a" "liblcms2.$SOLIBSUFFIX*"; then
cflags "-DUSE_LCMS=1"
else
if [ "$LCMS2" = 'auto' ]; then
status "lcms2" "no"
else
error "lcms2" "not found (please install libcms2-devel package)"
fi
fi
else
status "lcms2" "no"
fi
echo
# As of GCC 4.5, 387 fp math is significantly slower in C99 mode without this.
# Note: CPUs without SSE2 use 387 for doubles, even when SSE fp math is set.
conditional_cflags "-fexcess-precision=fast"
# Intel C++ Compiler
# ICC does usually only produce fast(er) code when it can optimize to the full
# capabilites of the (Intel) CPU. This is equivalent to -march=native for GCC.
conditional_cflags "-xHOST"
# Disable unsafe fp optimizations and enforce fp precision as set in the source.
conditional_cflags "-fp-model source"
# Silence a gold linker warning about string misalignment.
conditional_cflags "-falign-stack=maintain-16-byte"
lflags "-lm" # Ubuntu requires this library last, issue #38
if [ -n "$EXTRA_CFLAGS" ]; then
cflags "$EXTRA_CFLAGS"
fi
if [ -n "$EXTRA_LDFLAGS" ]; then
lflags "$EXTRA_LDFLAGS"
fi
# Overwrite previous configuration.
echo "
# auto-generated by configure
PREFIX = $PREFIX
VERSION = $VERSION
CC = $CC
CFLAGS = $CFLAGS
LDFLAGS = $LDFLAGS
COCOA_READER = $COCOA_READER
" > "$CONFIG"
# Configure static library the same way
cp "$CONFIG" lib/

View file

@ -1,36 +0,0 @@
© 1997-2002 by Greg Roelofs; based on an idea by Stefan Schneider.
© 2009-2014 by Kornel Lesiński.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
© 1989, 1991 by Jef Poskanzer.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, provided
that the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation. This software is provided "as is" without express or
implied warranty.

View file

@ -1,65 +0,0 @@
-include config.mk
STATICLIB=libimagequant.a
SHAREDLIB=libimagequant.so.0
DLL=libimagequant.dll
DLLIMP=libimagequant_dll.a
DLLDEF=libimagequant_dll.def
OBJS = pam.o mediancut.o blur.o mempool.o viter.o nearest.o libimagequant.o
SHAREDOBJS = $(subst .o,.lo,$(OBJS))
BUILD_CONFIGURATION="$(CC) $(CFLAGS) $(LDFLAGS)"
DISTFILES = $(OBJS:.o=.c) *.h MANUAL.md COPYRIGHT Makefile configure
TARNAME = libimagequant-$(VERSION)
TARFILE = $(TARNAME)-src.tar.bz2
all: static shared
static: $(STATICLIB)
shared: $(SHAREDLIB)
dll:
$(MAKE) CFLAGSADD="-DLIQ_EXPORT='__declspec(dllexport)'" $(DLL)
$(DLL) $(DLLIMP): $(OBJS)
$(CC) -fPIC -shared -o $(DLL) $^ $(LDFLAGS) -Wl,--out-implib,$(DLLIMP),--output-def,$(DLLDEF)
$(STATICLIB): $(OBJS)
$(AR) $(ARFLAGS) $@ $^
$(SHAREDOBJS):
$(CC) -fPIC $(CFLAGS) -c $(@:.lo=.c) -o $@
$(SHAREDLIB): $(SHAREDOBJS)
$(CC) -shared -o $@ $^ $(LDFLAGS)
$(OBJS): $(wildcard *.h) config.mk
dist: $(TARFILE)
$(TARFILE): $(DISTFILES)
rm -rf $(TARFILE) $(TARNAME)
mkdir $(TARNAME)
cp $(DISTFILES) $(TARNAME)
tar -cjf $(TARFILE) --numeric-owner --exclude='._*' $(TARNAME)
rm -rf $(TARNAME)
-shasum $(TARFILE)
clean:
rm -f $(OBJS) $(SHAREDOBJS) $(SHAREDLIB) $(STATICLIB) $(TARFILE) $(DLL) $(DLLIMP) $(DLLDEF)
distclean: clean
rm -f config.mk
config.mk:
ifeq ($(filter %clean %distclean, $(MAKECMDGOALS)), )
./configure
endif
.PHONY: all static shared clean dist distclean dll
.DELETE_ON_ERROR:

View file

@ -1,205 +0,0 @@
#!/usr/bin/env bash
CONFIG="config.mk"
PREFIX="/usr/local"
VERSION=$(grep LIQ_VERSION_STRING libimagequant.h | grep -Eo "2\.[0-9.]+")
DEBUG=
SSE=auto
OPENMP=
EXTRA_CFLAGS=
EXTRA_LDFLAGS=
# make gcc default compiler unless CC is already set
CC=${CC:-gcc}
help() {
printf "%4s %s\n" "" "$1"
}
for i in "$@"; do
case $i in
--help)
echo
help "--prefix= installation directory [$PREFIX]"
help "--extra-cflags= append to CFLAGS"
help "--extra-ldflags= append to LDFLAGS"
echo
help "--enable-debug"
help "--enable-sse/--disable-sse enable/disable SSE instructions"
echo
help "--with-openmp compile with multicore support"
echo
exit 0
;;
# Can be set before or after configure. Latter overrides former.
CC=*)
CC=${i#*=}
;;
CFLAGS=*)
CFLAGS=${i#*=}
;;
LDFLAGS=*)
LDFLAGS=${i#*=}
;;
--enable-debug)
DEBUG=1
;;
--enable-sse)
SSE=1
;;
--disable-sse)
SSE=0
;;
--with-openmp)
OPENMP=1
;;
--prefix=*)
PREFIX=${i#*=}
;;
# can be used multiple times or in quotes to set multiple flags
--extra-cflags=*)
EXTRA_CFLAGS="$EXTRA_CFLAGS ${i#*=}"
;;
--extra-ldflags=*)
EXTRA_LDFLAGS="$EXTRA_LDFLAGS ${i#*=}"
;;
*)
echo "error: unknown switch ${i%%=*}"
exit 1
;;
esac
done
# If someone runs sudo make install as very first command, and configure later,
# $CONFIG cannot be overwritten, and must be deleted before continuing.
if [[ -f "$CONFIG" && ! -w "$CONFIG" ]]; then
echo "Cannot overwrite file $CONFIG! Please delete it."
exit 1
fi
cflags() {
CFLAGS="$CFLAGS $1"
}
lflags() {
LDFLAGS="$LDFLAGS $1"
}
status() {
printf "%10s: %s\n" "$1" "$2"
}
# Append to CFLAGS if compiler supports flag, with optional prerequisite.
# Fails on errors and warnings.
conditional_cflags() {
if [ -z "$("$CC" -xc -S -o /dev/null $2 $1 <(echo) 2>&1)" ]; then
cflags "$1"
fi
}
error() {
status "$1" "error ... $2"
echo
exit 1
}
echo
# basic check
if ! "$CC" -xc -std=c99 <(echo "int main(){}") -o /dev/null &> /dev/null; then
error "Compiler" "$CC is no C compiler"
fi
status "Compiler" "$CC"
# init flags
CFLAGS=${CFLAGS:--O3 -fno-math-errno -funroll-loops -fomit-frame-pointer -Wall}
cflags "-std=c99 -I."
# DEBUG
if [ -z "$DEBUG" ]; then
cflags "-DNDEBUG"
status "Debug" "no"
else
cflags "-g"
status "Debug" "yes"
fi
# SSE
if [ "$SSE" = 'auto' ]; then
if [[ "$(uname -m)" =~ (amd|x86_)64 ||
"$(grep -E -m1 "^flags" /proc/cpuinfo)" =~ "sse" ]]; then
SSE=1
fi
fi
if [ "$SSE" -eq 1 ]; then
status "SSE" "yes"
cflags "-DUSE_SSE=1"
cflags "-msse"
# Silence a later ICC warning due to -msse working slightly different.
conditional_cflags "-wd10121"
# Must be set explicitly for GCC on x86_32. Other compilers imply it.
conditional_cflags "-mfpmath=sse" "-msse"
elif [ "$SSE" -eq 0 ]; then
status "SSE" "no"
cflags "-DUSE_SSE=0"
fi
# OpenMP
if [ -n "$OPENMP" ]; then
if [[ "$("$CC" -xc -E -fopenmp <(echo -e \
"#ifdef _OPENMP
#include <omp.h>
#endif") 2>&1)" =~ "omp_get_thread_num" ]]; then
cflags "-fopenmp"
lflags "-fopenmp"
status "OpenMP" "yes"
else
error "OpenMP" "not supported by compiler"
fi
else
# silence warnings about omp pragmas
cflags "-Wno-unknown-pragmas"
conditional_cflags "-wd3180" # ICC
status "OpenMP" "no"
fi
echo
# As of GCC 4.5, 387 fp math is significantly slower in C99 mode without this.
# Note: CPUs without SSE2 use 387 for doubles, even when SSE fp math is set.
conditional_cflags "-fexcess-precision=fast"
# Intel C++ Compiler
# ICC does usually only produce fast(er) code when it can optimize to the full
# capabilites of the (Intel) CPU. This is equivalent to -march=native for GCC.
conditional_cflags "-xHOST"
# Disable unsafe fp optimizations and enforce fp precision as set in the source.
conditional_cflags "-fp-model source"
# Silence a gold linker warning about string misalignment.
conditional_cflags "-falign-stack=maintain-16-byte"
lflags "-lm" # Ubuntu requires this library last, issue #38
if [ -n "$EXTRA_CFLAGS" ]; then
cflags "$EXTRA_CFLAGS"
fi
if [ -n "$EXTRA_LDFLAGS" ]; then
lflags "$EXTRA_LDFLAGS"
fi
# Overwrite previous configuration.
echo "
# auto-generated by configure
PREFIX = $PREFIX
VERSION = $VERSION
CC = $CC
CFLAGS = $CFLAGS
LDFLAGS = $LDFLAGS
" > $CONFIG

View file

@ -154,7 +154,7 @@ static void liq_verbose_printf_flush(liq_attr *attr)
} }
} }
#if USE_SSE #if __SSE__ || (_M_IX86_FP > 0)
inline static bool is_sse_available() inline static bool is_sse_available()
{ {
#if (defined(__x86_64__) || defined(__amd64)) #if (defined(__x86_64__) || defined(__amd64))
@ -416,7 +416,7 @@ static void liq_aligned_free(void *inptr)
LIQ_EXPORT liq_attr* liq_attr_create_with_allocator(void* (*custom_malloc)(size_t), void (*custom_free)(void*)) LIQ_EXPORT liq_attr* liq_attr_create_with_allocator(void* (*custom_malloc)(size_t), void (*custom_free)(void*))
{ {
#if USE_SSE #if __SSE__ || (_M_IX86_FP > 0)
if (!is_sse_available()) { if (!is_sse_available()) {
return NULL; return NULL;
} }
@ -640,7 +640,7 @@ static const rgba_pixel *liq_image_get_row_rgba(liq_image *img, unsigned int row
static void convert_row_to_f(liq_image *img, f_pixel *row_f_pixels, const unsigned int row, const float gamma_lut[]) static void convert_row_to_f(liq_image *img, f_pixel *row_f_pixels, const unsigned int row, const float gamma_lut[])
{ {
assert(row_f_pixels); assert(row_f_pixels);
assert(!USE_SSE || 0 == ((uintptr_t)row_f_pixels & 15)); assert(!(__SSE__ || (_M_IX86_FP > 0)) || 0 == ((uintptr_t)row_f_pixels & 15));
const rgba_pixel *const row_pixels = liq_image_get_row_rgba(img, row); const rgba_pixel *const row_pixels = liq_image_get_row_rgba(img, row);

View file

@ -28,15 +28,7 @@
#define MAX_DIFF 1e20 #define MAX_DIFF 1e20
#ifndef USE_SSE #if __SSE__ || (_M_IX86_FP > 0)
# if defined(__SSE__) && (defined(WIN32) || defined(__WIN32__))
# define USE_SSE 1
# else
# define USE_SSE 0
# endif
#endif
#if USE_SSE
# include <xmmintrin.h> # include <xmmintrin.h>
# ifdef _MSC_VER # ifdef _MSC_VER
# include <intrin.h> # include <intrin.h>
@ -186,7 +178,7 @@ inline static float min_colordifference(const f_pixel px, const f_pixel py)
ALWAYS_INLINE static float colordifference(f_pixel px, f_pixel py); ALWAYS_INLINE static float colordifference(f_pixel px, f_pixel py);
inline static float colordifference(f_pixel px, f_pixel py) inline static float colordifference(f_pixel px, f_pixel py)
{ {
#if USE_SSE #if __SSE__ || (_M_IX86_FP > 0)
const __m128 vpx = _mm_load_ps((const float*)&px); const __m128 vpx = _mm_load_ps((const float*)&px);
const __m128 vpy = _mm_load_ps((const float*)&py); const __m128 vpy = _mm_load_ps((const float*)&py);

View file

@ -1,129 +0,0 @@
.Dd 2013-02-25
.Dt pngquant 1
.Sh NAME
.Nm pngquant
.Nd PNG converter and lossy image compressor
.Sh SYNOPSIS
.Nm
.Aq options
.Op ncolors
.Pa file
.Op Ar
.Nm
.Aq options
.Op ncolors
.Fl
.Cm < Ns Pa file
.Cm > Ns Pa file
.Sh DESCRIPTION
.Nm
converts 32-bit RGBA PNGs to 8-bit (or smaller) RGBA-palette PNGs, optionally using Floyd-Steinberg dithering.
The output filename is the same as the input name except that it ends in
.Ql -fs8.png
or
.Ql -or8.png
(unless the input is stdin, in which case the quantized image will go to stdout).
The default behavior if the output file exists is to skip the conversion; use
.Fl Fl force
to overwrite.
.Sh OPTIONS
.Bl -tag -width -indent
.It Fl o Ar out.png , Fl Fl output Ar out.png
Writes converted file to the given path. When this option is used only single input file is allowed.
.It Fl Fl ext Ar new.png
File extension (suffix) to use for output files instead of the default
.Ql -fs8.png
or
.Ql -or8.png .
.It Fl f , Fl Fl force
Overwrite existing output files.
.Do
.Fl Fl ext
.Ar .png
.Fl Fl force
.Dc
can be used to convert files in place (which is unsafe).
.It Fl Fl nofs , Fl Fl ordered
Disable Floyd-Steinberg dithering.
.It Fl Fl floyd Op Ar =N
Set dithering level using fractional number between
.Cm 0
(none) and
.Cm 1
(full, the default).
.It Fl s Ar N , Fl Fl speed Ar N
.Cm 1
(brute-force) to
.Cm 11
(fastest). The default is
.Cm 3 .
Speed
.Cm 10
has 5% lower quality, but is about 8 times faster than the default. Speed 11 disables dithering and lowers compression level.
.It Fl Q Ar min-max , Fl Fl quality Ar min-max
.Va min
and
.Va max
are numbers in range
.Cm 0
(worst) to
.Cm 100
(perfect), similar to JPEG.
.Nm
will use the least amount of colors required to meet or exceed the
.Va max
quality. If conversion results in quality below the
.Va min
quality the image won't be saved (or if outputting to stdin, 24-bit original will be output) and pngquant will exit with status code
.Er 99 .
.It Fl Fl skip-if-larger
If conversion results in a file larger than the original the image won't be saved and pngquant will exit with status code
.Er 98 .
Additionally, file size gain must be greater than the amount of quality lost.
.It Fl Fl posterize Ar bits
Truncate number of least significant bits of color (per channel). Use this when image will be output on low-depth displays (e.g. 16-bit RGB).
.It Fl Fl iebug
Workaround for Internet Explorer 6, which only displays fully opaque pixels.
.Nm
will make almost-opaque pixels fully opaque and will reduce amount of semi-transparent colors. When this option is enabled the default filename suffix is
.Ql -ie-fs8.png
/
.Ql -ie-or8.png .
.It Fl Fl transbug
Workaround for readers that expect fully transparent color to be the last entry in the palette.
.It Fl v , Fl Fl verbose
Enable verbose messages showing progress and information about input/output. Opposite is
.Fl Fl quiet .
Errors are output to
.Pa stderr
regardless of this option.
.It Fl V , Fl Fl version
Display version on
.Pa stdout
and exit.
.It Fl h , Fl Fl help
Display help and exit.
.El
.Sh EXAMPLE
Creating a new image with the number of colors reduced to 64:
.Bd -ragged -offset indent
.Nm
.Cm 64 image.png
.Ed
.Pp
The resulting image will have 64 colors and will be saved as
.Pa image-fs8.png .
.Pp
Overwriting image in-place if it can be reduced without too much quality loss:
.Bd -ragged -offset indent
.Nm
.Cm -f --ext .png --quality 70-95 image.png
.Ed
.Sh AUTHOR
.Nm
is developed by Kornel Lesinski
.Aq Mt kornel@pngquant.org
based on code by Greg Roelofs
.Aq Mt newt@pobox.com .
.Pp
.Lk http://pngquant.org "pngquant homepage" .

View file

@ -1,864 +0,0 @@
/* pngquant.c - quantize the colors in an alphamap down to a specified number
**
** Copyright (C) 1989, 1991 by Jef Poskanzer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation. This software is provided "as is" without express or
** implied warranty.
**
** - - - -
**
** © 1997-2002 by Greg Roelofs; based on an idea by Stefan Schneider.
** © 2009-2014 by Kornel Lesiński.
**
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification,
** are permitted provided that the following conditions are met:
**
** 1. Redistributions of source code must retain the above copyright notice,
** this list of conditions and the following disclaimer.
**
** 2. Redistributions in binary form must reproduce the above copyright notice,
** this list of conditions and the following disclaimer in the documentation
** and/or other materials provided with the distribution.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
*/
#define PNGQUANT_VERSION LIQ_VERSION_STRING " (April 2015)"
#define PNGQUANT_USAGE "\
usage: pngquant [options] [ncolors] -- pngfile [pngfile ...]\n\
pngquant [options] [ncolors] - >stdout <stdin\n\n\
options:\n\
--force overwrite existing output files (synonym: -f)\n\
--skip-if-larger only save converted files if they're smaller than original\n\
--output file output path, only if one input file is specified (synonym: -o)\n\
--ext new.png set custom suffix/extension for output filenames\n\
--quality min-max don't save below min, use fewer colors below max (0-100)\n\
--speed N speed/quality trade-off. 1=slow, 3=default, 11=fast & rough\n\
--nofs disable Floyd-Steinberg dithering\n\
--posterize N output lower resolution color (e.g. for ARGB4444 output)\n\
--verbose print status messages (synonym: -v)\n\
\n\
Quantizes one or more 32-bit RGBA PNGs to 8-bit (or smaller) RGBA-palette\n\
PNGs using Floyd-Steinberg diffusion dithering (unless disabled).\n\
The output filename is the same as the input name except that\n\
it ends in \"-fs8.png\", \"-or8.png\" or your custom extension (unless the\n\
input is stdin, in which case the quantized image will go to stdout).\n\
The default behavior if the output file exists is to skip the conversion;\n\
use --force to overwrite. See man page for full list of options.\n"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdbool.h>
#include <getopt.h>
#include <unistd.h>
extern char *optarg;
extern int optind, opterr;
#if defined(WIN32) || defined(__WIN32__)
# include <fcntl.h> /* O_BINARY */
# include <io.h> /* setmode() */
#endif
#ifdef _OPENMP
#include <omp.h>
#else
#define omp_get_max_threads() 1
#define omp_get_thread_num() 0
#endif
#include "rwpng.h" /* typedefs, common macros, public prototypes */
#include "lib/libimagequant.h"
struct pngquant_options {
liq_attr *liq;
liq_image *fixed_palette_image;
liq_log_callback_function *log_callback;
void *log_callback_user_info;
float floyd;
bool using_stdin, using_stdout, force, fast_compression, ie_mode,
min_quality_limit, skip_if_larger,
verbose;
};
static pngquant_error prepare_output_image(liq_result *result, liq_image *input_image, png8_image *output_image);
static void set_palette(liq_result *result, png8_image *output_image);
static pngquant_error read_image(liq_attr *options, const char *filename, int using_stdin, png24_image *input_image_p, liq_image **liq_image_p, bool keep_input_pixels, bool verbose);
static pngquant_error write_image(png8_image *output_image, png24_image *output_image24, const char *outname, struct pngquant_options *options);
static char *add_filename_extension(const char *filename, const char *newext);
static bool file_exists(const char *outname);
static void verbose_printf(struct pngquant_options *context, const char *fmt, ...)
{
if (context->log_callback) {
va_list va;
va_start(va, fmt);
int required_space = vsnprintf(NULL, 0, fmt, va)+1; // +\0
va_end(va);
char buf[required_space];
va_start(va, fmt);
vsnprintf(buf, required_space, fmt, va);
va_end(va);
context->log_callback(context->liq, buf, context->log_callback_user_info);
}
}
static void log_callback(const liq_attr *attr, const char *msg, void* user_info)
{
fprintf(stderr, "%s\n", msg);
}
#ifdef _OPENMP
#define LOG_BUFFER_SIZE 1300
struct buffered_log {
int buf_used;
char buf[LOG_BUFFER_SIZE];
};
static void log_callback_buferred_flush(const liq_attr *attr, void *context)
{
struct buffered_log *log = context;
if (log->buf_used) {
fwrite(log->buf, 1, log->buf_used, stderr);
fflush(stderr);
log->buf_used = 0;
}
}
static void log_callback_buferred(const liq_attr *attr, const char *msg, void* context)
{
struct buffered_log *log = context;
int len = strlen(msg);
if (len > LOG_BUFFER_SIZE-2) len = LOG_BUFFER_SIZE-2;
if (len > LOG_BUFFER_SIZE - log->buf_used - 2) log_callback_buferred_flush(attr, log);
memcpy(&log->buf[log->buf_used], msg, len);
log->buf_used += len+1;
log->buf[log->buf_used-1] = '\n';
log->buf[log->buf_used] = '\0';
}
#endif
static void print_full_version(FILE *fd)
{
fprintf(fd, "pngquant, %s, by Greg Roelofs, Kornel Lesinski.\n"
#ifndef NDEBUG
" DEBUG (slow) version.\n" /* NDEBUG disables assert() */
#endif
#if USE_SSE
" Compiled with SSE instructions.\n"
#endif
#if _OPENMP
" Compiled with OpenMP (multicore support).\n"
#endif
, PNGQUANT_VERSION);
rwpng_version_info(fd);
fputs("\n", fd);
}
static void print_usage(FILE *fd)
{
fputs(PNGQUANT_USAGE, fd);
}
/**
* N = automatic quality, uses limit unless force is set (N-N or 0-N)
* -N = no better than N (same as 0-N)
* N-M = no worse than N, no better than M
* N- = no worse than N, perfect if possible (same as N-100)
*
* where N,M are numbers between 0 (lousy) and 100 (perfect)
*/
static bool parse_quality(const char *quality, liq_attr *options, bool *min_quality_limit)
{
long limit, target;
const char *str = quality; char *end;
long t1 = strtol(str, &end, 10);
if (str == end) return false;
str = end;
if ('\0' == end[0] && t1 < 0) { // quality="-%d"
target = -t1;
limit = 0;
} else if ('\0' == end[0]) { // quality="%d"
target = t1;
limit = t1*9/10;
} else if ('-' == end[0] && '\0' == end[1]) { // quality="%d-"
target = 100;
limit = t1;
} else { // quality="%d-%d"
long t2 = strtol(str, &end, 10);
if (str == end || t2 > 0) return false;
target = -t2;
limit = t1;
}
*min_quality_limit = (limit > 0);
return LIQ_OK == liq_set_quality(options, limit, target);
}
static const struct {const char *old; const char *newopt;} obsolete_options[] = {
{"-fs","--floyd=1"},
{"-nofs", "--ordered"},
{"-floyd", "--floyd=1"},
{"-nofloyd", "--ordered"},
{"-ordered", "--ordered"},
{"-force", "--force"},
{"-noforce", "--no-force"},
{"-verbose", "--verbose"},
{"-quiet", "--quiet"},
{"-noverbose", "--quiet"},
{"-noquiet", "--verbose"},
{"-help", "--help"},
{"-version", "--version"},
{"-ext", "--ext"},
{"-speed", "--speed"},
};
static void fix_obsolete_options(const unsigned int argc, char *argv[])
{
for(unsigned int argn=1; argn < argc; argn++) {
if ('-' != argv[argn][0]) continue;
if ('-' == argv[argn][1]) break; // stop on first --option or --
for(unsigned int i=0; i < sizeof(obsolete_options)/sizeof(obsolete_options[0]); i++) {
if (0 == strcmp(obsolete_options[i].old, argv[argn])) {
fprintf(stderr, " warning: option '%s' has been replaced with '%s'.\n", obsolete_options[i].old, obsolete_options[i].newopt);
argv[argn] = (char*)obsolete_options[i].newopt;
}
}
}
}
enum {arg_floyd=1, arg_ordered, arg_ext, arg_no_force, arg_iebug,
arg_transbug, arg_map, arg_posterize, arg_skip_larger};
static const struct option long_options[] = {
{"verbose", no_argument, NULL, 'v'},
{"quiet", no_argument, NULL, 'q'},
{"force", no_argument, NULL, 'f'},
{"no-force", no_argument, NULL, arg_no_force},
{"floyd", optional_argument, NULL, arg_floyd},
{"ordered", no_argument, NULL, arg_ordered},
{"nofs", no_argument, NULL, arg_ordered},
{"iebug", no_argument, NULL, arg_iebug},
{"transbug", no_argument, NULL, arg_transbug},
{"ext", required_argument, NULL, arg_ext},
{"skip-if-larger", no_argument, NULL, arg_skip_larger},
{"output", required_argument, NULL, 'o'},
{"speed", required_argument, NULL, 's'},
{"quality", required_argument, NULL, 'Q'},
{"posterize", required_argument, NULL, arg_posterize},
{"map", required_argument, NULL, arg_map},
{"version", no_argument, NULL, 'V'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0},
};
pngquant_error pngquant_file(const char *filename, const char *outname, struct pngquant_options *options);
int main(int argc, char *argv[])
{
struct pngquant_options options = {
.floyd = 1.f, // floyd-steinberg dithering
};
options.liq = liq_attr_create();
if (!options.liq) {
fputs("SSE-capable CPU is required for this build.\n", stderr);
return WRONG_ARCHITECTURE;
}
unsigned int error_count=0, skipped_count=0, file_count=0;
pngquant_error latest_error=SUCCESS;
const char *newext = NULL, *output_file_path = NULL;
fix_obsolete_options(argc, argv);
int opt;
do {
opt = getopt_long(argc, argv, "Vvqfhs:Q:o:", long_options, NULL);
switch (opt) {
case 'v':
options.verbose = true;
break;
case 'q':
options.verbose = false;
break;
case arg_floyd:
options.floyd = optarg ? atof(optarg) : 1.0;
if (options.floyd < 0 || options.floyd > 1.f) {
fputs("--floyd argument must be in 0..1 range\n", stderr);
return INVALID_ARGUMENT;
}
break;
case arg_ordered: options.floyd = 0; break;
case 'f': options.force = true; break;
case arg_no_force: options.force = false; break;
case arg_ext: newext = optarg; break;
case 'o':
if (output_file_path) {
fputs("--output option can be used only once\n", stderr);
return INVALID_ARGUMENT;
}
output_file_path = optarg; break;
case arg_iebug:
// opacities above 238 will be rounded up to 255, because IE6 truncates <255 to 0.
liq_set_min_opacity(options.liq, 238);
options.ie_mode = true;
break;
case arg_transbug:
liq_set_last_index_transparent(options.liq, true);
break;
case arg_skip_larger:
options.skip_if_larger = true;
break;
case 's':
{
int speed = atoi(optarg);
if (speed >= 10) {
options.fast_compression = true;
}
if (speed == 11) {
options.floyd = 0;
speed = 10;
}
if (LIQ_OK != liq_set_speed(options.liq, speed)) {
fputs("Speed should be between 1 (slow) and 11 (fast).\n", stderr);
return INVALID_ARGUMENT;
}
}
break;
case 'Q':
if (!parse_quality(optarg, options.liq, &options.min_quality_limit)) {
fputs("Quality should be in format min-max where min and max are numbers in range 0-100.\n", stderr);
return INVALID_ARGUMENT;
}
break;
case arg_posterize:
if (LIQ_OK != liq_set_min_posterization(options.liq, atoi(optarg))) {
fputs("Posterization should be number of bits in range 0-4.\n", stderr);
return INVALID_ARGUMENT;
}
break;
case arg_map:
{
png24_image tmp = {};
if (SUCCESS != read_image(options.liq, optarg, false, &tmp, &options.fixed_palette_image, false, false)) {
fprintf(stderr, " error: unable to load %s", optarg);
return INVALID_ARGUMENT;
}
}
break;
case 'h':
print_full_version(stdout);
print_usage(stdout);
return SUCCESS;
case 'V':
puts(PNGQUANT_VERSION);
return SUCCESS;
case -1: break;
default:
return INVALID_ARGUMENT;
}
} while (opt != -1);
int argn = optind;
if (argn >= argc) {
if (argn > 1) {
fputs("No input files specified.\n", stderr);
} else {
print_full_version(stderr);
}
print_usage(stderr);
return MISSING_ARGUMENT;
}
if (options.verbose) {
liq_set_log_callback(options.liq, log_callback, NULL);
options.log_callback = log_callback;
}
char *colors_end;
unsigned long colors = strtoul(argv[argn], &colors_end, 10);
if (colors_end != argv[argn] && '\0' == colors_end[0]) {
if (LIQ_OK != liq_set_max_colors(options.liq, colors)) {
fputs("Number of colors must be between 2 and 256.\n", stderr);
return INVALID_ARGUMENT;
}
argn++;
}
if (newext && output_file_path) {
fputs("--ext and --output options can't be used at the same time\n", stderr);
return INVALID_ARGUMENT;
}
// new filename extension depends on options used. Typically basename-fs8.png
if (newext == NULL) {
newext = options.floyd > 0 ? "-ie-fs8.png" : "-ie-or8.png";
if (!options.ie_mode) {
newext += 3; /* skip "-ie" */
}
}
if (argn == argc || (argn == argc-1 && 0==strcmp(argv[argn],"-"))) {
options.using_stdin = true;
options.using_stdout = !output_file_path;
argn = argc-1;
}
const int num_files = argc-argn;
if (output_file_path && num_files != 1) {
fputs("Only one input file is allowed when --output is used\n", stderr);
return INVALID_ARGUMENT;
}
#ifdef _OPENMP
// if there's a lot of files, coarse parallelism can be used
if (num_files > 2*omp_get_max_threads()) {
omp_set_nested(0);
omp_set_dynamic(1);
} else {
omp_set_nested(1);
}
#endif
#pragma omp parallel for \
schedule(static, 1) reduction(+:skipped_count) reduction(+:error_count) reduction(+:file_count) shared(latest_error)
for(int i=0; i < num_files; i++) {
struct pngquant_options opts = options;
opts.liq = liq_attr_copy(options.liq);
const char *filename = opts.using_stdin ? "stdin" : argv[argn+i];
#ifdef _OPENMP
struct buffered_log buf = {};
if (opts.log_callback && omp_get_num_threads() > 1 && num_files > 1) {
liq_set_log_callback(opts.liq, log_callback_buferred, &buf);
liq_set_log_flush_callback(opts.liq, log_callback_buferred_flush, &buf);
options.log_callback = log_callback_buferred;
options.log_callback_user_info = &buf;
}
#endif
pngquant_error retval = SUCCESS;
const char *outname = output_file_path;
char *outname_free = NULL;
if (!options.using_stdout) {
if (!outname) {
outname = outname_free = add_filename_extension(filename, newext);
}
if (!options.force && file_exists(outname)) {
fprintf(stderr, " error: '%s' exists; not overwriting\n", outname);
retval = NOT_OVERWRITING_ERROR;
}
}
if (SUCCESS == retval) {
retval = pngquant_file(filename, outname, &opts);
}
free(outname_free);
liq_attr_destroy(opts.liq);
if (retval) {
#pragma omp critical
{
latest_error = retval;
}
if (retval == TOO_LOW_QUALITY || retval == TOO_LARGE_FILE) {
skipped_count++;
} else {
error_count++;
}
}
++file_count;
}
if (error_count) {
verbose_printf(&options, "There were errors quantizing %d file%s out of a total of %d file%s.",
error_count, (error_count == 1)? "" : "s", file_count, (file_count == 1)? "" : "s");
}
if (skipped_count) {
verbose_printf(&options, "Skipped %d file%s out of a total of %d file%s.",
skipped_count, (skipped_count == 1)? "" : "s", file_count, (file_count == 1)? "" : "s");
}
if (!skipped_count && !error_count) {
verbose_printf(&options, "No errors detected while quantizing %d image%s.",
file_count, (file_count == 1)? "" : "s");
}
liq_image_destroy(options.fixed_palette_image);
liq_attr_destroy(options.liq);
return latest_error;
}
pngquant_error pngquant_file(const char *filename, const char *outname, struct pngquant_options *options)
{
pngquant_error retval = SUCCESS;
verbose_printf(options, "%s:", filename);
liq_image *input_image = NULL;
png24_image input_image_rwpng = {};
bool keep_input_pixels = options->skip_if_larger || (options->using_stdout && options->min_quality_limit); // original may need to be output to stdout
if (SUCCESS == retval) {
retval = read_image(options->liq, filename, options->using_stdin, &input_image_rwpng, &input_image, keep_input_pixels, options->verbose);
}
int quality_percent = 90; // quality on 0-100 scale, updated upon successful remap
png8_image output_image = {};
if (SUCCESS == retval) {
verbose_printf(options, " read %luKB file", (input_image_rwpng.file_size+1023UL)/1024UL);
#if USE_LCMS
if (input_image_rwpng.lcms_status == ICCP) {
verbose_printf(options, " used embedded ICC profile to transform image to sRGB colorspace");
} else if (input_image_rwpng.lcms_status == GAMA_CHRM) {
verbose_printf(options, " used gAMA and cHRM chunks to transform image to sRGB colorspace");
} else if (input_image_rwpng.lcms_status == ICCP_WARN_GRAY) {
verbose_printf(options, " warning: ignored ICC profile in GRAY colorspace");
}
#endif
if (input_image_rwpng.gamma != 0.45455) {
verbose_printf(options, " corrected image from gamma %2.1f to sRGB gamma",
1.0/input_image_rwpng.gamma);
}
// when using image as source of a fixed palette the palette is extracted using regular quantization
liq_result *remap = liq_quantize_image(options->liq, options->fixed_palette_image ? options->fixed_palette_image : input_image);
if (remap) {
liq_set_output_gamma(remap, 0.45455); // fixed gamma ~2.2 for the web. PNG can't store exact 1/2.2
liq_set_dithering_level(remap, options->floyd);
retval = prepare_output_image(remap, input_image, &output_image);
if (SUCCESS == retval) {
if (LIQ_OK != liq_write_remapped_image_rows(remap, input_image, output_image.row_pointers)) {
retval = OUT_OF_MEMORY_ERROR;
}
set_palette(remap, &output_image);
double palette_error = liq_get_quantization_error(remap);
if (palette_error >= 0) {
quality_percent = liq_get_quantization_quality(remap);
verbose_printf(options, " mapped image to new colors...MSE=%.3f (Q=%d)", palette_error, quality_percent);
}
}
liq_result_destroy(remap);
} else {
retval = TOO_LOW_QUALITY;
}
}
if (SUCCESS == retval) {
if (options->skip_if_larger) {
// this is very rough approximation, but generally avoid losing more quality than is gained in file size.
// Quality is squared, because even greater savings are needed to justify big quality loss.
double quality = quality_percent/100.0;
output_image.maximum_file_size = (input_image_rwpng.file_size-1) * quality*quality;
}
output_image.fast_compression = options->fast_compression;
output_image.chunks = input_image_rwpng.chunks; input_image_rwpng.chunks = NULL;
retval = write_image(&output_image, NULL, outname, options);
if (TOO_LARGE_FILE == retval) {
verbose_printf(options, " file exceeded expected size of %luKB", (unsigned long)output_image.maximum_file_size/1024UL);
}
}
if (options->using_stdout && keep_input_pixels && (TOO_LARGE_FILE == retval || TOO_LOW_QUALITY == retval)) {
// when outputting to stdout it'd be nasty to create 0-byte file
// so if quality is too low, output 24-bit original
pngquant_error write_retval = write_image(NULL, &input_image_rwpng, outname, options);
if (write_retval) {
retval = write_retval;
}
}
liq_image_destroy(input_image);
rwpng_free_image24(&input_image_rwpng);
rwpng_free_image8(&output_image);
return retval;
}
static void set_palette(liq_result *result, png8_image *output_image)
{
const liq_palette *palette = liq_get_palette(result);
// tRNS, etc.
output_image->num_palette = palette->count;
output_image->num_trans = 0;
for(unsigned int i=0; i < palette->count; i++) {
liq_color px = palette->entries[i];
if (px.a < 255) {
output_image->num_trans = i+1;
}
output_image->palette[i] = (png_color){.red=px.r, .green=px.g, .blue=px.b};
output_image->trans[i] = px.a;
}
}
static bool file_exists(const char *outname)
{
FILE *outfile = fopen(outname, "rb");
if ((outfile ) != NULL) {
fclose(outfile);
return true;
}
return false;
}
/* build the output filename from the input name by inserting "-fs8" or
* "-or8" before the ".png" extension (or by appending that plus ".png" if
* there isn't any extension), then make sure it doesn't exist already */
static char *add_filename_extension(const char *filename, const char *newext)
{
size_t x = strlen(filename);
char* outname = malloc(x+4+strlen(newext)+1);
if (!outname) return NULL;
strncpy(outname, filename, x);
if (strncmp(outname+x-4, ".png", 4) == 0 || strncmp(outname+x-4, ".PNG", 4) == 0) {
strcpy(outname+x-4, newext);
} else {
strcpy(outname+x, newext);
}
return outname;
}
static char *temp_filename(const char *basename) {
size_t x = strlen(basename);
char *outname = malloc(x+1+4);
if (!outname) return NULL;
strcpy(outname, basename);
strcpy(outname+x, ".tmp");
return outname;
}
static void set_binary_mode(FILE *fp)
{
#if defined(WIN32) || defined(__WIN32__)
setmode(fp == stdout ? 1 : 0, O_BINARY);
#endif
}
static const char *filename_part(const char *path)
{
const char *outfilename = strrchr(path, '/');
if (outfilename) {
return outfilename+1;
} else {
return path;
}
}
static bool replace_file(const char *from, const char *to, const bool force) {
#if defined(WIN32) || defined(__WIN32__)
if (force) {
// On Windows rename doesn't replace
unlink(to);
}
#endif
return (0 == rename(from, to));
}
static pngquant_error write_image(png8_image *output_image, png24_image *output_image24, const char *outname, struct pngquant_options *options)
{
FILE *outfile;
char *tempname = NULL;
if (options->using_stdout) {
set_binary_mode(stdout);
outfile = stdout;
if (output_image) {
verbose_printf(options, " writing %d-color image to stdout", output_image->num_palette);
} else {
verbose_printf(options, " writing truecolor image to stdout");
}
} else {
tempname = temp_filename(outname);
if (!tempname) return OUT_OF_MEMORY_ERROR;
if ((outfile = fopen(tempname, "wb")) == NULL) {
fprintf(stderr, " error: cannot open '%s' for writing\n", tempname);
free(tempname);
return CANT_WRITE_ERROR;
}
if (output_image) {
verbose_printf(options, " writing %d-color image as %s", output_image->num_palette, filename_part(outname));
} else {
verbose_printf(options, " writing truecolor image as %s", filename_part(outname));
}
}
pngquant_error retval;
#pragma omp critical (libpng)
{
if (output_image) {
retval = rwpng_write_image8(outfile, output_image);
} else {
retval = rwpng_write_image24(outfile, output_image24);
}
}
if (!options->using_stdout) {
fclose(outfile);
if (SUCCESS == retval) {
// Image has been written to a temporary file and then moved over destination.
// This makes replacement atomic and avoids damaging destination file on write error.
if (!replace_file(tempname, outname, options->force)) {
retval = CANT_WRITE_ERROR;
}
}
if (retval) {
unlink(tempname);
}
free(tempname);
}
if (retval && retval != TOO_LARGE_FILE) {
fprintf(stderr, " error: failed writing image to %s\n", outname);
}
return retval;
}
static pngquant_error read_image(liq_attr *options, const char *filename, int using_stdin, png24_image *input_image_p, liq_image **liq_image_p, bool keep_input_pixels, bool verbose)
{
FILE *infile;
if (using_stdin) {
set_binary_mode(stdin);
infile = stdin;
} else if ((infile = fopen(filename, "rb")) == NULL) {
fprintf(stderr, " error: cannot open %s for reading\n", filename);
return READ_ERROR;
}
pngquant_error retval;
#pragma omp critical (libpng)
{
retval = rwpng_read_image24(infile, input_image_p, verbose);
}
if (!using_stdin) {
fclose(infile);
}
if (retval) {
fprintf(stderr, " error: cannot decode image %s\n", using_stdin ? "from stdin" : filename_part(filename));
return retval;
}
*liq_image_p = liq_image_create_rgba_rows(options, (void**)input_image_p->row_pointers, input_image_p->width, input_image_p->height, input_image_p->gamma);
if (!*liq_image_p) {
return OUT_OF_MEMORY_ERROR;
}
if (!keep_input_pixels) {
if (LIQ_OK != liq_image_set_memory_ownership(*liq_image_p, LIQ_OWN_ROWS | LIQ_OWN_PIXELS)) {
return OUT_OF_MEMORY_ERROR;
}
input_image_p->row_pointers = NULL;
input_image_p->rgba_data = NULL;
}
return SUCCESS;
}
static pngquant_error prepare_output_image(liq_result *result, liq_image *input_image, png8_image *output_image)
{
output_image->width = liq_image_get_width(input_image);
output_image->height = liq_image_get_height(input_image);
output_image->gamma = liq_get_output_gamma(result);
/*
** Step 3.7 [GRR]: allocate memory for the entire indexed image
*/
output_image->indexed_data = malloc(output_image->height * output_image->width);
output_image->row_pointers = malloc(output_image->height * sizeof(output_image->row_pointers[0]));
if (!output_image->indexed_data || !output_image->row_pointers) {
return OUT_OF_MEMORY_ERROR;
}
for(unsigned int row = 0; row < output_image->height; ++row) {
output_image->row_pointers[row] = output_image->indexed_data + row*output_image->width;
}
const liq_palette *palette = liq_get_palette(result);
// tRNS, etc.
output_image->num_palette = palette->count;
output_image->num_trans = 0;
for(unsigned int i=0; i < palette->count; i++) {
if (palette->entries[i].a < 255) {
output_image->num_trans = i+1;
}
}
return SUCCESS;
}

View file

@ -1,62 +0,0 @@
Name: pngquant
Version: 2.4.1
Release: 1%{?dist}
Summary: PNG quantization tool for reducing image file size
License: BSD
URL: http://pngquant.org
Source0: https://github.com/pornel/pngquant/archive/%{version}.tar.gz
BuildRequires: libpng-devel%{?_isa} >= 1.2.46-1
BuildRequires: zlib-devel%{?_isa} >= 1.2.3-1
BuildRequires: gcc%{?_isa} >= 4.2-1
Requires: libpng%{?_isa} >= 1.2.46-1
Requires: zlib%{?isa} >= 1.2.3-1
%description
pngquant converts 24/32-bit RGBA PNG images to 8-bit palette with
alpha channel preserved. Such images are compatible with all modern web
browsers and a compatibility setting is available to help transparency
degrade well in Internet Explorer 6. Quantized files are often 40-70
percent smaller than their 24/32-bit version. pngquant uses the
median cut algorithm.
%prep
%setup -q -n pngquant-%{version}
%build
./configure --prefix=%{_prefix}
make %{?_smp_mflags}
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}/%{_bindir}
make install PREFIX=%{_prefix} DESTDIR=%{buildroot}
install -Dpm0755 pngquant %{buildroot}/%{_bindir}/pngquant
install -Dpm0644 pngquant.1 %{buildroot}/%{_mandir}/man1/pngquant.1
%files
%defattr(-,root,root,-)
%doc README.md CHANGELOG COPYRIGHT
%{_bindir}/pngquant
%{_mandir}/man1/pngquant.1*
%changelog
* Fri Sep 12 2014 Michael Dec <grepwood@sucs.org> 1.8.3-1
- Update to latest upstream version and corrected the .spec
* Thu May 03 2012 Craig Barnes <cr@igbarn.es> - 1.7.2-1
- Update to latest upstream version
* Sun Jan 15 2012 Craig Barnes <cr@igbarn.es> - 1.7.0-1
- Update to latest upstream version
* Mon Jan 09 2012 Craig Barnes <cr@igbarn.es> - 1.6.4-1
- Update to latest version
- Remove Makefile patch (merged upstream)
- Use prefix macro when installing (upstream changed the default prefix)
* Wed Dec 28 2011 Craig Barnes <cr@igbarn.es> - 1.6.2-1
- Initial package

615
deps/pngquant/rwpng.c vendored
View file

@ -1,615 +0,0 @@
/*---------------------------------------------------------------------------
pngquant: RGBA -> RGBA-palette quantization program rwpng.c
---------------------------------------------------------------------------
© 1998-2000 by Greg Roelofs.
© 2009-2014 by Kornel Lesiński.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "png.h"
#include "rwpng.h"
#if USE_LCMS
#include "lcms2.h"
#endif
#ifndef Z_BEST_COMPRESSION
#define Z_BEST_COMPRESSION 9
#endif
#ifndef Z_BEST_SPEED
#define Z_BEST_SPEED 1
#endif
#ifdef _OPENMP
#include <omp.h>
#else
#define omp_get_max_threads() 1
#endif
#if PNG_LIBPNG_VER < 10500
typedef png_const_charp png_const_bytep;
#endif
static void rwpng_error_handler(png_structp png_ptr, png_const_charp msg);
static void rwpng_warning_stderr_handler(png_structp png_ptr, png_const_charp msg);
static void rwpng_warning_silent_handler(png_structp png_ptr, png_const_charp msg);
int rwpng_read_image24_cocoa(FILE *infile, png24_image *mainprog_ptr);
void rwpng_version_info(FILE *fp)
{
const char *pngver = png_get_header_ver(NULL);
#if USE_COCOA
fprintf(fp, " Using Apple Cocoa image reader and libpng %s.\n", pngver);
#elif USE_LCMS
fprintf(fp, " Using libpng %s with Little CMS color profile support.\n", pngver);
#else
fprintf(fp, " Using libpng %s.\n", pngver);
#endif
#if PNG_LIBPNG_VER < 10600
if (strcmp(pngver, "1.3.") < 0) {
fputs("\nWARNING: Your version of libpng is outdated and may produce corrupted files.\n"
"Please recompile pngquant with the current version of libpng (1.6 or later).\n", fp);
} else if (strcmp(pngver, "1.6.") < 0) {
#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
fputs("\nWARNING: Your version of libpng is old and has buggy support for custom chunks.\n"
"Please recompile pngquant with the current version of libpng (1.6 or later).\n", fp);
#endif
}
#endif
}
struct rwpng_read_data {
FILE *const fp;
png_size_t bytes_read;
};
static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
{
struct rwpng_read_data *read_data = (struct rwpng_read_data *)png_get_io_ptr(png_ptr);
png_size_t read = fread(data, 1, length, read_data->fp);
if (!read) {
png_error(png_ptr, "Read error");
}
read_data->bytes_read += read;
}
struct rwpng_write_state {
FILE *outfile;
png_size_t maximum_file_size;
png_size_t bytes_written;
pngquant_error retval;
};
static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
{
struct rwpng_write_state *write_state = (struct rwpng_write_state *)png_get_io_ptr(png_ptr);
if (SUCCESS != write_state->retval) {
return;
}
if (!fwrite(data, length, 1, write_state->outfile)) {
write_state->retval = CANT_WRITE_ERROR;
}
write_state->bytes_written += length;
}
static void user_flush_data(png_structp png_ptr)
{
// libpng never calls this :(
}
static png_bytepp rwpng_create_row_pointers(png_infop info_ptr, png_structp png_ptr, unsigned char *base, unsigned int height, unsigned int rowbytes)
{
if (!rowbytes) {
rowbytes = png_get_rowbytes(png_ptr, info_ptr);
}
png_bytepp row_pointers = malloc(height * sizeof(row_pointers[0]));
if (!row_pointers) return NULL;
for(unsigned int row = 0; row < height; ++row) {
row_pointers[row] = base + row * rowbytes;
}
return row_pointers;
}
static int read_chunk_callback(png_structp png_ptr, png_unknown_chunkp in_chunk)
{
if (0 == memcmp("iCCP", in_chunk->name, 5) ||
0 == memcmp("cHRM", in_chunk->name, 5) ||
0 == memcmp("gAMA", in_chunk->name, 5)) {
return 0; // not handled
}
struct rwpng_chunk **head = (struct rwpng_chunk **)png_get_user_chunk_ptr(png_ptr);
struct rwpng_chunk *chunk = malloc(sizeof(struct rwpng_chunk));
memcpy(chunk->name, in_chunk->name, 5);
chunk->size = in_chunk->size;
chunk->location = in_chunk->location;
chunk->data = in_chunk->size ? malloc(in_chunk->size) : NULL;
if (in_chunk->size) {
memcpy(chunk->data, in_chunk->data, in_chunk->size);
}
chunk->next = *head;
*head = chunk;
return 1; // marks as "handled", libpng won't store it
}
/*
retval:
0 = success
21 = bad sig
22 = bad IHDR
24 = insufficient memory
25 = libpng error (via longjmp())
26 = wrong PNG color type (no alpha channel)
*/
pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr, int verbose)
{
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
png_size_t rowbytes;
int color_type, bit_depth;
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr,
rwpng_error_handler, verbose ? rwpng_warning_stderr_handler : rwpng_warning_silent_handler);
if (!png_ptr) {
return PNG_OUT_OF_MEMORY_ERROR; /* out of memory */
}
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_read_struct(&png_ptr, NULL, NULL);
return PNG_OUT_OF_MEMORY_ERROR; /* out of memory */
}
/* setjmp() must be called in every function that calls a non-trivial
* libpng function */
if (setjmp(mainprog_ptr->jmpbuf)) {
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return LIBPNG_FATAL_ERROR; /* fatal libpng error (via longjmp()) */
}
#if PNG_LIBPNG_VER >= 10500 && defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
/* copy standard chunks too */
png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_IF_SAFE, (png_const_bytep)"pHYs\0iTXt\0tEXt\0zTXt", 4);
#endif
png_set_read_user_chunk_fn(png_ptr, &mainprog_ptr->chunks, read_chunk_callback);
struct rwpng_read_data read_data = {infile, 0};
png_set_read_fn(png_ptr, &read_data, user_read_data);
png_read_info(png_ptr, info_ptr); /* read all PNG info up to image data */
/* alternatively, could make separate calls to png_get_image_width(),
* etc., but want bit_depth and color_type for later [don't care about
* compression_type and filter_type => NULLs] */
png_get_IHDR(png_ptr, info_ptr, &mainprog_ptr->width, &mainprog_ptr->height,
&bit_depth, &color_type, NULL, NULL, NULL);
/* expand palette images to RGB, low-bit-depth grayscale images to 8 bits,
* transparency chunks to full alpha channel; strip 16-bit-per-sample
* images to 8 bits per sample; and convert grayscale to RGB[A] */
/* GRR TO DO: preserve all safe-to-copy ancillary PNG chunks */
if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
#ifdef PNG_READ_FILLER_SUPPORTED
png_set_expand(png_ptr);
png_set_filler(png_ptr, 65535L, PNG_FILLER_AFTER);
#else
fprintf(stderr, "pngquant readpng: image is neither RGBA nor GA\n");
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
mainprog_ptr->retval = WRONG_INPUT_COLOR_TYPE;
return mainprog_ptr->retval;
#endif
}
if (bit_depth == 16) {
png_set_strip_16(png_ptr);
}
if (!(color_type & PNG_COLOR_MASK_COLOR)) {
png_set_gray_to_rgb(png_ptr);
}
/* get source gamma for gamma correction, or use sRGB default */
double gamma = 0.45455;
if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
png_get_gAMA(png_ptr, info_ptr, &gamma);
if (gamma < 0 || gamma > 1.0) {
fprintf(stderr, "pngquant readpng: ignored out-of-range gamma %f\n", gamma);
gamma = 0.45455;
}
}
mainprog_ptr->gamma = gamma;
png_set_interlace_handling(png_ptr);
/* all transformations have been registered; now update info_ptr data,
* get rowbytes and channels, and allocate image memory */
png_read_update_info(png_ptr, info_ptr);
rowbytes = png_get_rowbytes(png_ptr, info_ptr);
if ((mainprog_ptr->rgba_data = malloc(rowbytes*mainprog_ptr->height)) == NULL) {
fprintf(stderr, "pngquant readpng: unable to allocate image data\n");
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return PNG_OUT_OF_MEMORY_ERROR;
}
png_bytepp row_pointers = rwpng_create_row_pointers(info_ptr, png_ptr, mainprog_ptr->rgba_data, mainprog_ptr->height, 0);
/* now we can go ahead and just read the whole image */
png_read_image(png_ptr, row_pointers);
/* and we're done! (png_read_end() can be omitted if no processing of
* post-IDAT text/time/etc. is desired) */
png_read_end(png_ptr, NULL);
#if USE_LCMS
#if PNG_LIBPNG_VER < 10500
png_charp ProfileData;
#else
png_bytep ProfileData;
#endif
png_uint_32 ProfileLen;
cmsHPROFILE hInProfile = NULL;
/* color_type is read from the image before conversion to RGBA */
int COLOR_PNG = color_type & PNG_COLOR_MASK_COLOR;
mainprog_ptr->lcms_status = NONE;
/* embedded ICC profile */
if (png_get_iCCP(png_ptr, info_ptr, &(png_charp){0}, &(int){0}, &ProfileData, &ProfileLen)) {
hInProfile = cmsOpenProfileFromMem(ProfileData, ProfileLen);
cmsColorSpaceSignature colorspace = cmsGetColorSpace(hInProfile);
/* only RGB (and GRAY) valid for PNGs */
if (colorspace == cmsSigRgbData && COLOR_PNG) {
mainprog_ptr->lcms_status = ICCP;
} else {
if (colorspace == cmsSigGrayData && !COLOR_PNG) {
mainprog_ptr->lcms_status = ICCP_WARN_GRAY;
}
cmsCloseProfile(hInProfile);
hInProfile = NULL;
}
}
/* build RGB profile from cHRM and gAMA */
if (hInProfile == NULL && COLOR_PNG &&
!png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB) &&
png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) &&
png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM)) {
cmsCIExyY WhitePoint;
cmsCIExyYTRIPLE Primaries;
png_get_cHRM(png_ptr, info_ptr, &WhitePoint.x, &WhitePoint.y,
&Primaries.Red.x, &Primaries.Red.y,
&Primaries.Green.x, &Primaries.Green.y,
&Primaries.Blue.x, &Primaries.Blue.y);
WhitePoint.Y = Primaries.Red.Y = Primaries.Green.Y = Primaries.Blue.Y = 1.0;
cmsToneCurve *GammaTable[3];
GammaTable[0] = GammaTable[1] = GammaTable[2] = cmsBuildGamma(NULL, 1/gamma);
hInProfile = cmsCreateRGBProfile(&WhitePoint, &Primaries, GammaTable);
cmsFreeToneCurve(GammaTable[0]);
mainprog_ptr->lcms_status = GAMA_CHRM;
}
/* transform image to sRGB colorspace */
if (hInProfile != NULL) {
cmsHPROFILE hOutProfile = cmsCreate_sRGBProfile();
cmsHTRANSFORM hTransform = cmsCreateTransform(hInProfile, TYPE_RGBA_8,
hOutProfile, TYPE_RGBA_8,
INTENT_PERCEPTUAL,
omp_get_max_threads() > 1 ? cmsFLAGS_NOCACHE : 0);
#pragma omp parallel for \
if (mainprog_ptr->height*mainprog_ptr->width > 8000) \
schedule(static)
for (unsigned int i = 0; i < mainprog_ptr->height; i++) {
/* It is safe to use the same block for input and output,
when both are of the same TYPE. */
cmsDoTransform(hTransform, row_pointers[i],
row_pointers[i],
mainprog_ptr->width);
}
cmsDeleteTransform(hTransform);
cmsCloseProfile(hOutProfile);
cmsCloseProfile(hInProfile);
mainprog_ptr->gamma = 0.45455;
}
#endif
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
mainprog_ptr->file_size = read_data.bytes_read;
mainprog_ptr->row_pointers = (unsigned char **)row_pointers;
return SUCCESS;
}
static void rwpng_free_chunks(struct rwpng_chunk *chunk) {
if (!chunk) return;
rwpng_free_chunks(chunk->next);
free(chunk->data);
free(chunk);
}
void rwpng_free_image24(png24_image *image)
{
free(image->row_pointers);
image->row_pointers = NULL;
free(image->rgba_data);
image->rgba_data = NULL;
rwpng_free_chunks(image->chunks);
image->chunks = NULL;
}
void rwpng_free_image8(png8_image *image)
{
free(image->indexed_data);
image->indexed_data = NULL;
free(image->row_pointers);
image->row_pointers = NULL;
rwpng_free_chunks(image->chunks);
image->chunks = NULL;
}
pngquant_error rwpng_read_image24(FILE *infile, png24_image *input_image_p, int verbose)
{
#if USE_COCOA
return rwpng_read_image24_cocoa(infile, input_image_p);
#else
return rwpng_read_image24_libpng(infile, input_image_p, verbose);
#endif
}
static pngquant_error rwpng_write_image_init(rwpng_png_image *mainprog_ptr, png_structpp png_ptr_p, png_infopp info_ptr_p, int fast_compression)
{
/* could also replace libpng warning-handler (final NULL), but no need: */
*png_ptr_p = png_create_write_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr, rwpng_error_handler, NULL);
if (!(*png_ptr_p)) {
return LIBPNG_INIT_ERROR; /* out of memory */
}
*info_ptr_p = png_create_info_struct(*png_ptr_p);
if (!(*info_ptr_p)) {
png_destroy_write_struct(png_ptr_p, NULL);
return LIBPNG_INIT_ERROR; /* out of memory */
}
/* setjmp() must be called in every function that calls a PNG-writing
* libpng function, unless an alternate error handler was installed--
* but compatible error handlers must either use longjmp() themselves
* (as in this program) or exit immediately, so here we go: */
if (setjmp(mainprog_ptr->jmpbuf)) {
png_destroy_write_struct(png_ptr_p, info_ptr_p);
return LIBPNG_INIT_ERROR; /* libpng error (via longjmp()) */
}
png_set_compression_level(*png_ptr_p, fast_compression ? Z_BEST_SPEED : Z_BEST_COMPRESSION);
png_set_compression_mem_level(*png_ptr_p, fast_compression ? 9 : 5); // judging by optipng results, smaller mem makes libpng compress slightly better
return SUCCESS;
}
void rwpng_write_end(png_infopp info_ptr_p, png_structpp png_ptr_p, png_bytepp row_pointers)
{
png_write_info(*png_ptr_p, *info_ptr_p);
png_set_packing(*png_ptr_p);
png_write_image(*png_ptr_p, row_pointers);
png_write_end(*png_ptr_p, NULL);
png_destroy_write_struct(png_ptr_p, info_ptr_p);
}
void rwpng_set_gamma(png_infop info_ptr, png_structp png_ptr, double gamma)
{
/* remap sets gamma to 0.45455 */
png_set_gAMA(png_ptr, info_ptr, gamma);
png_set_sRGB(png_ptr, info_ptr, 0); // 0 = Perceptual
}
pngquant_error rwpng_write_image8(FILE *outfile, const png8_image *mainprog_ptr)
{
png_structp png_ptr;
png_infop info_ptr;
pngquant_error retval = rwpng_write_image_init((rwpng_png_image*)mainprog_ptr, &png_ptr, &info_ptr, mainprog_ptr->fast_compression);
if (retval) return retval;
struct rwpng_write_state write_state;
write_state = (struct rwpng_write_state){
.outfile = outfile,
.maximum_file_size = mainprog_ptr->maximum_file_size,
.retval = SUCCESS,
};
png_set_write_fn(png_ptr, &write_state, user_write_data, user_flush_data);
// Palette images generally don't gain anything from filtering
png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_VALUE_NONE);
rwpng_set_gamma(info_ptr, png_ptr, mainprog_ptr->gamma);
/* set the image parameters appropriately */
int sample_depth;
#if PNG_LIBPNG_VER > 10400 /* old libpng corrupts files with low depth */
if (mainprog_ptr->num_palette <= 2)
sample_depth = 1;
else if (mainprog_ptr->num_palette <= 4)
sample_depth = 2;
else if (mainprog_ptr->num_palette <= 16)
sample_depth = 4;
else
#endif
sample_depth = 8;
struct rwpng_chunk *chunk = mainprog_ptr->chunks;
int chunk_num=0;
while(chunk) {
png_unknown_chunk pngchunk = {
.size = chunk->size,
.data = chunk->data,
.location = chunk->location,
};
memcpy(pngchunk.name, chunk->name, 5);
png_set_unknown_chunks(png_ptr, info_ptr, &pngchunk, 1);
#if defined(PNG_HAVE_IHDR) && PNG_LIBPNG_VER < 10600
png_set_unknown_chunk_location(png_ptr, info_ptr, chunk_num, pngchunk.location ? pngchunk.location : PNG_HAVE_IHDR);
#endif
chunk = chunk->next;
chunk_num++;
}
png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height,
sample_depth, PNG_COLOR_TYPE_PALETTE,
0, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_BASE);
png_set_PLTE(png_ptr, info_ptr, &mainprog_ptr->palette[0], mainprog_ptr->num_palette);
if (mainprog_ptr->num_trans > 0) {
png_set_tRNS(png_ptr, info_ptr, mainprog_ptr->trans, mainprog_ptr->num_trans, NULL);
}
rwpng_write_end(&info_ptr, &png_ptr, mainprog_ptr->row_pointers);
if (SUCCESS == write_state.retval && write_state.maximum_file_size && write_state.bytes_written > write_state.maximum_file_size) {
return TOO_LARGE_FILE;
}
return write_state.retval;
}
pngquant_error rwpng_write_image24(FILE *outfile, const png24_image *mainprog_ptr)
{
png_structp png_ptr;
png_infop info_ptr;
pngquant_error retval = rwpng_write_image_init((rwpng_png_image*)mainprog_ptr, &png_ptr, &info_ptr, 0);
if (retval) return retval;
png_init_io(png_ptr, outfile);
rwpng_set_gamma(info_ptr, png_ptr, mainprog_ptr->gamma);
png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height,
8, PNG_COLOR_TYPE_RGB_ALPHA,
0, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_BASE);
png_bytepp row_pointers = rwpng_create_row_pointers(info_ptr, png_ptr, mainprog_ptr->rgba_data, mainprog_ptr->height, 0);
rwpng_write_end(&info_ptr, &png_ptr, row_pointers);
free(row_pointers);
return SUCCESS;
}
static void rwpng_warning_stderr_handler(png_structp png_ptr, png_const_charp msg) {
fprintf(stderr, " %s\n", msg);
}
static void rwpng_warning_silent_handler(png_structp png_ptr, png_const_charp msg) {
}
static void rwpng_error_handler(png_structp png_ptr, png_const_charp msg)
{
rwpng_png_image *mainprog_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, " error: %s (libpng failed)\n", msg);
fflush(stderr);
mainprog_ptr = png_get_error_ptr(png_ptr);
if (mainprog_ptr == NULL) abort();
longjmp(mainprog_ptr->jmpbuf, 1);
}

125
deps/pngquant/rwpng.h vendored
View file

@ -1,125 +0,0 @@
/*---------------------------------------------------------------------------
pngquant: RGBA -> RGBA-palette quantization program rwpng.h
---------------------------------------------------------------------------
© 1998-2000 by Greg Roelofs.
© 2009-2014 by Kornel Lesiński.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------*/
#ifndef RWPNG_H
#define RWPNG_H
#include "png.h" /* if this include fails, you need to install libpng (e.g. libpng-devel package) and run ./configure */
#include <setjmp.h>
#ifndef USE_COCOA
#define USE_COCOA 0
#endif
typedef enum {
SUCCESS = 0,
MISSING_ARGUMENT = 1,
READ_ERROR = 2,
INVALID_ARGUMENT = 4,
NOT_OVERWRITING_ERROR = 15,
CANT_WRITE_ERROR = 16,
OUT_OF_MEMORY_ERROR = 17,
WRONG_ARCHITECTURE = 18, // Missing SSE
PNG_OUT_OF_MEMORY_ERROR = 24,
LIBPNG_FATAL_ERROR = 25,
WRONG_INPUT_COLOR_TYPE = 26,
LIBPNG_INIT_ERROR = 35,
TOO_LARGE_FILE = 98,
TOO_LOW_QUALITY = 99,
} pngquant_error;
struct rwpng_chunk {
struct rwpng_chunk *next;
png_byte *data;
png_size_t size;
png_byte name[5];
png_byte location;
};
#if USE_LCMS
typedef enum {
NONE = 0,
ICCP = 1, // used ICC profile
ICCP_WARN_GRAY = 2, // ignore and warn about GRAY ICC profile
GAMA_CHRM = 3, // used gAMA and cHARM
} lcms_transform;
#endif
typedef struct {
jmp_buf jmpbuf;
png_uint_32 width;
png_uint_32 height;
png_size_t file_size;
double gamma;
unsigned char **row_pointers;
unsigned char *rgba_data;
struct rwpng_chunk *chunks;
#if USE_LCMS
lcms_transform lcms_status;
#endif
} png24_image;
typedef struct {
jmp_buf jmpbuf;
png_uint_32 width;
png_uint_32 height;
png_size_t maximum_file_size;
double gamma;
unsigned char **row_pointers;
unsigned char *indexed_data;
unsigned int num_palette;
unsigned int num_trans;
png_color palette[256];
unsigned char trans[256];
struct rwpng_chunk *chunks;
char fast_compression;
} png8_image;
typedef union {
jmp_buf jmpbuf;
png24_image png24;
png8_image png8;
} rwpng_png_image;
/* prototypes for public functions in rwpng.c */
void rwpng_version_info(FILE *fp);
pngquant_error rwpng_read_image24(FILE *infile, png24_image *mainprog_ptr, int verbose);
pngquant_error rwpng_write_image8(FILE *outfile, const png8_image *mainprog_ptr);
pngquant_error rwpng_write_image24(FILE *outfile, const png24_image *mainprog_ptr);
void rwpng_free_image24(png24_image *);
void rwpng_free_image8(png8_image *);
#endif

View file

@ -1,73 +0,0 @@
//
// rwpng_cocoa.m
// pngquant
//
#include "rwpng.h"
#ifdef USE_COCOA
#import <Cocoa/Cocoa.h>
#import <CoreGraphics/CoreGraphics.h>
#include <stdio.h>
#include "lib/libimagequant.h"
#include "lib/pam.h"
int rwpng_read_image24_cocoa(FILE *fp, png24_image *out)
{
rgba_pixel *pixel_data;
int width, height;
@autoreleasepool {
NSFileHandle *fh = [[NSFileHandle alloc] initWithFileDescriptor:fileno(fp)];
NSData *data = [fh readDataToEndOfFile];
out->file_size = [data length];
CGImageRef image = [[NSBitmapImageRep imageRepWithData:data] CGImage];
[fh release];
if (!image) return READ_ERROR;
width = CGImageGetWidth(image);
height = CGImageGetHeight(image);
pixel_data = calloc(width*height,4);
CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
CGContextRef context = CGBitmapContextCreate(pixel_data,
width, height,
8, width*4,
colorspace,
kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorspace);
if (!context) return READ_ERROR;
CGContextDrawImage(context, CGRectMake(0.0, 0.0, width, height), image);
CGContextRelease(context);
}
// reverse premultiplication
for(int i=0; i < width*height; i++) {
if (pixel_data[i].a) {
pixel_data[i] = (rgba_pixel){
.a = pixel_data[i].a,
.r = pixel_data[i].r*255/pixel_data[i].a,
.g = pixel_data[i].g*255/pixel_data[i].a,
.b = pixel_data[i].b*255/pixel_data[i].a,
};
}
}
out->gamma = 0.45455;
out->width = width;
out->height = height;
out->rgba_data = (unsigned char *)pixel_data;
out->row_pointers = malloc(sizeof(out->row_pointers[0])*out->height);
for(int i=0; i < out->height; i++) {
out->row_pointers[i] = (unsigned char *)&pixel_data[width*i];
}
return SUCCESS;
}
#endif

View file

@ -346,12 +346,12 @@ source += Split(
""") """)
# libimagequant # libimagequant
lib_env.Append(CFLAGS = "-O3 -fno-math-errno -funroll-loops -fomit-frame-pointer -std=c99") lib_env.Append(CFLAGS = "-O3 -fno-math-errno -funroll-loops -fomit-frame-pointer -std=c99 -msse -mfpmath=sse")
# As of GCC 4.5, 387 fp math is significantly slower in C99 mode without this. # As of GCC 4.5, 387 fp math is significantly slower in C99 mode without this.
# Note: CPUs without SSE2 use 387 for doubles, even when SSE fp math is set. # Note: CPUs without SSE2 use 387 for doubles, even when SSE fp math is set.
if 'gcc' in env['CC']: if 'gcc' in env['CC']:
lib_env.Append(CFLAGS='-fexcess-prevision=fast') lib_env.Append(CFLAGS='-fexcess-prevision=fast')
source += glob.glob("../deps/pngquant/lib/" + "*.c") source += glob.glob("../deps/pngquant/" + "*.c")
if env['RUNTIME_LINK'] == "static": if env['RUNTIME_LINK'] == "static":
source += glob.glob('../deps/agg/src/' + '*.cpp') source += glob.glob('../deps/agg/src/' + '*.cpp')