Merge branch 'master' into invalidate-on-load-error
This commit is contained in:
commit
c464503510
|
@ -0,0 +1,5 @@
|
|||
# Shell scripts and Automake sources can't have CRLF line endings
|
||||
configure.ac eol=lf
|
||||
Makefile.am eol=lf
|
||||
*.m4 eol=lf
|
||||
*.sh eol=lf
|
|
@ -48,7 +48,20 @@ libtool
|
|||
libvips/include/vips/version.h
|
||||
fred
|
||||
ltmain.sh
|
||||
m4/
|
||||
m4/gtk-doc.m4
|
||||
m4/intltool.m4
|
||||
m4/libtool.m4
|
||||
m4/lt~obsolete.m4
|
||||
m4/ltoptions.m4
|
||||
m4/ltsugar.m4
|
||||
m4/ltversion.m4
|
||||
m4/codeset.m4
|
||||
m4/gettext.m4
|
||||
m4/glibc21.m4
|
||||
m4/iconv.m4
|
||||
m4/introspection.m4
|
||||
m4/lcmessage.m4
|
||||
m4/progtest.m4
|
||||
missing
|
||||
mkinstalldirs
|
||||
po/Makefile.in.in
|
||||
|
|
100
.travis.yml
100
.travis.yml
|
@ -1,19 +1,85 @@
|
|||
language: cpp
|
||||
before_install:
|
||||
- sudo add-apt-repository ppa:lyrasis/precise-backports -y
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install automake gtk-doc-tools
|
||||
- sudo apt-get install gobject-introspection
|
||||
- sudo apt-get install libfftw3-dev libjpeg-turbo8-dev
|
||||
- sudo apt-get install libpng12-dev libwebp-dev libtiff4-dev libxml2-dev
|
||||
- sudo apt-get install swig libmagick++-dev bc
|
||||
- sudo apt-get install libcfitsio3-dev libgsl0-dev libmatio-dev
|
||||
- sudo apt-get install liborc-0.4-dev liblcms2-dev
|
||||
|
||||
before_script:
|
||||
- ./bootstrap.sh
|
||||
- ./configure
|
||||
- make
|
||||
- sudo make install
|
||||
- sudo ldconfig
|
||||
script:
|
||||
- make check
|
||||
- ./autogen.sh
|
||||
--disable-dependency-tracking
|
||||
--with-jpeg-includes=$JPEG/include
|
||||
--with-jpeg-libraries=$JPEG/lib
|
||||
- make -j$JOBS -s V=0
|
||||
script:
|
||||
- make -Ctest -j$JOBS -s V=0 VERBOSE=1 check
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- os: osx
|
||||
include:
|
||||
- os: linux
|
||||
sudo: required
|
||||
dist: precise
|
||||
env:
|
||||
- PYTHON=/usr/bin/python
|
||||
- JPEG=/usr
|
||||
- JOBS=`nproc`
|
||||
- LIBTOOLFLAGS=--quiet
|
||||
cache: ccache
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -y
|
||||
automake gtk-doc-tools
|
||||
gobject-introspection
|
||||
libfftw3-dev libjpeg-turbo8-dev
|
||||
libpng12-dev libwebp-dev libtiff4-dev libxml2-dev
|
||||
swig libmagick++-dev bc
|
||||
libcfitsio3-dev libgsl0-dev libmatio-dev
|
||||
liborc-0.4-dev liblcms2-dev libpoppler-glib-dev
|
||||
librsvg2-dev libgif-dev
|
||||
libpango1.0-dev
|
||||
python-dev
|
||||
|
||||
- os: linux
|
||||
sudo: required
|
||||
dist: trusty
|
||||
env:
|
||||
- PYTHON=/usr/bin/python
|
||||
- JPEG=/usr
|
||||
- JOBS=`nproc`
|
||||
cache: ccache
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -y
|
||||
automake gtk-doc-tools
|
||||
gobject-introspection
|
||||
libfftw3-dev libjpeg-turbo8-dev
|
||||
libpng12-dev libwebp-dev libtiff4-dev libxml2-dev
|
||||
swig libmagick++-dev bc
|
||||
libcfitsio3-dev libgsl0-dev libmatio-dev
|
||||
liborc-0.4-dev liblcms2-dev libpoppler-glib-dev
|
||||
librsvg2-dev libgif-dev
|
||||
libpango1.0-dev libgsf-1-dev libopenslide-dev
|
||||
python-dev python-gi-dev libgirepository1.0-dev
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode7.3
|
||||
env:
|
||||
- PYTHON=/usr/local/bin/python
|
||||
- JPEG=/usr/local/opt/mozjpeg
|
||||
- PKG_CONFIG_PATH=/usr/local/opt/libffi/lib/pkgconfig
|
||||
- JOBS="`sysctl -n hw.ncpu`"
|
||||
- PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
cache: ccache
|
||||
before_install:
|
||||
- brew tap homebrew/science
|
||||
- brew update
|
||||
- brew install ccache pkg-config automake
|
||||
- brew install --ignore-dependencies gtk-doc
|
||||
- brew reinstall
|
||||
gobject-introspection
|
||||
fftw mozjpeg libexif
|
||||
libpng webp libtiff
|
||||
swig imagemagick
|
||||
cfitsio libmatio
|
||||
orc little-cms2 poppler
|
||||
pango libgsf openslide
|
||||
librsvg giflib openexr
|
||||
python pygobject3
|
||||
|
|
104
ChangeLog
104
ChangeLog
|
@ -1,6 +1,107 @@
|
|||
13/10/16 started 8.5.0
|
||||
- rewritten buffer system is safer and frees memory earlier
|
||||
- added tiff save to buffer
|
||||
- added dzsave save to buffer (zip only)
|
||||
- revise header get/set functions
|
||||
- better vipsheader behaviour with complex field types
|
||||
- added vips_image_hasalpha()
|
||||
- added vips_thumbnail() / vips_thumbnail_buffer()
|
||||
- webpload/webpsave read and write icc, xmp, exif metadata
|
||||
- better >4gb detect for zip dzsave output [Felix Bünemann]
|
||||
- all loaders have a @fail option, meaning fail on first warning, though it
|
||||
only does anything for jpg and csv
|
||||
- add vips_image_get_fields() to help bindings
|
||||
- add tiff multi-page read/write
|
||||
- add VIPS_META_PAGE_HEIGHT metadata
|
||||
- IM6/IM7 magickload supports page/n/page-height, all_frames deprecated
|
||||
- gifload supports n/page-height
|
||||
- added #defines for VIPS_SONAME, VIPS_LIBRARY_CURRENT, VIPS_LIBRARY_REVISION,
|
||||
VIPS_LIBRARY_AGE
|
||||
- better support for bscale / bzero in fits images
|
||||
- deprecate vips_warn() / vips_info(); use g_warning() / g_info() instead
|
||||
- vipsthumbnail supports much fancier geometry strings, thanks tomasc
|
||||
- vips_thumbnail() has new @size option
|
||||
- fix --vips-cache-max etc.
|
||||
- add compute reordering, plus some new API to support it:
|
||||
vips_reorder_margin_hint() and vips_reorder_prepare_many(), thanks
|
||||
aferrero2707
|
||||
- kick load operations from cache on read error, thanks gaillard
|
||||
|
||||
8/12/16 started 8.4.5
|
||||
- allow libgsf-1.14.26 to help centos, thanks tdiprima
|
||||
|
||||
11/11/16 started 8.4.4
|
||||
- fix crash in vips.exe arg parsing on Windows, thanks Yury
|
||||
|
||||
18/10/16 started 8.4.3
|
||||
- fix error detection in gif_close, thanks aaron42net
|
||||
- fix tiny threading memleak
|
||||
- improve compatibility with very old glib, see #548
|
||||
|
||||
27/9/16 started 8.4.2
|
||||
- small doc improvements
|
||||
- fix error message for metadata fetch type mismatch
|
||||
- resolve a race condition in thread shutdown, thanks Lovell
|
||||
|
||||
1/5/16 started 8.4
|
||||
- many more wepsave options [Felix Bünemann]
|
||||
- kick load operations from cache on read error
|
||||
- added quant_table option to wepsave [Felix Bünemann]
|
||||
- added @n option to pdfload, thanks andris
|
||||
- dzsave won't write empty tiles in google mode, thanks bverem, perog,
|
||||
felixbuenemann
|
||||
- allow nested [] in CLI args
|
||||
- restore BandFmt on unpremultiply in vipsthumbnail
|
||||
- better python detection and build [Felix Bünemann]
|
||||
- max-alpha defaults to 65535 for RGB16/GREY16
|
||||
- added radsave_buffer [Henri Chain]
|
||||
- support tiff orientation tag
|
||||
- autorotate option for tiff load
|
||||
- tiffsave converts for jpg if jpg compression is turned on
|
||||
- tiffsave supports --strip
|
||||
- conversions to GREY16 could lock
|
||||
- free pixel buffers on image close as well as thread exit ... stops main
|
||||
thread buffers clogging up the system
|
||||
- dzsave can write compressed zips [Felix Bünemann]
|
||||
- vips_image_write() only refs the input when it has to ... makes it easier to
|
||||
combine many images in bounded memory
|
||||
- VImage::write() implementation was missing
|
||||
- VImage::write() return value changed from void to VImage to help chaining
|
||||
- added C++ arithmetic assignment overloads, += etc.
|
||||
- VImage::ifthenelse() with double args was missing =0 on options
|
||||
- better accuracy for reducev with smarter multiplication
|
||||
- better quality for vips_resize() with linear/cubic kernels
|
||||
- pyvips8 can create new metadata
|
||||
- better upsizing with vips_resize()
|
||||
- add imagemagick v7 support, thanks sachinwalia2k8
|
||||
- added vips_worley(), vips_perlin() noise generators
|
||||
- added vips_convf(), vips_convi(), vips_convasep(), vips_conva() ...
|
||||
im_conv*() functions rewritten as classes
|
||||
- vips_convsep() calls vips_convasep() for the approximate case
|
||||
- new fixed-point vector path for convi is up to about 2x faster
|
||||
- gif loader can make 1, 2, 3, or 4 bands depending on file contents
|
||||
- support --strip for pngsave
|
||||
- add svgz support [Felix Bünemann]
|
||||
- rename boostrap.sh -> autogen.sh to help snapcraft
|
||||
- support unicode filenames on Windows
|
||||
- added VIPS_ROUND as well as VIPS_RINT
|
||||
- resize/reduce*/shrink*/affine now round output size to nearest rather than
|
||||
rounding down, thanks ioquatix
|
||||
- better support for tile overlaps in google maps mode in dzsave
|
||||
- dzsave puts vips-properties.xml in the main dir for gm and zoomify layouts
|
||||
- resize and reduce have @centre option for centre convention downsampling
|
||||
- vipsthumbnail uses centre convention to better match imagemagick
|
||||
|
||||
19/8/16 started 8.3.4
|
||||
- better transparency handling in gifload, thanks diegocsandrim
|
||||
|
||||
30/7/16 started 8.3.3
|
||||
- fix performance regression in 8.3.2, thanks Lovell
|
||||
- yet more robust vips file reading
|
||||
|
||||
18/5/16 started 8.3.2
|
||||
- more robust vips image reading
|
||||
- more robust tiff read [Matt Richards]
|
||||
>>>>>>> master
|
||||
|
||||
15/4/16 started 8.3.1
|
||||
- rename vips wrapper script, it was still vips-8.2, thanks Benjamin
|
||||
|
@ -9,6 +110,7 @@
|
|||
- add giflib5 support
|
||||
- allow resize >1 on one axis, <1 on the other
|
||||
- vips_resize has an optional @kernel argument
|
||||
- fix giflib4 detection [felixbuenemann]
|
||||
|
||||
29/1/16 started 8.3
|
||||
- add vips_reduce*() ... a fast path for affine downsize
|
||||
|
|
|
@ -32,12 +32,11 @@ SUBDIRS = \
|
|||
EXTRA_DIST = \
|
||||
m4 \
|
||||
benchmark \
|
||||
bootstrap.sh \
|
||||
autogen.sh \
|
||||
vips.pc.in \
|
||||
vipsCC.pc.in \
|
||||
vips-cpp.pc.in \
|
||||
libvips.supp \
|
||||
acinclude.m4 \
|
||||
depcomp \
|
||||
README.md \
|
||||
$(P_DIST_DIR) \
|
||||
|
|
22
README.md
22
README.md
|
@ -1,6 +1,6 @@
|
|||
# libvips : an image processing library
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/jcupitt/libvips.png)](http://travis-ci.org/jcupitt/libvips)
|
||||
[![Build Status](https://travis-ci.org/jcupitt/libvips.svg?branch=master)](https://travis-ci.org/jcupitt/libvips)
|
||||
[![Coverity Status](https://scan.coverity.com/projects/6503/badge.svg)](https://scan.coverity.com/projects/jcupitt-libvips)
|
||||
|
||||
libvips is a 2D image processing library. Compared to
|
||||
|
@ -25,6 +25,8 @@ binding](http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/usin
|
|||
and a [command-line
|
||||
interface](http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/using-cli.html).
|
||||
Bindings are available for [Ruby](https://rubygems.org/gems/ruby-vips),
|
||||
[PHP](https://github.com/jcupitt/php-vips),
|
||||
[Go](https://github.com/davidbyttow/govips),
|
||||
JavaScript and others. There is full
|
||||
[documentation](http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/index.html).
|
||||
There are several GUIs as well, see the [VIPS
|
||||
|
@ -80,7 +82,7 @@ and `gobject-introspection`, see the dependencies section below. For example:
|
|||
|
||||
Then build the build system with:
|
||||
|
||||
$ ./bootstrap.sh
|
||||
$ ./autogen.sh
|
||||
|
||||
Debug build:
|
||||
|
||||
|
@ -92,11 +94,14 @@ Debug build:
|
|||
Leak check:
|
||||
|
||||
$ export G_DEBUG=gc-friendly
|
||||
$ export G_SLICE=always-malloc
|
||||
$ valgrind --suppressions=libvips.supp \
|
||||
--leak-check=yes \
|
||||
vips ... > vips-vg.log 2>&1
|
||||
|
||||
Memory error debug:
|
||||
|
||||
$ valgrind --vgdb=yes --vgdb-error=0 vips ...
|
||||
|
||||
valgrind threading check:
|
||||
|
||||
$ valgrind --tool=helgrind vips ... > vips-vg.log 2>&1
|
||||
|
@ -150,12 +155,14 @@ libraries automatically. See `./configure --help` for a set of flags to
|
|||
control library detection. Packages are generally found with `pkg-config`,
|
||||
so make sure that is working.
|
||||
|
||||
libtiff and libjpeg do not usually use `pkg-config` so libvips looks for
|
||||
libtiff, giflib and libjpeg do not usually use `pkg-config` so libvips looks for
|
||||
them in the default path and in `$prefix`. If you have installed your own
|
||||
versions of these libraries in a different location, libvips will not see
|
||||
them. Use switches to libvips configure like:
|
||||
|
||||
./configure --prefix=/Users/john/vips \
|
||||
--with-giflib-includes=/opt/local/include \
|
||||
--with-giflib-libraries=/opt/local/lib \
|
||||
--with-tiff-includes=/opt/local/include \
|
||||
--with-tiff-libraries=/opt/local/lib \
|
||||
--with-jpeg-includes=/opt/local/include \
|
||||
|
@ -187,8 +194,6 @@ If available, libvips adds support for EXIF metadata in JPEG files.
|
|||
The standard gif loader. If this is not present, vips will try to load gifs
|
||||
via imagemagick instead.
|
||||
|
||||
vips will only work with giflib 4.
|
||||
|
||||
### librsvg
|
||||
|
||||
The usual SVG loader. If this is not present, vips will try to load SVGs
|
||||
|
@ -238,6 +243,11 @@ Imagemagick 6.9+ needs to have been built with `--with-modules`. Most packaged
|
|||
IMs are, I think, but if you are rolling your own, you'll need to pass
|
||||
this flag to configure.
|
||||
|
||||
If you are going to be using libvips with untrusted images, perhaps in a
|
||||
web-server, for example, you should consider the security implications of
|
||||
using a package with such a large attack surface. You might prefer not to
|
||||
enable Magick support.
|
||||
|
||||
### pangoft2
|
||||
|
||||
If available, libvips adds support for text rendering. You need the
|
||||
|
|
66
TODO
66
TODO
|
@ -1,3 +1,4 @@
|
|||
|
||||
- jpegload should put a pointer to the load operation inside the output image
|
||||
somewhere
|
||||
|
||||
|
@ -6,17 +7,34 @@
|
|||
then the loaders can use this if they get a read error from the lib to
|
||||
invalidate the load operation and knock it out of cache
|
||||
|
||||
- I like the new int mask creator in reducev, can we use it in im_vips2imask()
|
||||
as well?
|
||||
|
||||
what about making the int masks for the interpolators?
|
||||
|
||||
|
||||
|
||||
|
||||
- not sure about utf8 error messages on win
|
||||
|
||||
- strange:
|
||||
|
||||
$ vips similarity --scale 0.33 k2.jpg x.v
|
||||
$ vipsheader k2.jpg
|
||||
k2.jpg: 1450x2048 uchar, 3 bands, srgb, jpegload
|
||||
$ vipsheader x.v
|
||||
x.v: 478x676 uchar, 3 bands, srgb, jpegload
|
||||
|
||||
1450 * 0.33 = 478.5 ... this was rounded down
|
||||
2048 * 0.33 = 675.84 ... this was rounded up
|
||||
|
||||
- add APPROX convsep test?
|
||||
|
||||
- add more webp tests to py suite
|
||||
|
||||
- try moving some more of the CLI tests to py
|
||||
|
||||
- try SEQ_UNBUFFERED on jpg source, get out of order error?
|
||||
|
||||
- could load pdf thumbnails?
|
||||
|
||||
- still not happy about float->int mask conversion in im_vips2mask.c
|
||||
|
||||
- colour needs to split _build() into preprocess / process / postprocess
|
||||
phases
|
||||
|
||||
|
@ -57,39 +75,9 @@
|
|||
|
||||
|
||||
|
||||
- support orientation tag in tiff images
|
||||
|
||||
see
|
||||
|
||||
http://www.awaresystems.be/imaging/tiff/tifftags/orientation.html
|
||||
|
||||
seems to be the same values used for exif
|
||||
|
||||
jpg uses "exif-ifd0-Orientation", see conversion/autorot.c
|
||||
|
||||
instead, standardize on "orientation" and give it an int value
|
||||
|
||||
jpg load and save need to use this in preference to the exif value
|
||||
|
||||
tiff load and save need to set and recover this value
|
||||
|
||||
tiff load needs an autorot flag
|
||||
|
||||
don't touch parse_header()
|
||||
|
||||
on header-only read, just swap width/height and delete the tag
|
||||
|
||||
on full image read, use read_jpeg_rotate(), broken out into a separate
|
||||
func
|
||||
|
||||
see https://github.com/jcupitt/libvips/issues/243
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- why can't we do
|
||||
|
||||
|
||||
im = Vips.Image.new_from_file("/data/john/pics/k2.jpg", access = "sequential")
|
||||
|
||||
|
||||
|
@ -470,13 +458,11 @@ new operations
|
|||
|
||||
Article: nomis80.org/ctmf.pdf
|
||||
|
||||
- check CMC equations against web
|
||||
|
||||
http://en.wikipedia.org/wiki/Color_difference#CMC_l:c_.281984.29
|
||||
|
||||
- see
|
||||
|
||||
http://www.dentistry.bham.ac.uk/landinig/software/cdeconv/cdeconv.html
|
||||
|
||||
http://www.nature.com/srep/2015/150730/srep12096/full/srep12096.html
|
||||
|
||||
sounds useful for BM?
|
||||
|
||||
|
|
686
acinclude.m4
686
acinclude.m4
|
@ -1,686 +0,0 @@
|
|||
dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding
|
||||
dnl
|
||||
dnl FIND_ZIP[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]
|
||||
dnl ------------------------------------------------
|
||||
dnl
|
||||
dnl Find ZIP libraries and headers
|
||||
dnl
|
||||
dnl Put includes stuff in ZIP_INCLUDES
|
||||
dnl Put link stuff in ZIP_LIBS
|
||||
dnl Define HAVE_ZIP if found
|
||||
dnl
|
||||
AC_DEFUN([FIND_ZIP], [
|
||||
AC_REQUIRE([AC_PATH_XTRA])
|
||||
|
||||
ZIP_INCLUDES=""
|
||||
ZIP_LIBS=""
|
||||
|
||||
AC_ARG_WITH(zip,
|
||||
AS_HELP_STRING([--without-zip], [build without libx (default: test)]))
|
||||
# Treat --without-zip like --without-zip-includes --without-zip-libraries.
|
||||
if test "$with_zip" = "no"; then
|
||||
ZIP_INCLUDES=no
|
||||
ZIP_LIBS=no
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(zip-includes,
|
||||
AS_HELP_STRING([--with-zip-includes=DIR], [libz includes are in DIR]),
|
||||
ZIP_INCLUDES="-I$withval")
|
||||
AC_ARG_WITH(zip-libraries,
|
||||
AS_HELP_STRING([--with-zip-libraries=DIR], [libz libraries are in DIR]),
|
||||
ZIP_LIBS="-L$withval -lz")
|
||||
|
||||
AC_MSG_CHECKING(for ZIP)
|
||||
|
||||
# Look for zlib.h
|
||||
if test "$ZIP_INCLUDES" = ""; then
|
||||
# Check the standard search path
|
||||
AC_TRY_COMPILE([#include <zlib.h>],[int a;],[
|
||||
ZIP_INCLUDES=""
|
||||
], [
|
||||
# zlib.h is not in the standard search path, try
|
||||
# $prefix
|
||||
zip_save_INCLUDES="$INCLUDES"
|
||||
|
||||
INCLUDES="-I${prefix}/include $INCLUDES"
|
||||
|
||||
AC_TRY_COMPILE([#include <zlib.h>],[int a;],[
|
||||
ZIP_INCLUDES="-I${prefix}/include"
|
||||
], [
|
||||
ZIP_INCLUDES="no"
|
||||
])
|
||||
|
||||
INCLUDES=$zip_save_INCLUDES
|
||||
])
|
||||
fi
|
||||
|
||||
# Now for the libraries
|
||||
if test "$ZIP_LIBS" = ""; then
|
||||
zip_save_LIBS="$LIBS"
|
||||
zip_save_INCLUDES="$INCLUDES"
|
||||
|
||||
LIBS="-lz $LIBS"
|
||||
INCLUDES="$ZIP_INCLUDES $INCLUDES"
|
||||
|
||||
# Try the standard search path first
|
||||
AC_TRY_LINK([#include <zlib.h>],[zlibVersion()], [
|
||||
ZIP_LIBS="-lz"
|
||||
], [
|
||||
# libz is not in the standard search path, try $prefix
|
||||
|
||||
LIBS="-L${prefix}/lib $LIBS"
|
||||
|
||||
AC_TRY_LINK([#include <zlib.h>],[zlibVersion()], [
|
||||
ZIP_LIBS="-L${prefix}/lib -lz"
|
||||
], [
|
||||
ZIP_LIBS=no
|
||||
])
|
||||
])
|
||||
|
||||
LIBS="$zip_save_LIBS"
|
||||
INCLUDES="$zip_save_INCLUDES"
|
||||
fi
|
||||
|
||||
AC_SUBST(ZIP_LIBS)
|
||||
AC_SUBST(ZIP_INCLUDES)
|
||||
|
||||
# Print a helpful message
|
||||
zip_libraries_result="$ZIP_LIBS"
|
||||
zip_includes_result="$ZIP_INCLUDES"
|
||||
|
||||
if test x"$zip_libraries_result" = x""; then
|
||||
zip_libraries_result="in default path"
|
||||
fi
|
||||
if test x"$zip_includes_result" = x""; then
|
||||
zip_includes_result="in default path"
|
||||
fi
|
||||
|
||||
if test "$zip_libraries_result" = "no"; then
|
||||
zip_libraries_result="(none)"
|
||||
fi
|
||||
if test "$zip_includes_result" = "no"; then
|
||||
zip_includes_result="(none)"
|
||||
fi
|
||||
|
||||
AC_MSG_RESULT([libraries $zip_libraries_result, headers $zip_includes_result])
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test "$ZIP_INCLUDES" != "no" && test "$ZIP_LIBS" != "no"; then
|
||||
AC_DEFINE(HAVE_ZIP,1,[Define if you have libz libraries and header files.])
|
||||
$1
|
||||
else
|
||||
ZIP_LIBS=""
|
||||
ZIP_INCLUDES=""
|
||||
$2
|
||||
fi
|
||||
|
||||
])dnl
|
||||
|
||||
dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding
|
||||
dnl
|
||||
dnl FIND_TIFF[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]
|
||||
dnl ------------------------------------------------
|
||||
dnl
|
||||
dnl Find TIFF libraries and headers
|
||||
dnl
|
||||
dnl Put compile stuff in TIFF_INCLUDES
|
||||
dnl Put link stuff in TIFF_LIBS
|
||||
dnl Define HAVE_TIFF if found
|
||||
dnl
|
||||
AC_DEFUN([FIND_TIFF], [
|
||||
AC_REQUIRE([AC_PATH_XTRA])
|
||||
|
||||
TIFF_INCLUDES=""
|
||||
TIFF_LIBS=""
|
||||
|
||||
AC_ARG_WITH(tiff,
|
||||
AS_HELP_STRING([--without-tiff], [build without libtiff (default: test)]))
|
||||
# Treat --without-tiff like --without-tiff-includes --without-tiff-libraries.
|
||||
if test "$with_tiff" = "no"; then
|
||||
TIFF_INCLUDES=no
|
||||
TIFF_LIBS=no
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(tiff-includes,
|
||||
AS_HELP_STRING([--with-tiff-includes=DIR], [libtiff includes are in DIR]),
|
||||
TIFF_INCLUDES="-I$withval")
|
||||
AC_ARG_WITH(tiff-libraries,
|
||||
AS_HELP_STRING([--with-tiff-libraries=DIR], [libtiff libraries are in DIR]),
|
||||
TIFF_LIBS="-L$withval -ltiff")
|
||||
|
||||
AC_MSG_CHECKING(for TIFF)
|
||||
|
||||
# Look for tiff.h
|
||||
if test "$TIFF_INCLUDES" = ""; then
|
||||
# Check the standard search path
|
||||
AC_TRY_COMPILE([#include <tiff.h>],[int a;],[
|
||||
TIFF_INCLUDES=""
|
||||
], [
|
||||
# tiff.h is not in the standard search path, try
|
||||
# $prefix
|
||||
tiff_save_INCLUDES="$INCLUDES"
|
||||
|
||||
INCLUDES="-I${prefix}/include $INCLUDES"
|
||||
|
||||
AC_TRY_COMPILE([#include <tiff.h>],[int a;],[
|
||||
TIFF_INCLUDES="-I${prefix}/include"
|
||||
], [
|
||||
TIFF_INCLUDES="no"
|
||||
])
|
||||
|
||||
INCLUDES=$tiff_save_INCLUDES
|
||||
])
|
||||
fi
|
||||
|
||||
# Now for the libraries
|
||||
if test "$TIFF_LIBS" = ""; then
|
||||
tiff_save_LIBS="$LIBS"
|
||||
tiff_save_INCLUDES="$INCLUDES"
|
||||
|
||||
LIBS="-ltiff -lm $LIBS"
|
||||
INCLUDES="$TIFF_INCLUDES $INCLUDES"
|
||||
|
||||
# Try the standard search path first
|
||||
AC_TRY_LINK([#include <tiff.h>],[TIFFGetVersion()], [
|
||||
TIFF_LIBS="-ltiff"
|
||||
], [
|
||||
# libtiff is not in the standard search path, try $prefix
|
||||
|
||||
LIBS="-L${prefix}/lib $LIBS"
|
||||
|
||||
AC_TRY_LINK([#include <tiff.h>],[TIFFGetVersion()], [
|
||||
TIFF_LIBS="-L${prefix}/lib -ltiff"
|
||||
], [
|
||||
TIFF_LIBS=no
|
||||
])
|
||||
])
|
||||
|
||||
LIBS="$tiff_save_LIBS"
|
||||
INCLUDES="$tiff_save_INCLUDES"
|
||||
fi
|
||||
|
||||
AC_SUBST(TIFF_LIBS)
|
||||
AC_SUBST(TIFF_INCLUDES)
|
||||
|
||||
# Print a helpful message
|
||||
tiff_libraries_result="$TIFF_LIBS"
|
||||
tiff_includes_result="$TIFF_INCLUDES"
|
||||
|
||||
if test x"$tiff_libraries_result" = x""; then
|
||||
tiff_libraries_result="in default path"
|
||||
fi
|
||||
if test x"$tiff_includes_result" = x""; then
|
||||
tiff_includes_result="in default path"
|
||||
fi
|
||||
|
||||
if test "$tiff_libraries_result" = "no"; then
|
||||
tiff_libraries_result="(none)"
|
||||
fi
|
||||
if test "$tiff_includes_result" = "no"; then
|
||||
tiff_includes_result="(none)"
|
||||
fi
|
||||
|
||||
AC_MSG_RESULT([libraries $tiff_libraries_result, headers $tiff_includes_result])
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test "$TIFF_INCLUDES" != "no" && test "$TIFF_LIBS" != "no"; then
|
||||
AC_DEFINE(HAVE_TIFF,1,[Define if you have tiff libraries and header files.])
|
||||
$1
|
||||
else
|
||||
TIFF_INCLUDES=""
|
||||
TIFF_LIBS=""
|
||||
$2
|
||||
fi
|
||||
|
||||
])dnl
|
||||
|
||||
dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding
|
||||
dnl
|
||||
dnl FIND_JPEG[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]
|
||||
dnl ------------------------------------------------
|
||||
dnl
|
||||
dnl Find JPEG libraries and headers
|
||||
dnl
|
||||
dnl Put compile stuff in JPEG_INCLUDES
|
||||
dnl Put link stuff in JPEG_LIBS
|
||||
dnl Define HAVE_JPEG if found
|
||||
dnl
|
||||
AC_DEFUN([FIND_JPEG], [
|
||||
AC_REQUIRE([AC_PATH_XTRA])
|
||||
|
||||
JPEG_INCLUDES=""
|
||||
JPEG_LIBS=""
|
||||
|
||||
AC_ARG_WITH(jpeg,
|
||||
AS_HELP_STRING([--without-jpeg], [build without libjpeg (default: test)]))
|
||||
# Treat --without-jpeg like --without-jpeg-includes --without-jpeg-libraries.
|
||||
if test "$with_jpeg" = "no"; then
|
||||
JPEG_INCLUDES=no
|
||||
JPEG_LIBS=no
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(jpeg-includes,
|
||||
AS_HELP_STRING([--with-jpeg-includes=DIR], [libjpeg includes are in DIR]),
|
||||
JPEG_INCLUDES="-I$withval")
|
||||
AC_ARG_WITH(jpeg-libraries,
|
||||
AS_HELP_STRING([--with-jpeg-libraries=DIR], [libjpeg libraries are in DIR]),
|
||||
JPEG_LIBS="-L$withval -ljpeg")
|
||||
|
||||
AC_MSG_CHECKING(for JPEG)
|
||||
|
||||
# Look for jpeglib.h
|
||||
if test "$JPEG_INCLUDES" = ""; then
|
||||
# Check the standard search path
|
||||
AC_TRY_COMPILE([#include <stdio.h>
|
||||
#include <jpeglib.h>],[int a;],[
|
||||
JPEG_INCLUDES=""
|
||||
], [
|
||||
# jpeglib.h is not in the standard search path, try
|
||||
# $prefix
|
||||
jpeg_save_INCLUDES="$INCLUDES"
|
||||
|
||||
INCLUDES="-I${prefix}/include $INCLUDES"
|
||||
|
||||
AC_TRY_COMPILE([#include <stdio.h>
|
||||
#include <jpeglib.h>],[int a;],[
|
||||
JPEG_INCLUDES="-I${prefix}/include"
|
||||
], [
|
||||
JPEG_INCLUDES="no"
|
||||
])
|
||||
|
||||
INCLUDES=$jpeg_save_INCLUDES
|
||||
])
|
||||
fi
|
||||
|
||||
# Now for the libraries
|
||||
if test "$JPEG_LIBS" = ""; then
|
||||
jpeg_save_LIBS="$LIBS"
|
||||
jpeg_save_INCLUDES="$INCLUDES"
|
||||
|
||||
LIBS="-ljpeg $LIBS"
|
||||
INCLUDES="$JPEG_INCLUDES $INCLUDES"
|
||||
|
||||
# Try the standard search path first
|
||||
AC_TRY_LINK([#include <stdio.h>
|
||||
#include <jpeglib.h>
|
||||
],[jpeg_abort((void*)0)], [
|
||||
JPEG_LIBS="-ljpeg"
|
||||
], [
|
||||
# libjpeg is not in the standard search path, try $prefix
|
||||
|
||||
LIBS="-L${prefix}/lib $LIBS"
|
||||
|
||||
AC_TRY_LINK([#include <stdio.h>
|
||||
#include <jpeg.h>
|
||||
],[jpeg_abort((void*)0)], [
|
||||
JPEG_LIBS="-L${prefix}/lib -ljpeg"
|
||||
], [
|
||||
JPEG_LIBS=no
|
||||
])
|
||||
])
|
||||
|
||||
LIBS="$jpeg_save_LIBS"
|
||||
INCLUDES="$jpeg_save_INCLUDES"
|
||||
fi
|
||||
|
||||
AC_SUBST(JPEG_LIBS)
|
||||
AC_SUBST(JPEG_INCLUDES)
|
||||
|
||||
# Print a helpful message
|
||||
jpeg_libraries_result="$JPEG_LIBS"
|
||||
jpeg_includes_result="$JPEG_INCLUDES"
|
||||
|
||||
if test x"$jpeg_libraries_result" = x""; then
|
||||
jpeg_libraries_result="in default path"
|
||||
fi
|
||||
if test x"$jpeg_includes_result" = x""; then
|
||||
jpeg_includes_result="in default path"
|
||||
fi
|
||||
|
||||
if test "$jpeg_libraries_result" = "no"; then
|
||||
jpeg_libraries_result="(none)"
|
||||
fi
|
||||
if test "$jpeg_includes_result" = "no"; then
|
||||
jpeg_includes_result="(none)"
|
||||
fi
|
||||
|
||||
AC_MSG_RESULT([libraries $jpeg_libraries_result, headers $jpeg_includes_result])
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test "$JPEG_INCLUDES" != "no" && test "$JPEG_LIBS" != "no"; then
|
||||
AC_DEFINE(HAVE_JPEG,1,[Define if you have jpeg libraries and header files.])
|
||||
$1
|
||||
else
|
||||
JPEG_INCLUDES=""
|
||||
JPEG_LIBS=""
|
||||
$2
|
||||
fi
|
||||
|
||||
])dnl
|
||||
|
||||
dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding
|
||||
dnl
|
||||
dnl FIND_PNG[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]
|
||||
dnl ------------------------------------------------
|
||||
dnl
|
||||
dnl Find PNG libraries and headers
|
||||
dnl
|
||||
dnl Put compile stuff in PNG_INCLUDES
|
||||
dnl Put link stuff in PNG_LIBS
|
||||
dnl Define HAVE_PNG if found.
|
||||
dnl
|
||||
AC_DEFUN([FIND_PNG], [
|
||||
AC_REQUIRE([AC_PATH_XTRA])
|
||||
|
||||
PNG_INCLUDES=""
|
||||
PNG_LIBS=""
|
||||
|
||||
AC_ARG_WITH(png,
|
||||
AS_HELP_STRING([--without-png], [build without libpng (default: test)]))
|
||||
# Treat --without-png like --without-png-includes --without-png-libraries.
|
||||
if test "$with_png" = "no"; then
|
||||
PNG_INCLUDES=no
|
||||
PNG_LIBS=no
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(png-includes,
|
||||
AS_HELP_STRING([--with-png-includes=DIR], [libpng includes are in DIR]),
|
||||
PNG_INCLUDES="-I$withval")
|
||||
AC_ARG_WITH(png-libraries,
|
||||
AS_HELP_STRING([--with-png-libraries=DIR], [libpng libraries are in DIR]),
|
||||
PNG_LIBS="-L$withval -lpng")
|
||||
|
||||
AC_MSG_CHECKING(for libpng)
|
||||
|
||||
# Look for png.h
|
||||
if test "$PNG_INCLUDES" = ""; then
|
||||
# Check the standard search path
|
||||
AC_TRY_COMPILE([#include <png.h>],[int a;],[
|
||||
PNG_INCLUDES=""
|
||||
], [
|
||||
# png.h is not in the standard search path, try
|
||||
# $prefix
|
||||
png_save_INCLUDES="$INCLUDES"
|
||||
|
||||
INCLUDES="-I${prefix}/include $INCLUDES"
|
||||
|
||||
AC_TRY_COMPILE([#include <png.h>],[int a;],[
|
||||
PNG_INCLUDES="-I${prefix}/include"
|
||||
], [
|
||||
PNG_INCLUDES="no"
|
||||
])
|
||||
|
||||
INCLUDES=$png_save_INCLUDES
|
||||
])
|
||||
fi
|
||||
|
||||
# Now for the libraries
|
||||
if test "$PNG_LIBS" = ""; then
|
||||
png_save_LIBS="$LIBS"
|
||||
png_save_INCLUDES="$INCLUDES"
|
||||
|
||||
LIBS="-lpng $LIBS"
|
||||
INCLUDES="$PNG_INCLUDES $INCLUDES"
|
||||
|
||||
# Try the standard search path first
|
||||
AC_TRY_LINK([#include <png.h>],[png_access_version_number()], [
|
||||
PNG_LIBS="-lpng"
|
||||
], [
|
||||
# libpng is not in the standard search path, try $prefix
|
||||
|
||||
LIBS="-L${prefix}/lib $LIBS"
|
||||
|
||||
AC_TRY_LINK([#include <png.h>],[png_access_version_number()], [
|
||||
PNG_LIBS="-L${prefix}/lib -lpng"
|
||||
], [
|
||||
PNG_LIBS=no
|
||||
])
|
||||
])
|
||||
|
||||
LIBS="$png_save_LIBS"
|
||||
INCLUDES="$png_save_INCLUDES"
|
||||
fi
|
||||
|
||||
AC_SUBST(PNG_LIBS)
|
||||
AC_SUBST(PNG_INCLUDES)
|
||||
|
||||
# Print a helpful message
|
||||
png_libraries_result="$PNG_LIBS"
|
||||
png_includes_result="$PNG_INCLUDES"
|
||||
|
||||
if test x"$png_libraries_result" = x""; then
|
||||
png_libraries_result="in default path"
|
||||
fi
|
||||
if test x"$png_includes_result" = x""; then
|
||||
png_includes_result="in default path"
|
||||
fi
|
||||
|
||||
if test "$png_libraries_result" = "no"; then
|
||||
png_libraries_result="(none)"
|
||||
fi
|
||||
if test "$png_includes_result" = "no"; then
|
||||
png_includes_result="(none)"
|
||||
fi
|
||||
|
||||
AC_MSG_RESULT([libraries $png_libraries_result, headers $png_includes_result])
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test "$PNG_INCLUDES" != "no" && test "$PNG_LIBS" != "no"; then
|
||||
AC_DEFINE(HAVE_PNG,1,[Define if you have png libraries and header files.])
|
||||
$1
|
||||
else
|
||||
PNG_INCLUDES=""
|
||||
PNG_LIBS=""
|
||||
$2
|
||||
fi
|
||||
|
||||
])dnl
|
||||
|
||||
dnl a macro to check for ability to create python extensions
|
||||
dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE])
|
||||
dnl function also defines PYTHON_INCLUDES
|
||||
AC_DEFUN([AM_CHECK_PYTHON_HEADERS],
|
||||
[AC_REQUIRE([AM_PATH_PYTHON])
|
||||
AC_MSG_CHECKING(for headers required to compile python extensions)
|
||||
dnl deduce PYTHON_INCLUDES
|
||||
py_prefix=`$PYTHON -c "import sys; print(sys.prefix)"`
|
||||
py_exec_prefix=`$PYTHON -c "import sys; print(sys.exec_prefix)"`
|
||||
PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}"
|
||||
if test "$py_prefix" != "$py_exec_prefix"; then
|
||||
PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}"
|
||||
fi
|
||||
AC_SUBST(PYTHON_INCLUDES)
|
||||
dnl check if the headers exist:
|
||||
save_CPPFLAGS="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES"
|
||||
AC_TRY_CPP([#include <Python.h>],dnl
|
||||
[AC_MSG_RESULT(found)
|
||||
$1],dnl
|
||||
[AC_MSG_RESULT(not found)
|
||||
$2])
|
||||
CPPFLAGS="$save_CPPFLAGS"
|
||||
])
|
||||
|
||||
dnl @synopsis AC_FUNC_MKDIR
|
||||
dnl
|
||||
dnl Check whether mkdir() is mkdir or _mkdir, and whether it takes one
|
||||
dnl or two arguments.
|
||||
dnl
|
||||
dnl This macro can define HAVE_MKDIR, HAVE__MKDIR, and
|
||||
dnl MKDIR_TAKES_ONE_ARG, which are expected to be used as follows:
|
||||
dnl
|
||||
dnl #if HAVE_MKDIR
|
||||
dnl # if MKDIR_TAKES_ONE_ARG
|
||||
dnl /* MinGW32 */
|
||||
dnl # define mkdir(a, b) mkdir(a)
|
||||
dnl # endif
|
||||
dnl #else
|
||||
dnl # if HAVE__MKDIR
|
||||
dnl /* plain Windows 32 */
|
||||
dnl # define mkdir(a, b) _mkdir(a)
|
||||
dnl # else
|
||||
dnl # error "Don't know how to create a directory on this system."
|
||||
dnl # endif
|
||||
dnl #endif
|
||||
dnl
|
||||
dnl @category C
|
||||
dnl @author Alexandre Duret-Lutz <adl@gnu.org>
|
||||
dnl @version 2003-12-28
|
||||
dnl @license GPLWithACException
|
||||
|
||||
AC_DEFUN([AC_FUNC_MKDIR],
|
||||
[AC_CHECK_FUNCS([mkdir _mkdir])
|
||||
AC_CACHE_CHECK([whether mkdir takes one argument],
|
||||
[ac_cv_mkdir_takes_one_arg],
|
||||
[AC_TRY_COMPILE([
|
||||
#include <sys/stat.h>
|
||||
#if HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
], [mkdir (".");],
|
||||
[ac_cv_mkdir_takes_one_arg=yes], [ac_cv_mkdir_takes_one_arg=no])])
|
||||
if test x"$ac_cv_mkdir_takes_one_arg" = xyes; then
|
||||
AC_DEFINE([MKDIR_TAKES_ONE_ARG], 1,
|
||||
[Define if mkdir takes only one argument.])
|
||||
fi
|
||||
])
|
||||
|
||||
dnl Note:
|
||||
dnl =====
|
||||
dnl I have not implemented the following suggestion because I don't have
|
||||
dnl access to such a broken environment to test the macro. So I'm just
|
||||
dnl appending the comments here in case you have, and want to fix
|
||||
dnl AC_FUNC_MKDIR that way.
|
||||
dnl
|
||||
dnl |Thomas E. Dickey (dickey@herndon4.his.com) said:
|
||||
dnl | it doesn't cover the problem areas (compilers that mistreat mkdir
|
||||
dnl | may prototype it in dir.h and dirent.h, for instance).
|
||||
dnl |
|
||||
dnl |Alexandre:
|
||||
dnl | Would it be sufficient to check for these headers and #include
|
||||
dnl | them in the AC_TRY_COMPILE block? (and is AC_HEADER_DIRENT
|
||||
dnl | suitable for this?)
|
||||
dnl |
|
||||
dnl |Thomas:
|
||||
dnl | I think that might be a good starting point (with the set of recommended
|
||||
dnl | ifdef's and includes for AC_HEADER_DIRENT, of course).
|
||||
|
||||
|
||||
dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding
|
||||
dnl
|
||||
dnl FIND_GIFLIB[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]
|
||||
dnl ---------------------------------------------------
|
||||
dnl
|
||||
dnl Find GIFLIB libraries and headers
|
||||
dnl
|
||||
dnl Put compile stuff in GIFLIB_INCLUDES
|
||||
dnl Put link stuff in GIFLIB_LIBS
|
||||
dnl Define HAVE_GIFLIB if found.
|
||||
dnl
|
||||
AC_DEFUN([FIND_GIFLIB], [
|
||||
AC_REQUIRE([AC_PATH_XTRA])
|
||||
|
||||
GIFLIB_INCLUDES=""
|
||||
GIFLIB_LIBS=""
|
||||
|
||||
AC_ARG_WITH(giflib,
|
||||
AS_HELP_STRING([--without-giflib], [build without giflib (default: test)]))
|
||||
# Treat --without-giflib like --without-giflib-includes --without-giflib-libraries.
|
||||
if test "$with_giflib" = "no"; then
|
||||
GIFLIB_INCLUDES=no
|
||||
GIFLIB_LIBS=no
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(giflib-includes,
|
||||
AS_HELP_STRING([--with-giflib-includes=DIR], [giflib includes are in DIR]),
|
||||
GIFLIB_INCLUDES="-I$withval")
|
||||
AC_ARG_WITH(giflib-libraries,
|
||||
AS_HELP_STRING([--with-giflib-libraries=DIR], [giflib libraries are in DIR]),
|
||||
GIFLIB_LIBS="-L$withval -lgif")
|
||||
|
||||
AC_MSG_CHECKING(for giflib)
|
||||
|
||||
# Look for gif_lib.h
|
||||
if test "$GIFLIB_INCLUDES" = ""; then
|
||||
# Check the standard search path
|
||||
AC_TRY_COMPILE([#include <gif_lib.h>],[int a;],[
|
||||
GIFLIB_INCLUDES=""
|
||||
], [
|
||||
# gif_lib.h is not in the standard search path, try
|
||||
# $prefix
|
||||
giflib_save_INCLUDES="$INCLUDES"
|
||||
|
||||
INCLUDES="-I${prefix}/include $INCLUDES"
|
||||
|
||||
AC_TRY_COMPILE([#include <gif_lib.h>],[int a;],[
|
||||
GIFLIB_INCLUDES="-I${prefix}/include"
|
||||
], [
|
||||
GIFLIB_INCLUDES="no"
|
||||
])
|
||||
|
||||
INCLUDES=$giflib_save_INCLUDES
|
||||
])
|
||||
fi
|
||||
|
||||
# Now for the libraries
|
||||
if test "$GIFLIB_LIBS" = ""; then
|
||||
giflib_save_LIBS="$LIBS"
|
||||
giflib_save_INCLUDES="$INCLUDES"
|
||||
|
||||
LIBS="-lgif $LIBS"
|
||||
INCLUDES="$GIFLIB_INCLUDES $INCLUDES"
|
||||
|
||||
# Try the standard search path first
|
||||
AC_TRY_LINK([#include <gif_lib.h>],[EGifSetGifVersion(0,0)], [
|
||||
GIFLIB_LIBS="-lgif"
|
||||
], [
|
||||
# giflib is not in the standard search path, try $prefix
|
||||
|
||||
LIBS="-L${prefix}/lib $LIBS"
|
||||
|
||||
AC_TRY_LINK([#include <gif_lib.h>],[EGifSetGifVersion(0,0)], [
|
||||
GIFLIB_LIBS="-L${prefix}/lib -lgif"
|
||||
], [
|
||||
GIFLIB_LIBS=no
|
||||
])
|
||||
])
|
||||
|
||||
LIBS="$giflib_save_LIBS"
|
||||
INCLUDES="$giflib_save_INCLUDES"
|
||||
fi
|
||||
|
||||
AC_SUBST(GIFLIB_LIBS)
|
||||
AC_SUBST(GIFLIB_INCLUDES)
|
||||
|
||||
# Print a helpful message
|
||||
giflib_libraries_result="$GIFLIB_LIBS"
|
||||
giflib_includes_result="$GIFLIB_INCLUDES"
|
||||
|
||||
if test x"$giflib_libraries_result" = x""; then
|
||||
giflib_libraries_result="in default path"
|
||||
fi
|
||||
if test x"$giflib_includes_result" = x""; then
|
||||
giflib_includes_result="in default path"
|
||||
fi
|
||||
|
||||
if test "$giflib_libraries_result" = "no"; then
|
||||
giflib_libraries_result="(none)"
|
||||
fi
|
||||
if test "$giflib_includes_result" = "no"; then
|
||||
giflib_includes_result="(none)"
|
||||
fi
|
||||
|
||||
AC_MSG_RESULT([libraries $giflib_libraries_result, headers $giflib_includes_result])
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test "$GIFLIB_INCLUDES" != "no" && test "$GIFLIB_LIBS" != "no"; then
|
||||
AC_DEFINE(HAVE_GIFLIB,1,[Define if you have giflib libraries and header files.])
|
||||
$1
|
||||
else
|
||||
GIFLIB_INCLUDES=""
|
||||
GIFLIB_LIBS=""
|
||||
$2
|
||||
fi
|
||||
|
||||
])dnl
|
||||
|
|
@ -5,7 +5,15 @@
|
|||
# a bunch of cleaning up ... make certain everything will be regenerated
|
||||
rm -f Makefile Makefile.in aclocal.m4
|
||||
rm -rf autom4te.cache
|
||||
rm -f m4/*
|
||||
|
||||
# remove m4/ macros put there by libtool etc.
|
||||
rm -f m4/libtool.m4
|
||||
rm -f m4/lt~obsolete.m4
|
||||
rm -f m4/ltoptions.m4
|
||||
rm -f m4/ltsugar.m4
|
||||
rm -f m4/ltversion.m4
|
||||
rm -f m4/gtk-doc.m4
|
||||
|
||||
rm -f config.* configure depcomp
|
||||
rm -f install-sh intltool-* libtool ltmain.sh missing mkinstalldirs
|
||||
rm -f stamp-* vipsCC-7.19.pc vips-7.19.spec vips-7.19.pc
|
||||
|
@ -31,19 +39,10 @@ if [ -e $ACDIR/dirlist ]; then
|
|||
ACDIR=`cat $ACDIR/dirlist`
|
||||
fi
|
||||
|
||||
mkdir -p m4
|
||||
# glib-gettextize asks us to copy these files to m4 if they aren't there:
|
||||
files="codeset gettext glibc21 iconv isc-posix lcmessage progtest introspection"
|
||||
for dir in $ACDIR; do
|
||||
test -d $dir && for file in $files; do
|
||||
test -e $dir/$file.m4 && cp $dir/$file.m4 m4
|
||||
done
|
||||
done
|
||||
|
||||
gtkdocize --copy --docdir doc --flavour no-tmpl || exit 1
|
||||
|
||||
# some systems need libtoolize, some glibtoolize ... how annoying
|
||||
echo testing for glibtoolize ...
|
||||
printf "testing for glibtoolize ... "
|
||||
if glibtoolize --version >/dev/null 2>&1; then
|
||||
LIBTOOLIZE=glibtoolize
|
||||
echo using glibtoolize
|
||||
|
@ -57,8 +56,7 @@ test -r aclocal.m4 || touch aclocal.m4
|
|||
# it ... hopefully any errors will go to stderr and not be hidden
|
||||
glib-gettextize --force --copy > /dev/null
|
||||
test -r aclocal.m4 && chmod u+w aclocal.m4
|
||||
# intltoolize --copy --force --automake
|
||||
aclocal
|
||||
aclocal -I m4
|
||||
autoconf
|
||||
autoheader
|
||||
$LIBTOOLIZE --copy --force --automake
|
||||
|
@ -68,3 +66,5 @@ swig -version > /dev/null
|
|||
if [ $? -ne 0 ]; then
|
||||
echo you need swig to build from source control
|
||||
fi
|
||||
|
||||
./configure $*
|
458
configure.ac
458
configure.ac
|
@ -2,7 +2,7 @@
|
|||
|
||||
# also update the version number in the m4 macros below
|
||||
|
||||
AC_INIT([vips], [8.4.0], [vipsip@jiscmail.ac.uk])
|
||||
AC_INIT([vips], [8.5.0], [vipsip@jiscmail.ac.uk])
|
||||
# required for gobject-introspection
|
||||
AC_PREREQ(2.62)
|
||||
|
||||
|
@ -17,7 +17,7 @@ AC_CONFIG_MACRO_DIR([m4])
|
|||
|
||||
# user-visible library versioning
|
||||
m4_define([vips_major_version], [8])
|
||||
m4_define([vips_minor_version], [4])
|
||||
m4_define([vips_minor_version], [5])
|
||||
m4_define([vips_micro_version], [0])
|
||||
m4_define([vips_version],
|
||||
[vips_major_version.vips_minor_version.vips_micro_version])
|
||||
|
@ -37,9 +37,9 @@ VIPS_VERSION_STRING=$VIPS_VERSION-`date`
|
|||
# binary interface changes backwards compatible?: increment age
|
||||
# binary interface changes not backwards compatible?: reset age to 0
|
||||
|
||||
LIBRARY_CURRENT=46
|
||||
LIBRARY_REVISION=2
|
||||
LIBRARY_AGE=4
|
||||
LIBRARY_CURRENT=49
|
||||
LIBRARY_REVISION=0
|
||||
LIBRARY_AGE=7
|
||||
|
||||
# patched into include/vips/version.h
|
||||
AC_SUBST(VIPS_VERSION)
|
||||
|
@ -112,6 +112,7 @@ headers="\
|
|||
draw.h \
|
||||
morphology.h \
|
||||
type.h \
|
||||
rect.h \
|
||||
memory.h \
|
||||
region.h"
|
||||
|
||||
|
@ -235,7 +236,9 @@ fi
|
|||
if test x"$vips_os_darwin" = x"yes"; then
|
||||
profile_dir="/Library/ColorSync/Profiles"
|
||||
elif test x"$vips_os_win32" = x"yes"; then
|
||||
profile_dir="C:\\Windows\\System32\\spool\\drivers\\color"
|
||||
# need double escapes since this will get pasted into a #define in a C
|
||||
# header
|
||||
profile_dir="C:\\\\Windows\\\\System32\\\\spool\\\\drivers\\\\color"
|
||||
else
|
||||
profile_dir="/usr/share/color/icc"
|
||||
fi
|
||||
|
@ -260,6 +263,7 @@ AM_WITH_DMALLOC
|
|||
# without this we get something like
|
||||
# define VIPS_LIBDIR ${exec_prefix}/lib
|
||||
# argh
|
||||
test "x$prefix" = xNONE && prefix=$ac_default_prefix
|
||||
test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
|
||||
|
||||
# set $expanded_value to the fully-expanded value of the argument
|
||||
|
@ -348,30 +352,47 @@ PKG_CHECK_MODULES(REQUIRED, glib-2.0 >= 2.6 gmodule-2.0 libxml-2.0 gobject-2.0)
|
|||
PACKAGES_USED="$PACKAGES_USED glib-2.0 libxml-2.0 gmodule-2.0 gobject-2.0"
|
||||
|
||||
# after 2.28 we have a monotonic timer
|
||||
PKG_CHECK_MODULES(MONOTONIC, glib-2.0 >= 2.28,[
|
||||
AC_DEFINE(HAVE_MONOTONIC_TIME,1,[define if your glib has g_get_monotonic_time().])
|
||||
],[:
|
||||
PKG_CHECK_MODULES(MONOTONIC, glib-2.0 >= 2.28,
|
||||
[AC_DEFINE(HAVE_MONOTONIC_TIME,1,
|
||||
[define if your glib has g_get_monotonic_time().]
|
||||
)
|
||||
],
|
||||
[:
|
||||
]
|
||||
)
|
||||
|
||||
# after 2.32 there are a new set of thread functions, annoyingly
|
||||
PKG_CHECK_MODULES(THREADS, glib-2.0 >= 2.32,[
|
||||
AC_DEFINE(HAVE_MUTEX_INIT,1,[define if your glib has g_mutex_init().])
|
||||
AC_DEFINE(HAVE_COND_INIT,1,[define if your glib has g_cond_init().])
|
||||
AC_DEFINE(HAVE_THREAD_NEW,1,[define if your glib has g_thread_new().])
|
||||
AC_DEFINE(HAVE_PRIVATE_INIT,1,[define if your glib has G_PRIVATE_INIT().])
|
||||
AC_DEFINE(HAVE_VALUE_GET_SCHAR,1,[define if your glib has g_value_get_schar().])
|
||||
],[
|
||||
# the old threading system ... we need to link against gthread
|
||||
PKG_CHECK_MODULES(GTHREAD, gthread-2.0)
|
||||
PACKAGES_USED="$PACKAGES_USED gthread-2.0"
|
||||
PKG_CHECK_MODULES(THREADS, glib-2.0 >= 2.32,
|
||||
[AC_DEFINE(HAVE_MUTEX_INIT,1,[define if your glib has g_mutex_init().])
|
||||
AC_DEFINE(HAVE_COND_INIT,1,[define if your glib has g_cond_init().])
|
||||
AC_DEFINE(HAVE_THREAD_NEW,1,[define if your glib has g_thread_new().])
|
||||
AC_DEFINE(HAVE_PRIVATE_INIT,1,[define if your glib has G_PRIVATE_INIT().])
|
||||
AC_DEFINE(HAVE_VALUE_GET_SCHAR,1,
|
||||
[define if your glib has g_value_get_schar().]
|
||||
)
|
||||
],
|
||||
[# the old threading system ... we need to link against gthread
|
||||
PKG_CHECK_MODULES(GTHREAD, gthread-2.0)
|
||||
PACKAGES_USED="$PACKAGES_USED gthread-2.0"
|
||||
]
|
||||
)
|
||||
|
||||
# with 2.36 and after the type system inits itself
|
||||
PKG_CHECK_MODULES(TYPE_INIT, glib-2.0 < 2.36,[
|
||||
AC_DEFINE(NEED_TYPE_INIT,1,[define if your glib needs g_type_init().])
|
||||
],[:
|
||||
PKG_CHECK_MODULES(TYPE_INIT, glib-2.0 < 2.36,
|
||||
[AC_DEFINE(HAVE_TYPE_INIT,1,[define if your glib needs g_type_init().])
|
||||
],
|
||||
[:
|
||||
]
|
||||
)
|
||||
|
||||
# with 2.40 we have g_win32_get_command_line() on win
|
||||
PKG_CHECK_MODULES(TYPE_INIT, glib-2.0 >= 2.40,
|
||||
[if test x"$vips_os_win32" = x"yes"; then
|
||||
AC_DEFINE(HAVE_G_WIN32_GET_COMMAND_LINE,1,[define if your glib has g_win32_get_command_line().])
|
||||
have_g_win32_get_command_line=yes
|
||||
fi
|
||||
],
|
||||
[:
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -383,14 +404,30 @@ GTK_DOC_CHECK([1.14],[--flavour no-tmpl])
|
|||
AC_ARG_WITH([gsf],
|
||||
AS_HELP_STRING([--without-gsf], [build without libgsf-1 (default: test)]))
|
||||
|
||||
# libgsf-1 1.14.21 crashes, .27 is known-good, not sure about 22-26
|
||||
# libgsf-1 1.14.21 crashes
|
||||
# .27 is known to work well
|
||||
# .26 seems OK but has not been tested much
|
||||
# not sure about 22-25
|
||||
if test x"$with_gsf" != "xno"; then
|
||||
PKG_CHECK_MODULES(GSF, libgsf-1 >= 1.14.27,
|
||||
PKG_CHECK_MODULES(GSF, libgsf-1 >= 1.14.26,
|
||||
[AC_DEFINE(HAVE_GSF,1,[define if you have libgsf-1 installed.])
|
||||
with_gsf=yes
|
||||
PACKAGES_USED="$PACKAGES_USED libgsf-1"],
|
||||
PACKAGES_USED="$PACKAGES_USED libgsf-1"
|
||||
],
|
||||
[AC_MSG_WARN([libgsf-1 not found; disabling dzsave support])
|
||||
with_gsf=no])
|
||||
with_gsf=no
|
||||
]
|
||||
)
|
||||
|
||||
# zip64 and deflate-level came in .31
|
||||
PKG_CHECK_MODULES(GSF_ZIP64, libgsf-1 >= 1.14.31,
|
||||
[AC_DEFINE(HAVE_GSF_ZIP64,1,[define if your libgsf supports zip64.])
|
||||
AC_DEFINE(HAVE_GSF_DEFLATE_LEVEL,1,
|
||||
[define if your libgsf supports deflate-level.])
|
||||
],
|
||||
[:
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
AC_ARG_WITH([fftw],
|
||||
|
@ -400,54 +437,74 @@ if test x"$with_fftw" != "xno"; then
|
|||
PKG_CHECK_MODULES(FFTW, fftw3,
|
||||
[AC_DEFINE(HAVE_FFTW,1,[define if you have fftw3 installed.])
|
||||
with_fftw=yes
|
||||
PACKAGES_USED="$PACKAGES_USED fftw3"],
|
||||
PACKAGES_USED="$PACKAGES_USED fftw3"
|
||||
],
|
||||
[AC_MSG_WARN([fftw not found; disabling fftw support])
|
||||
with_fftw=no])
|
||||
with_fftw=no
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
# ImageMagick ... detect attribute iteration too
|
||||
# Optionally look for GraphicsMagick instead ... use
|
||||
# --with-magickpackage=GraphicsMagick
|
||||
# ImageMagick
|
||||
AC_ARG_WITH([magick],
|
||||
AS_HELP_STRING([--without-magick], [build without libMagic (default: test)]))
|
||||
AC_ARG_WITH([magickpackage],
|
||||
AS_HELP_STRING([--with-magickpackage],
|
||||
[magickpackage to use (default: MagickWand, ImageMagick; try GraphicsMagick to build against gm instead)]))
|
||||
[magickpackage to use (default: MagickCore; try GraphicsMagick to build against gm instead)]))
|
||||
|
||||
# recent versions of ImageMagick have split parts of the library off to
|
||||
# MagickWand, so by default we test for that first
|
||||
# set the default magick package ... very old imagemagicks called it
|
||||
# ImageMagick
|
||||
if test x"$with_magickpackage" = "x"; then
|
||||
PKG_CHECK_MODULES(MAGICK_WAND, MagickWand,
|
||||
[with_magickpackage=MagickWand],
|
||||
PKG_CHECK_MODULES(MAGICK_WAND, MagickCore,
|
||||
[with_magickpackage=MagickCore
|
||||
],
|
||||
[PKG_CHECK_MODULES(IMAGE_MAGICK, ImageMagick,
|
||||
[with_magickpackage=ImageMagick],
|
||||
[AC_MSG_WARN([neither MagickWand nor ImageMagick found; disabling Magick support])
|
||||
with_magick=no
|
||||
])])
|
||||
[with_magickpackage=ImageMagick
|
||||
],
|
||||
[AC_MSG_WARN([neither MagickCore nor ImageMagick found; disabling Magick support])
|
||||
with_magick=no
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
if test x"$with_magick" != "xno"; then
|
||||
PKG_CHECK_MODULES(MAGICK, $with_magickpackage,
|
||||
[AC_DEFINE(HAVE_MAGICK,1,[define if you have libMagick installed.])
|
||||
with_magick=yes
|
||||
PACKAGES_USED="$PACKAGES_USED $with_magickpackage"],
|
||||
[AC_MSG_WARN([$with_magickpackage not found; disabling Magick support])
|
||||
with_magick=no
|
||||
])
|
||||
# we have a separate loader for magick7 with fewer ifdef
|
||||
# options; only test for features on the magick6 case
|
||||
|
||||
if test x"$with_magick" != x"no"; then
|
||||
PKG_CHECK_MODULES(MAGICK, $with_magickpackage >= 7.0,
|
||||
[AC_DEFINE(HAVE_MAGICK7,1,[define if you have libMagick7 installed.])
|
||||
with_magick="yes (magick7)"
|
||||
magick7=yes
|
||||
PACKAGES_USED="$PACKAGES_USED $with_magickpackage"
|
||||
],
|
||||
[PKG_CHECK_MODULES(MAGICK, $with_magickpackage,
|
||||
[AC_DEFINE(HAVE_MAGICK,1,[define if you have libMagick installed.])
|
||||
with_magick="yes (magick6)"
|
||||
magick6=yes
|
||||
PACKAGES_USED="$PACKAGES_USED $with_magickpackage"
|
||||
],
|
||||
[AC_MSG_WARN([$with_magickpackage not found; disabling Magick support])
|
||||
with_magick=no
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
if test x"$with_magick" != "xno"; then
|
||||
if test x"$magick6" = x"yes"; then
|
||||
# we SetImageOption to disable some DICOM read processing, but that's only
|
||||
# in more recent imagemagicks and not in graphicsmagick
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$LIBS $MAGICK_LIBS"
|
||||
AC_CHECK_FUNCS(SetImageOption,
|
||||
AC_DEFINE(HAVE_SETIMAGEOPTION,1,
|
||||
[define if your magick has SetImageOption.]))
|
||||
AC_DEFINE(HAVE_SETIMAGEOPTION,1,[define if your magick has SetImageOption.])
|
||||
)
|
||||
LIBS="$save_LIBS"
|
||||
fi
|
||||
|
||||
if test x"$with_magick" != "xno"; then
|
||||
if test x"$magick6" = x"yes"; then
|
||||
# newer ImageMagicks use MagickCoreGenesis instead of InitializeMagick argh
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$LIBS $MAGICK_LIBS"
|
||||
|
@ -457,7 +514,7 @@ if test x"$with_magick" != "xno"; then
|
|||
LIBS="$save_LIBS"
|
||||
fi
|
||||
|
||||
if test x"$with_magick" != "xno"; then
|
||||
if test x"$magick6" = x"yes"; then
|
||||
# newer ImageMagicks use ResetImagePropertyIterator instead of
|
||||
# ResetImageAttributeIterator argh
|
||||
save_LIBS="$LIBS"
|
||||
|
@ -468,7 +525,7 @@ if test x"$with_magick" != "xno"; then
|
|||
LIBS="$save_LIBS"
|
||||
fi
|
||||
|
||||
if test x"$with_magick" != "xno"; then
|
||||
if test x"$magick6" = x"yes"; then
|
||||
# so ... do we have ResetImageAttributeIterator()? GM does not
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$LIBS $MAGICK_LIBS"
|
||||
|
@ -478,7 +535,7 @@ if test x"$with_magick" != "xno"; then
|
|||
LIBS="$save_LIBS"
|
||||
fi
|
||||
|
||||
if test x"$with_magick" != "xno"; then
|
||||
if test x"$magick6" = x"yes"; then
|
||||
# more recent magicks have GetVirtualPixels rather than GetImagePixels
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$LIBS $MAGICK_LIBS"
|
||||
|
@ -488,7 +545,7 @@ if test x"$with_magick" != "xno"; then
|
|||
LIBS="$save_LIBS"
|
||||
fi
|
||||
|
||||
if test x"$with_magick" != "xno"; then
|
||||
if test x"$magick6" = x"yes"; then
|
||||
# do we have number_scenes in image_info ... imagemagick uses this, gm
|
||||
# still uses subrange
|
||||
save_CFLAGS="$CFLAGS"
|
||||
|
@ -520,7 +577,8 @@ if test x"$with_orc" != "xno"; then
|
|||
],
|
||||
[AC_MSG_WARN([orc-0.4.11 or later not found; disabling orc support])
|
||||
with_orc=no
|
||||
])
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
# lcms ... look for lcms2 first, it has better threading support
|
||||
|
@ -531,14 +589,19 @@ if test x"$with_lcms" != "xno"; then
|
|||
PKG_CHECK_MODULES(LCMS, lcms2,
|
||||
[AC_DEFINE(HAVE_LCMS2,1,[define if you have lcms2 installed.])
|
||||
with_lcms="yes (lcms2)"
|
||||
PACKAGES_USED="$PACKAGES_USED lcms2"],
|
||||
PACKAGES_USED="$PACKAGES_USED lcms2"
|
||||
],
|
||||
[PKG_CHECK_MODULES(LCMS, lcms,
|
||||
[AC_DEFINE(HAVE_LCMS,1,[define if you have lcms installed.])
|
||||
with_lcms="yes (lcms1)"
|
||||
PACKAGES_USED="$PACKAGES_USED lcms"],
|
||||
[AC_MSG_WARN([lcms2/lcms not found; disabling lcms support])
|
||||
with_lcms=no])
|
||||
])
|
||||
[AC_DEFINE(HAVE_LCMS,1,[define if you have lcms installed.])
|
||||
with_lcms="yes (lcms1)"
|
||||
PACKAGES_USED="$PACKAGES_USED lcms"
|
||||
],
|
||||
[AC_MSG_WARN([lcms2/lcms not found; disabling lcms support])
|
||||
with_lcms=no
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
# OpenEXR
|
||||
|
@ -563,12 +626,12 @@ AC_ARG_WITH([poppler],
|
|||
AS_HELP_STRING([--without-poppler], [build without poppler (default: test)]))
|
||||
|
||||
if test x"$with_poppler" != x"no"; then
|
||||
PKG_CHECK_MODULES(POPPLER, [poppler-glib >= 0.30.0 cairo >= 1.2],
|
||||
[AC_DEFINE(HAVE_POPPLER,1,[define if you have poppler-glib >= 0.30.0 and cairo >= 1.2 installed.])
|
||||
PKG_CHECK_MODULES(POPPLER, [poppler-glib >= 0.16.0 cairo >= 1.2],
|
||||
[AC_DEFINE(HAVE_POPPLER,1,[define if you have poppler-glib >= 0.16.0 and cairo >= 1.2 installed.])
|
||||
with_poppler=yes
|
||||
PACKAGES_USED="$PACKAGES_USED poppler-glib cairo"
|
||||
],
|
||||
[AC_MSG_WARN([poppler-glib >= 0.30.0 or cairo >= 1.2 not found; disabling PDF load via poppler])
|
||||
[AC_MSG_WARN([poppler-glib >= 0.16.0 or cairo >= 1.2 not found; disabling PDF load via poppler])
|
||||
with_poppler=no
|
||||
]
|
||||
)
|
||||
|
@ -579,17 +642,40 @@ AC_ARG_WITH([rsvg],
|
|||
AS_HELP_STRING([--without-rsvg], [build without rsvg (default: test)]))
|
||||
|
||||
if test x"$with_rsvg" != x"no"; then
|
||||
PKG_CHECK_MODULES(RSVG, [librsvg-2.0 >= 2.40 cairo >= 1.2],
|
||||
[AC_DEFINE(HAVE_RSVG,1,[define if you have librsvg-2.0 >= 2.40.0 and cairo >= 1.2 installed.])
|
||||
PKG_CHECK_MODULES(RSVG, [librsvg-2.0 >= 2.34 cairo >= 1.2],
|
||||
[AC_DEFINE(HAVE_RSVG,1,[define if you have librsvg-2.0 >= 2.34.0 and cairo >= 1.2 installed.])
|
||||
with_rsvg=yes
|
||||
PACKAGES_USED="$PACKAGES_USED librsvg-2.0 cairo"
|
||||
],
|
||||
[AC_MSG_WARN([librsvg-2.0 >= 2.40.0 or cairo >= 1.2 not found; disabling SVG load via rsvg])
|
||||
[AC_MSG_WARN([librsvg-2.0 >= 2.34.0 or cairo >= 1.2 not found; disabling SVG load via rsvg])
|
||||
with_rsvg=no
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
# zlib
|
||||
# some platforms, like macosx, are missing the .pc files for zlib, so
|
||||
# we fall back to FIND_ZLIB
|
||||
AC_ARG_WITH([zlib],
|
||||
AS_HELP_STRING([--without-zlib], [build without zlib (default: test)]))
|
||||
|
||||
if test x"$with_zlib" != "xno"; then
|
||||
PKG_CHECK_MODULES(ZLIB, zlib >= 0.4,
|
||||
[AC_DEFINE(HAVE_ZLIB,1,[define if you have zlib installed.])
|
||||
with_zlib=yes
|
||||
PACKAGES_USED="$PACKAGES_USED zlib"
|
||||
],
|
||||
[FIND_ZLIB(
|
||||
[with_zlib="yes (found by search)"
|
||||
],
|
||||
[AC_MSG_WARN([zlib not found; disabling SVGZ buffer support])
|
||||
with_zlib=no
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
# OpenSlide
|
||||
AC_ARG_WITH([openslide],
|
||||
AS_HELP_STRING([--without-openslide],
|
||||
|
@ -625,10 +711,12 @@ if test x"$with_matio" != "xno"; then
|
|||
PKG_CHECK_MODULES(MATIO, matio,
|
||||
[AC_DEFINE(HAVE_MATIO,1,[define if you have matio installed.])
|
||||
with_matio=yes
|
||||
PACKAGES_USED="$PACKAGES_USED matio"],
|
||||
PACKAGES_USED="$PACKAGES_USED matio"
|
||||
],
|
||||
[AC_MSG_WARN([matio not found; disabling matio support])
|
||||
with_matio=no
|
||||
])
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
# not external libraries, but have options to disable them, helps to
|
||||
|
@ -666,13 +754,17 @@ if test x"$with_cfitsio" != "xno"; then
|
|||
PKG_CHECK_MODULES(CFITSIO, cfitsio,
|
||||
[AC_DEFINE(HAVE_CFITSIO,1,[define if you have cfitsio installed.])
|
||||
with_cfitsio=yes
|
||||
PACKAGES_USED="$PACKAGES_USED cfitsio"],
|
||||
PACKAGES_USED="$PACKAGES_USED cfitsio"
|
||||
],
|
||||
[AC_MSG_WARN([cfitsio not found; disabling cfitsio support])
|
||||
with_cfitsio=no
|
||||
])
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
# libwebp
|
||||
# some platforms, like ubuntu 12.04, are missing the .pc files for libwebp, so
|
||||
# we fall back to FIND_LIBWEBP
|
||||
AC_ARG_WITH([libwebp],
|
||||
AS_HELP_STRING([--without-libwebp], [build without libwebp (default: test)]))
|
||||
|
||||
|
@ -680,10 +772,34 @@ if test x"$with_libwebp" != "xno"; then
|
|||
PKG_CHECK_MODULES(LIBWEBP, libwebp >= 0.1.3,
|
||||
[AC_DEFINE(HAVE_LIBWEBP,1,[define if you have libwebp installed.])
|
||||
with_libwebp=yes
|
||||
PACKAGES_USED="$PACKAGES_USED libwebp"],
|
||||
[AC_MSG_WARN([libwebp not found; disabling libwebp support])
|
||||
with_libwebp=no
|
||||
])
|
||||
PACKAGES_USED="$PACKAGES_USED libwebp"
|
||||
],
|
||||
[FIND_LIBWEBP(
|
||||
[with_libwebp="yes (found by search)"
|
||||
],
|
||||
[AC_MSG_WARN([libwebp not found; disabling WEBP support])
|
||||
with_libwebp=no
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
# webp has the stuff for pulling out ICC profile etc in a separate library
|
||||
#
|
||||
# we can build with libwebpmux back to 0.3, but it's not until libwebp 0.5 that
|
||||
# we can read that metadata back successfully ... insist on 0.5 so that tests
|
||||
# can work smoothly
|
||||
if test x"$with_libwebp" != "xno"; then
|
||||
PKG_CHECK_MODULES(LIBWEBPMUX, libwebpmux >= 0.5.0,
|
||||
[AC_DEFINE(HAVE_LIBWEBPMUX,1,[define if you have libwebpmux installed.])
|
||||
with_libwebpmux=yes
|
||||
PACKAGES_USED="$PACKAGES_USED libwebpmux"
|
||||
],
|
||||
[AC_MSG_WARN([libwebpmux not found; disabling webp metadata support])
|
||||
with_libwebpmux=no
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
# pangoft2
|
||||
|
@ -699,19 +815,24 @@ if test x"$with_pangoft2" != "xno"; then
|
|||
],
|
||||
[AC_MSG_WARN([pangoft2 not found; disabling pangoft2 support])
|
||||
with_pangoft2=no
|
||||
])
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
# install vips8 python
|
||||
AC_ARG_ENABLE([pyvips8],
|
||||
AS_HELP_STRING([--enable-pyvips8],
|
||||
[install vips8 Python overrides (default: test)]),
|
||||
[enable_pyvips8=$enableval],
|
||||
[enable_pyvips8="auto"])
|
||||
[enable_pyvips8=$enableval
|
||||
],
|
||||
[enable_pyvips8="auto"
|
||||
]
|
||||
)
|
||||
|
||||
if test "x$enable_pyvips8" = "xauto"; then
|
||||
PKG_CHECK_EXISTS([pygobject-3.0 >= 3.12.0],
|
||||
[enable_pyvips8=yes],
|
||||
PKG_CHECK_EXISTS([pygobject-3.0 >= 3.13.0],
|
||||
[enable_pyvips8=yes
|
||||
],
|
||||
[AC_MSG_WARN([pygobject-3.0 not found; disabling vips8 python support])
|
||||
enable_pyvips8=no
|
||||
]
|
||||
|
@ -719,27 +840,19 @@ if test "x$enable_pyvips8" = "xauto"; then
|
|||
fi
|
||||
|
||||
if test x"$enable_pyvips8" = x"yes"; then
|
||||
AM_PATH_PYTHON(2.7,,
|
||||
JD_PATH_PYTHON(2.7,,
|
||||
[enable_pyvips8=no
|
||||
AC_MSG_WARN([Python not found; disabling vips8 Python binding])]
|
||||
AC_MSG_WARN([Python not found; disabling vips8 Python binding])
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
if test x"$enable_pyvips8" = x"yes"; then
|
||||
PKG_CHECK_MODULES(PYGOBJECT, [pygobject-3.0 >= 3.12.0])
|
||||
pyoverridesdir="\$(pyexecdir)/gi/overrides"
|
||||
AC_SUBST(pyoverridesdir)
|
||||
PKG_CHECK_MODULES(PYGOBJECT, [pygobject-3.0 >= 3.13.0])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(ENABLE_PYVIPS8, test x"$enable_pyvips8" = x"yes")
|
||||
|
||||
# hmm, these don't have .pc files on ubuntu 5.10, how odd
|
||||
FIND_ZIP(
|
||||
[with_zip=yes],
|
||||
[AC_MSG_WARN([libz not found; disabling ZIP support])
|
||||
with_zip=no
|
||||
])
|
||||
|
||||
# look for TIFF with pkg-config ... fall back to our tester
|
||||
# pkgconfig support for libtiff starts with libtiff-4
|
||||
AC_ARG_WITH([tiff],
|
||||
|
@ -749,22 +862,26 @@ if test x"$with_tiff" != "xno"; then
|
|||
PKG_CHECK_MODULES(TIFF, libtiff-4,
|
||||
[AC_DEFINE(HAVE_TIFF,1,[define if you have libtiff installed.])
|
||||
with_tiff="yes (pkg-config libtiff-4)"
|
||||
PACKAGES_USED="$PACKAGES_USED libtiff-4"],
|
||||
PACKAGES_USED="$PACKAGES_USED libtiff-4"
|
||||
],
|
||||
[FIND_TIFF(
|
||||
with_tiff="yes (found by search)",
|
||||
[AC_MSG_WARN([libtiff not found; disabling TIFF support])
|
||||
with_tiff=no
|
||||
])
|
||||
])
|
||||
with_tiff="yes (found by search)",
|
||||
[AC_MSG_WARN([libtiff not found; disabling TIFF support])
|
||||
with_tiff=no
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
# giflib
|
||||
FIND_GIFLIB([
|
||||
with_giflib="yes (found by search)"
|
||||
], [
|
||||
AC_MSG_WARN([giflib not found; disabling direct GIF support])
|
||||
with_giflib=no
|
||||
])
|
||||
FIND_GIFLIB(
|
||||
[with_giflib="yes (found by search)"
|
||||
],
|
||||
[AC_MSG_WARN([giflib not found; disabling direct GIF support])
|
||||
with_giflib=no
|
||||
]
|
||||
)
|
||||
|
||||
# look for PNG with pkg-config ... fall back to our tester
|
||||
AC_ARG_WITH([png],
|
||||
|
@ -774,21 +891,27 @@ if test x"$with_png" != "xno"; then
|
|||
PKG_CHECK_MODULES(PNG, libpng >= 1.2.9,
|
||||
[AC_DEFINE(HAVE_PNG,1,[define if you have libpng installed.])
|
||||
with_png="yes (pkg-config libpng >= 1.2.9)"
|
||||
PACKAGES_USED="$PACKAGES_USED libpng"],
|
||||
PACKAGES_USED="$PACKAGES_USED libpng"
|
||||
],
|
||||
[FIND_PNG(
|
||||
with_png="yes (found by search)",
|
||||
[AC_MSG_WARN([libpng not found; disabling PNG support])
|
||||
with_png=no
|
||||
])
|
||||
])
|
||||
[with_png="yes (found by search)"
|
||||
],
|
||||
[AC_MSG_WARN([libpng not found; disabling PNG support])
|
||||
with_png=no
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
FIND_JPEG(
|
||||
[with_jpeg=yes
|
||||
EXTRA_LIBS_USED="$EXTRA_LIBS_USED -ljpeg"],
|
||||
EXTRA_LIBS_USED="$EXTRA_LIBS_USED -ljpeg"
|
||||
],
|
||||
[AC_MSG_WARN([libjpeg not found; disabling JPEG support])
|
||||
with_jpeg=no
|
||||
])
|
||||
]
|
||||
)
|
||||
|
||||
# JPEG extension parameters available in libjpeg-turbo >=1.5.0, mozjpeg >=3.0
|
||||
if test x"$with_jpeg" != "xno"; then
|
||||
|
@ -808,10 +931,12 @@ if test x"$with_libexif" != "xno"; then
|
|||
PKG_CHECK_MODULES(EXIF, libexif >= 0.6,
|
||||
[AC_DEFINE(HAVE_EXIF,1,[define if you have libexif >= 0.6 installed.])
|
||||
with_libexif=yes
|
||||
PACKAGES_USED="$PACKAGES_USED libexif"],
|
||||
PACKAGES_USED="$PACKAGES_USED libexif"
|
||||
],
|
||||
[AC_MSG_WARN([libexif >= 0.6 not found; disabling exif support])
|
||||
with_libexif=no
|
||||
])
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
# some libexif packages need include <libexif/poop.h>, some just <poop.h>
|
||||
|
@ -839,16 +964,28 @@ if test x"$with_python" != x"no"; then
|
|||
fi
|
||||
|
||||
if test x"$with_python" != x"no"; then
|
||||
AM_PATH_PYTHON(2.7,,
|
||||
JD_PATH_PYTHON(2.7,,
|
||||
[with_python=no
|
||||
AC_MSG_WARN([Python not found; disabling vips7 Python binding])]
|
||||
AC_MSG_WARN([Python >= 2.7 not found; disabling vips7 Python binding])
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
if test x"$with_python" != x"no"; then
|
||||
# The SWIG bindings don't compile on python3 (see issue #334).
|
||||
AM_PYTHON_CHECK_VERSION([$PYTHON], [3.0],
|
||||
[with_python=no
|
||||
AC_MSG_WARN([Python >= 3.0 found; disabling vips7 Python binding])
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
if test x"$with_python" != x"no"; then
|
||||
AM_CHECK_PYTHON_HEADERS(,
|
||||
[with_python=no
|
||||
AC_MSG_WARN([Python headers not found; disabling vips7 Python binding])])
|
||||
AC_MSG_WARN([Python headers not found; disabling vips7 Python binding])
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
# we don't check for swig: we include the generated bindings in the
|
||||
|
@ -864,14 +1001,14 @@ fi
|
|||
# Gather all up for VIPS_CFLAGS, VIPS_INCLUDES, VIPS_LIBS
|
||||
# sort includes to get longer, more specific dirs first
|
||||
# helps, for example, selecting graphicsmagick over imagemagick
|
||||
VIPS_CFLAGS=`for i in $VIPS_CFLAGS $GTHREAD_CFLAGS $REQUIRED_CFLAGS $PANGOFT2_CFLAGS $GSF_CFLAGS $FFTW_CFLAGS $MAGICK_CFLAGS $PNG_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $LIBWEBP_CFLAGS $GIFLIB_INCLUDES $RSVG_CFLAGS $POPPLER_CFLAGS $OPENEXR_CFLAGS $OPENSLIDE_CFLAGS $ORC_CFLAGS $TIFF_CFLAGS $LCMS_CFLAGS
|
||||
VIPS_CFLAGS=`for i in $VIPS_CFLAGS $GTHREAD_CFLAGS $REQUIRED_CFLAGS $ZLIB_CFLAGS $PANGOFT2_CFLAGS $GSF_CFLAGS $FFTW_CFLAGS $MAGICK_CFLAGS $PNG_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $LIBWEBP_CFLAGS $LIBWEBPMUX_CFLAGS $GIFLIB_INCLUDES $RSVG_CFLAGS $POPPLER_CFLAGS $OPENEXR_CFLAGS $OPENSLIDE_CFLAGS $ORC_CFLAGS $TIFF_CFLAGS $LCMS_CFLAGS
|
||||
do
|
||||
echo $i
|
||||
done | sort -ru`
|
||||
VIPS_CFLAGS=`echo $VIPS_CFLAGS`
|
||||
VIPS_CFLAGS="$VIPS_DEBUG_FLAGS $VIPS_CFLAGS"
|
||||
VIPS_INCLUDES="$PNG_INCLUDES $TIFF_INCLUDES $ZIP_INCLUDES $JPEG_INCLUDES"
|
||||
VIPS_LIBS="$MAGICK_LIBS $PNG_LIBS $TIFF_LIBS $ZIP_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $PANGOFT2_LIBS $GSF_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $GIFLIB_LIBS $RSVG_LIBS $POPPLER_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $LIBWEBP_LIBS $MATIO_LIBS $EXIF_LIBS -lm"
|
||||
VIPS_INCLUDES="$ZLIB_INCLUDES $PNG_INCLUDES $TIFF_INCLUDES $JPEG_INCLUDES"
|
||||
VIPS_LIBS="$ZLIB_LIBS $MAGICK_LIBS $PNG_LIBS $TIFF_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $PANGOFT2_LIBS $GSF_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $GIFLIB_LIBS $RSVG_LIBS $POPPLER_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $LIBWEBP_LIBS $LIBWEBPMUX_LIBS $MATIO_LIBS $EXIF_LIBS -lm"
|
||||
|
||||
AC_SUBST(VIPS_LIBDIR)
|
||||
|
||||
|
@ -923,6 +1060,9 @@ AC_OUTPUT([
|
|||
tools/light_correct
|
||||
tools/shrink_width
|
||||
python/Makefile
|
||||
python/packages/Makefile
|
||||
python/packages/gi/Makefile
|
||||
python/packages/gi/overrides/Makefile
|
||||
test/Makefile
|
||||
test/variables.sh
|
||||
swig/Makefile
|
||||
|
@ -933,7 +1073,7 @@ AC_OUTPUT([
|
|||
po/Makefile.in
|
||||
])
|
||||
|
||||
AC_MSG_RESULT([
|
||||
AC_MSG_RESULT([dnl
|
||||
* build options
|
||||
native win32: $vips_os_win32
|
||||
native OS X: $vips_os_darwin
|
||||
|
@ -944,7 +1084,7 @@ build docs with gtkdoc: $enable_gtk_doc
|
|||
gobject introspection: $found_introspection
|
||||
build vips7 Python binding: $with_python
|
||||
install vips8 Python overrides: $enable_pyvips8
|
||||
(requires pygobject-3.12.0 or later)
|
||||
(requires pygobject-3.13.0 or later)
|
||||
build radiance support: $with_radiance
|
||||
build analyze support: $with_analyze
|
||||
build PPM support: $with_ppm
|
||||
|
@ -961,12 +1101,15 @@ file import with OpenSlide: $with_openslide
|
|||
(requires openslide-3.3.0 or later)
|
||||
file import with matio: $with_matio
|
||||
PDF import with poppler-glib: $with_poppler
|
||||
(requires poppler-glib 0.30.0 or later)
|
||||
(requires poppler-glib 0.16.0 or later)
|
||||
SVG import with librsvg-2.0: $with_rsvg
|
||||
(requires librsvg-2.0 2.40.0 or later)
|
||||
(requires librsvg-2.0 2.34.0 or later)
|
||||
zlib: $with_zlib
|
||||
file import with cfitsio: $with_cfitsio
|
||||
file import/export with libwebp: $with_libwebp
|
||||
(requires libwebp-0.1.3 or later)
|
||||
support webp metadata: $with_libwebpmux
|
||||
(requires libwebpmux-0.5 or later)
|
||||
text rendering with pangoft2: $with_pangoft2
|
||||
file import/export with libpng: $with_png
|
||||
(requires libpng-1.2.9 or later)
|
||||
|
@ -974,6 +1117,65 @@ file import/export with libtiff: $with_tiff
|
|||
file import/export with giflib: $with_giflib
|
||||
file import/export with libjpeg: $with_jpeg
|
||||
image pyramid export: $with_gsf
|
||||
(requires libgsf-1 1.14.27 or later)
|
||||
(requires libgsf-1 1.14.26 or later)
|
||||
use libexif to load/save JPEG metadata: $with_libexif
|
||||
])
|
||||
|
||||
if test x"$found_introspection" = xyes -a "$VIPS_LIBDIR/girepository-1.0" != "$INTROSPECTION_TYPELIBDIR"; then
|
||||
case "$VIPS_LIBDIR" in
|
||||
/usr/local/Cellar/vips/*)
|
||||
;; # ignore for homebrew
|
||||
*)
|
||||
AC_MSG_RESULT([dnl
|
||||
Vips-8.0.typelib will install to $VIPS_LIBDIR/girepository-1.0, but your
|
||||
system repository seems to be $INTROSPECTION_TYPELIBDIR.
|
||||
You may need to add this directory to your typelib path, for example:
|
||||
|
||||
export GI_TYPELIB_PATH="$VIPS_LIBDIR/girepository-1.0"
|
||||
])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x"$enable_pyvips8" = xyes; then
|
||||
expand $pyexecdir
|
||||
VIPS_PYEXECDIR=$expanded_value
|
||||
case "$VIPS_PYEXECDIR" in
|
||||
/usr/local/Cellar/vips/*)
|
||||
;; # ingnore for homebrew
|
||||
*)
|
||||
syspygipath=`$PYTHON -c "
|
||||
import sys
|
||||
sys.path.append('$VIPS_PYEXECDIR')
|
||||
try: import gi; print(gi._overridesdir)
|
||||
except: pass"`
|
||||
if test x"$syspygipath" = x; then
|
||||
AC_MSG_RESULT([dnl
|
||||
Your python gi module could not be loaded.
|
||||
|
||||
You should change your PYTHONPATH environment
|
||||
variable to include the pygobject3 gi module
|
||||
and re-run configure to check if the Vips.py
|
||||
overrides are installed in the correct location.
|
||||
])
|
||||
elif test x"$VIPS_PYEXECDIR/gi/overrides" != x"$syspygipath"; then
|
||||
AC_MSG_RESULT([dnl
|
||||
The vips Python overrides file will install to
|
||||
$VIPS_PYEXECDIR/gi/overrides/Vips.py, but your
|
||||
system gi overrides seem to be $syspygipath.
|
||||
You may need to copy this file, for example:
|
||||
|
||||
cp $VIPS_PYEXECDIR/gi/overrides/Vips.* $syspygipath
|
||||
])
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x"$vips_os_win32" = x"yes"; then
|
||||
if test x"$have_g_win32_get_command_line" != x"yes"; then
|
||||
AC_MSG_RESULT([dnl
|
||||
Your glib is too old, vips will not support unicode command-line arguments.
|
||||
])
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
*
|
||||
* 30/12/14
|
||||
* - allow set enum value from string
|
||||
* 10/6/16
|
||||
* - missing implementation of VImage::write()
|
||||
* 11/6/16
|
||||
* - added arithmetic assignment overloads, += etc.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -346,7 +350,7 @@ set_property( VipsObject *object, const char *name, const GValue *value )
|
|||
|
||||
if( vips_object_get_argument( object, name,
|
||||
&pspec, &argument_class, &argument_instance ) ) {
|
||||
vips_warn( NULL, "%s", vips_error_buffer() );
|
||||
g_warning( "%s", vips_error_buffer() );
|
||||
vips_error_clear();
|
||||
return;
|
||||
}
|
||||
|
@ -360,7 +364,7 @@ set_property( VipsObject *object, const char *name, const GValue *value )
|
|||
|
||||
if( (enum_value = vips_enum_from_nick( object_class->nickname,
|
||||
pspec_type, g_value_get_string( value ) )) < 0 ) {
|
||||
vips_warn( NULL, "%s", vips_error_buffer() );
|
||||
g_warning( "%s", vips_error_buffer() );
|
||||
vips_error_clear();
|
||||
return;
|
||||
}
|
||||
|
@ -457,7 +461,7 @@ VImage::call_option_string( const char *operation_name,
|
|||
{
|
||||
VipsOperation *operation;
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_call_by_name: starting for %s ...\n",
|
||||
VIPS_DEBUG_MSG( "call_option_string: starting for %s ...\n",
|
||||
operation_name );
|
||||
|
||||
if( !(operation = vips_operation_new( operation_name )) ) {
|
||||
|
@ -485,6 +489,7 @@ VImage::call_option_string( const char *operation_name,
|
|||
*/
|
||||
if( vips_cache_operation_buildp( &operation ) ) {
|
||||
vips_object_unref_outputs( VIPS_OBJECT( operation ) );
|
||||
g_object_unref( operation );
|
||||
delete options;
|
||||
throw( VError() );
|
||||
}
|
||||
|
@ -566,7 +571,7 @@ VImage::new_from_image( std::vector<double> pixel )
|
|||
VImage onepx = VImage::black( 1, 1,
|
||||
VImage::option()->set( "bands", bands() ) );
|
||||
|
||||
onepx = onepx.linear( to_vectorv( 1, 1.0 ), pixel ).cast( format() );
|
||||
onepx = (onepx + pixel).cast( format() );
|
||||
|
||||
VImage big = onepx.embed( 0, 0, width(), height(),
|
||||
VImage::option()->set( "extend", VIPS_EXTEND_COPY ) );
|
||||
|
@ -612,6 +617,15 @@ VImage::new_matrixv( int width, int height, ... )
|
|||
return( matrix );
|
||||
}
|
||||
|
||||
VImage
|
||||
VImage::write( VImage out )
|
||||
{
|
||||
if( vips_image_write( this->get_image(), out.get_image() ) )
|
||||
throw VError();
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
void
|
||||
VImage::write_to_file( const char *name, VOption *options )
|
||||
{
|
||||
|
@ -755,13 +769,32 @@ operator+( VImage a, std::vector<double> b )
|
|||
return( a.linear( 1.0, b ) );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator+=( VImage a, const VImage b )
|
||||
{
|
||||
return( a = a + b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator+=( VImage a, const double b )
|
||||
{
|
||||
return( a = a + b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator+=( VImage a, std::vector<double> b )
|
||||
{
|
||||
return( a = a + b );
|
||||
}
|
||||
|
||||
VImage
|
||||
operator-( VImage a, VImage b )
|
||||
{
|
||||
return( a.subtract( b ) );
|
||||
}
|
||||
|
||||
VImage operator-( double a, VImage b )
|
||||
VImage
|
||||
operator-( double a, VImage b )
|
||||
{
|
||||
return( b.linear( -1.0, a ) );
|
||||
}
|
||||
|
@ -784,6 +817,24 @@ operator-( VImage a, std::vector<double> b )
|
|||
return( a.linear( 1.0, vips::negate( b ) ) );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator-=( VImage a, const VImage b )
|
||||
{
|
||||
return( a = a - b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator-=( VImage a, const double b )
|
||||
{
|
||||
return( a = a - b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator-=( VImage a, std::vector<double> b )
|
||||
{
|
||||
return( a = a - b );
|
||||
}
|
||||
|
||||
VImage
|
||||
operator-( VImage a )
|
||||
{
|
||||
|
@ -820,6 +871,24 @@ operator*( VImage a, std::vector<double> b )
|
|||
return( a.linear( b, 0.0 ) );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator*=( VImage a, const VImage b )
|
||||
{
|
||||
return( a = a * b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator*=( VImage a, const double b )
|
||||
{
|
||||
return( a = a * b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator*=( VImage a, std::vector<double> b )
|
||||
{
|
||||
return( a = a * b );
|
||||
}
|
||||
|
||||
VImage
|
||||
operator/( VImage a, VImage b )
|
||||
{
|
||||
|
@ -850,6 +919,24 @@ operator/( VImage a, std::vector<double> b )
|
|||
return( a.linear( vips::invert( b ), 0.0 ) );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator/=( VImage a, const VImage b )
|
||||
{
|
||||
return( a = a / b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator/=( VImage a, const double b )
|
||||
{
|
||||
return( a = a / b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator/=( VImage a, std::vector<double> b )
|
||||
{
|
||||
return( a = a / b );
|
||||
}
|
||||
|
||||
VImage
|
||||
operator%( VImage a, VImage b )
|
||||
{
|
||||
|
@ -868,6 +955,24 @@ operator%( VImage a, std::vector<double> b )
|
|||
return( a.remainder_const( b ) );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator%=( VImage a, const VImage b )
|
||||
{
|
||||
return( a = a % b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator%=( VImage a, const double b )
|
||||
{
|
||||
return( a = a % b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator%=( VImage a, std::vector<double> b )
|
||||
{
|
||||
return( a = a % b );
|
||||
}
|
||||
|
||||
VImage
|
||||
operator<( VImage a, VImage b )
|
||||
{
|
||||
|
@ -1104,6 +1209,24 @@ operator&( VImage a, std::vector<double> b )
|
|||
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_AND ) );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator&=( VImage a, const VImage b )
|
||||
{
|
||||
return( a = a & b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator&=( VImage a, const double b )
|
||||
{
|
||||
return( a = a & b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator&=( VImage a, std::vector<double> b )
|
||||
{
|
||||
return( a = a & b );
|
||||
}
|
||||
|
||||
VImage
|
||||
operator|( VImage a, VImage b )
|
||||
{
|
||||
|
@ -1136,6 +1259,24 @@ operator|( VImage a, std::vector<double> b )
|
|||
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_OR ) );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator|=( VImage a, const VImage b )
|
||||
{
|
||||
return( a = a | b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator|=( VImage a, const double b )
|
||||
{
|
||||
return( a = a | b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator|=( VImage a, std::vector<double> b )
|
||||
{
|
||||
return( a = a | b );
|
||||
}
|
||||
|
||||
VImage
|
||||
operator^( VImage a, VImage b )
|
||||
{
|
||||
|
@ -1168,6 +1309,24 @@ operator^( VImage a, std::vector<double> b )
|
|||
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_EOR ) );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator^=( VImage a, const VImage b )
|
||||
{
|
||||
return( a = a ^ b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator^=( VImage a, const double b )
|
||||
{
|
||||
return( a = a ^ b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator^=( VImage a, std::vector<double> b )
|
||||
{
|
||||
return( a = a ^ b );
|
||||
}
|
||||
|
||||
VImage
|
||||
operator<<( VImage a, VImage b )
|
||||
{
|
||||
|
@ -1187,6 +1346,24 @@ operator<<( VImage a, std::vector<double> b )
|
|||
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_LSHIFT ) );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator<<=( VImage a, const VImage b )
|
||||
{
|
||||
return( a = a << b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator<<=( VImage a, const double b )
|
||||
{
|
||||
return( a = a << b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator<<=( VImage a, std::vector<double> b )
|
||||
{
|
||||
return( a = a << b );
|
||||
}
|
||||
|
||||
VImage
|
||||
operator>>( VImage a, VImage b )
|
||||
{
|
||||
|
@ -1206,4 +1383,22 @@ operator>>( VImage a, std::vector<double> b )
|
|||
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_RSHIFT ) );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator>>=( VImage a, const VImage b )
|
||||
{
|
||||
return( a = a << b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator>>=( VImage a, const double b )
|
||||
{
|
||||
return( a = a << b );
|
||||
}
|
||||
|
||||
VImage &
|
||||
operator>>=( VImage a, std::vector<double> b )
|
||||
{
|
||||
return( a = a << b );
|
||||
}
|
||||
|
||||
VIPS_NAMESPACE_END
|
||||
|
|
|
@ -34,8 +34,7 @@ equal_vector( std::vector<double> a, std::vector<double> b )
|
|||
printf( "%g", a[i] );
|
||||
}
|
||||
printf( "], is [" );
|
||||
for( unsigned int i = 0; i < a.size(); i++ ) {
|
||||
if( i > 0 )
|
||||
for( unsigned int i = 0; i < a.size(); i++ ) { if( i > 0 )
|
||||
printf( ", " );
|
||||
printf( "%g", a[i] );
|
||||
}
|
||||
|
@ -70,12 +69,12 @@ equal_double( double a, double b )
|
|||
void \
|
||||
test_binary_##OPERATOR( VImage left, VImage right ) \
|
||||
{ \
|
||||
for( int x = 10; x < 30; x += 10 ) { \
|
||||
std::vector<double> p_left = left.getpoint( x, x ); \
|
||||
std::vector<double> p_right = right.getpoint( x, x ); \
|
||||
std::vector<double> p_result = \
|
||||
OPERATOR<std::vector<double>, \
|
||||
std::vector<double>, \
|
||||
for( int x = 10; x < 30; x += 10 ) { \
|
||||
std::vector<double> p_left = left.getpoint( x, x ); \
|
||||
std::vector<double> p_right = right.getpoint( x, x ); \
|
||||
std::vector<double> p_result = \
|
||||
OPERATOR<std::vector<double>, \
|
||||
std::vector<double>, \
|
||||
std::vector<double> >(p_left, p_right ); \
|
||||
\
|
||||
VImage im_result; \
|
||||
|
@ -111,7 +110,7 @@ test_binary_##OPERATOR( VImage left, VImage right ) \
|
|||
*/ \
|
||||
im_result = \
|
||||
OPERATOR<VImage, std::vector<double>, \
|
||||
VImage>( p_left, right ); \
|
||||
VImage>( p_left, right ); \
|
||||
p_im_result = im_result.getpoint( x, x ); \
|
||||
\
|
||||
if( !equal_vector( p_result, p_im_result ) ) { \
|
||||
|
@ -138,7 +137,7 @@ test_binary_##OPERATOR( VImage left, VImage right ) \
|
|||
} \
|
||||
} \
|
||||
\
|
||||
/* test: image = double OP image \
|
||||
/* test: image = double OP image \
|
||||
*/ \
|
||||
for( unsigned int i = 0; i < p_left.size(); i++ ) { \
|
||||
im_result = \
|
||||
|
@ -166,7 +165,7 @@ A test_add( B left, C right )
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> operator+(std::vector<T> &v1, const std::vector<T> &v2)
|
||||
std::vector<T> operator+( std::vector<T> &v1, const std::vector<T> &v2 )
|
||||
{
|
||||
std::vector<T> result( v1.size() );
|
||||
|
||||
|
@ -185,7 +184,7 @@ A test_subtract( B left, C right )
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> operator-(std::vector<T> &v1, const std::vector<T> &v2)
|
||||
std::vector<T> operator-( std::vector<T> &v1, const std::vector<T> &v2 )
|
||||
{
|
||||
std::vector<T> result( v1.size() );
|
||||
|
||||
|
@ -204,7 +203,7 @@ A test_multiply( B left, C right )
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> operator*(std::vector<T> &v1, const std::vector<T> &v2)
|
||||
std::vector<T> operator*( std::vector<T> &v1, const std::vector<T> &v2 )
|
||||
{
|
||||
std::vector<T> result( v1.size() );
|
||||
|
||||
|
@ -223,7 +222,7 @@ A test_divide( B left, C right )
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> operator/(std::vector<T> &v1, const std::vector<T> &v2)
|
||||
std::vector<T> operator/( std::vector<T> &v1, const std::vector<T> &v2 )
|
||||
{
|
||||
std::vector<T> result( v1.size() );
|
||||
|
||||
|
@ -235,6 +234,135 @@ std::vector<T> operator/(std::vector<T> &v1, const std::vector<T> &v2)
|
|||
|
||||
TEST_BINARY( test_divide );
|
||||
|
||||
/* We can't test remainder easily, vips does not support constant % image.
|
||||
*/
|
||||
|
||||
/* We'd need an int version to test the bool operators, C++ does not like
|
||||
* double & double.
|
||||
*/
|
||||
|
||||
/* Only test a few points and only test uchar: we are just testing the C++
|
||||
* overloads, we rely on the python test suite for testing the underlying
|
||||
* vips operators.
|
||||
*/
|
||||
#define TEST_ASSIGNMENT( OPERATOR ) \
|
||||
void \
|
||||
test_assignment_##OPERATOR( VImage left, VImage right ) \
|
||||
{ \
|
||||
for( int x = 10; x < 30; x += 10 ) { \
|
||||
std::vector<double> p_left = left.getpoint( x, x ); \
|
||||
std::vector<double> p_right = right.getpoint( x, x ); \
|
||||
std::vector<double> p_result = p_left; \
|
||||
OPERATOR<std::vector<double>, \
|
||||
std::vector<double> >( p_result, p_right ); \
|
||||
\
|
||||
/* test: image OP= image \
|
||||
*/ \
|
||||
VImage im_result = left; \
|
||||
OPERATOR<VImage, VImage>( im_result, right ); \
|
||||
std::vector<double> p_im_result = im_result.getpoint( x, x ); \
|
||||
\
|
||||
if( !equal_vector( p_result, p_im_result ) ) { \
|
||||
printf( #OPERATOR \
|
||||
"(VImage, VImage) failed at (%d, %d)\n", \
|
||||
x, x ); \
|
||||
abort(); \
|
||||
} \
|
||||
\
|
||||
/* test: image OP= vec \
|
||||
*/ \
|
||||
im_result = left; \
|
||||
OPERATOR<VImage, std::vector<double> >( im_result, p_right ); \
|
||||
p_im_result = im_result.getpoint( x, x ); \
|
||||
\
|
||||
if( !equal_vector( p_result, p_im_result ) ) { \
|
||||
printf( #OPERATOR \
|
||||
"(VImage, vector) failed at (%d, %d)\n", \
|
||||
x, x ); \
|
||||
abort(); \
|
||||
} \
|
||||
\
|
||||
\
|
||||
/* test: image OP= double \
|
||||
*/ \
|
||||
for( unsigned int i = 0; i < p_left.size(); i++ ) { \
|
||||
im_result = left; \
|
||||
OPERATOR<VImage, double>( im_result, p_right[i] ); \
|
||||
p_im_result = im_result.getpoint( x, x ); \
|
||||
\
|
||||
if( !equal_double( p_result[i], p_im_result[i] ) ) { \
|
||||
printf( #OPERATOR \
|
||||
"(VImage, double) failed at " \
|
||||
"(%d, %d)\n", \
|
||||
x, x ); \
|
||||
abort(); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> & operator+=( std::vector<T> & a, std::vector<T> b )
|
||||
{
|
||||
return( a = a + b );
|
||||
}
|
||||
|
||||
template <typename A, typename B>
|
||||
void test_plusequals( A &left, B right )
|
||||
{
|
||||
left += right;
|
||||
}
|
||||
|
||||
TEST_ASSIGNMENT( test_plusequals );
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> & operator-=( std::vector<T> & a, std::vector<T> b )
|
||||
{
|
||||
return( a = a - b );
|
||||
}
|
||||
|
||||
template <typename A, typename B>
|
||||
void test_minusequals( A &left, B right )
|
||||
{
|
||||
left -= right;
|
||||
}
|
||||
|
||||
TEST_ASSIGNMENT( test_minusequals );
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> & operator*=( std::vector<T> & a, std::vector<T> b )
|
||||
{
|
||||
return( a = a * b );
|
||||
}
|
||||
|
||||
template <typename A, typename B>
|
||||
void test_timesequals( A &left, B right )
|
||||
{
|
||||
left *= right;
|
||||
}
|
||||
|
||||
TEST_ASSIGNMENT( test_timesequals );
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> & operator/=( std::vector<T> & a, std::vector<T> b )
|
||||
{
|
||||
return( a = a / b );
|
||||
}
|
||||
|
||||
template <typename A, typename B>
|
||||
void test_divideequals( A &left, B right )
|
||||
{
|
||||
left /= right;
|
||||
}
|
||||
|
||||
TEST_ASSIGNMENT( test_divideequals );
|
||||
|
||||
/* We can't test remainder easily, vips does not support constant % image.
|
||||
*/
|
||||
|
||||
/* We'd need an int version to test the bool operators.
|
||||
*/
|
||||
|
||||
int
|
||||
main( int argc, char **argv )
|
||||
{
|
||||
|
@ -266,14 +394,18 @@ main( int argc, char **argv )
|
|||
VImage left = VImage::new_from_file( argv[1] );
|
||||
VImage right = VImage::new_from_file( argv[2] );
|
||||
|
||||
VImage band_one = left[1];
|
||||
std::vector<double> point = left(0, 0);
|
||||
|
||||
test_binary_test_add( left, right );
|
||||
test_binary_test_subtract( left, right );
|
||||
test_binary_test_multiply( left, right );
|
||||
test_binary_test_divide( left, right );
|
||||
|
||||
VImage band_one = left[1];
|
||||
|
||||
std::vector<double> point = left(0, 0);
|
||||
test_assignment_test_plusequals( left, right );
|
||||
test_assignment_test_minusequals( left, right );
|
||||
test_assignment_test_timesequals( left, right );
|
||||
test_assignment_test_divideequals( left, right );
|
||||
}
|
||||
|
||||
vips_shutdown();
|
||||
|
|
|
@ -22,6 +22,8 @@ import re
|
|||
import logging
|
||||
#logging.basicConfig(level = logging.DEBUG)
|
||||
|
||||
import gi
|
||||
gi.require_version('Vips', '8.0')
|
||||
from gi.repository import Vips, GObject
|
||||
|
||||
vips_type_image = GObject.GType.from_name("VipsImage")
|
||||
|
|
|
@ -471,7 +471,7 @@ public:
|
|||
VImage new_from_image( std::vector<double> pixel );
|
||||
VImage new_from_image( double pixel );
|
||||
|
||||
void write( VImage out );
|
||||
VImage write( VImage out );
|
||||
|
||||
void write_to_file( const char *name, VOption *options = 0 );
|
||||
|
||||
|
@ -751,19 +751,19 @@ public:
|
|||
}
|
||||
|
||||
VImage
|
||||
ifthenelse( double th, VImage el, VOption *options )
|
||||
ifthenelse( double th, VImage el, VOption *options = 0 )
|
||||
{
|
||||
return( ifthenelse( to_vector( th ), el, options ) );
|
||||
}
|
||||
|
||||
VImage
|
||||
ifthenelse( VImage th, double el, VOption *options )
|
||||
ifthenelse( VImage th, double el, VOption *options = 0 )
|
||||
{
|
||||
return( ifthenelse( th, to_vector( el ), options ) );
|
||||
}
|
||||
|
||||
VImage
|
||||
ifthenelse( double th, double el, VOption *options )
|
||||
ifthenelse( double th, double el, VOption *options = 0 )
|
||||
{
|
||||
return( ifthenelse( to_vector( th ), to_vector( el ),
|
||||
options ) );
|
||||
|
@ -775,97 +775,137 @@ public:
|
|||
|
||||
std::vector<double> operator()( int x, int y );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator+( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator+( double a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator+( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator+( std::vector<double> a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator+( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator+( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator+( double a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator+( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator+( std::vector<double> a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator+( VImage a, std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator-( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator-( double a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator-( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator-( std::vector<double> a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator-( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator+=( VImage a, const VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator+=( VImage a, const double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator+=( VImage a, const std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator-( VImage a );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator-( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator-( double a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator-( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator-( std::vector<double> a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator-( VImage a, std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator*( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator*( double a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator*( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator*( std::vector<double> a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator*( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator-=( VImage a, const VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator-=( VImage a, const double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator-=( VImage a, const std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator/( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator/( double a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator/( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator/( std::vector<double> a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator/( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator-( VImage a );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator%( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator%( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator%( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator*( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator*( double a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator*( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator*( std::vector<double> a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator*( VImage a, std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator<( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator<( double a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator<( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator<( std::vector<double> a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator<( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator*=( VImage a, const VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator*=( VImage a, const double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator*=( VImage a, const std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator<=( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator<=( double a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator<=( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator<=( std::vector<double> a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator<=( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator/( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator/( double a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator/( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator/( std::vector<double> a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator/( VImage a, std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator>( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator>( double a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator>( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator>( std::vector<double> a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator>( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator/=( VImage a, const VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator/=( VImage a, const double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator/=( VImage a, const std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator>=( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator>=( double a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator>=( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator>=( std::vector<double> a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator>=( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator%( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator%( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator%( VImage a, std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator==( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator==( double a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator==( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator==( std::vector<double> a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator==( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator%=( VImage a, const VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator%=( VImage a, const double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator%=( VImage a, const std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator!=( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator!=( double a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator!=( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator!=( std::vector<double> a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator!=( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator<( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator<( double a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator<( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator<( std::vector<double> a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator<( VImage a, std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator&( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator&( double a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator&( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator&( std::vector<double> a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator&( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator<=( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator<=( double a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator<=( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator<=( std::vector<double> a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator<=( VImage a, std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator|( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator|( double a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator|( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator|( std::vector<double> a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator|( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator>( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator>( double a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator>( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator>( std::vector<double> a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator>( VImage a, std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator^( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator^( double a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator^( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator^( std::vector<double> a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator^( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator>=( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator>=( double a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator>=( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator>=( std::vector<double> a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator>=( VImage a, std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator<<( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator<<( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator<<( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator==( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator==( double a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator==( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator==( std::vector<double> a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator==( VImage a, std::vector<double> b );
|
||||
|
||||
friend VImage VIPS_CPLUSPLUS_API operator>>( VImage a, VImage b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator>>( VImage a, double b );
|
||||
friend VImage VIPS_CPLUSPLUS_API operator>>( VImage a, std::vector<double> b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator!=( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator!=( double a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator!=( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator!=( std::vector<double> a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator!=( VImage a, std::vector<double> b );
|
||||
|
||||
friend VIPS_CPLUSPLUS_API VImage operator&( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator&( double a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator&( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator&( std::vector<double> a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator&( VImage a, std::vector<double> b );
|
||||
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator&=( VImage a, const VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator&=( VImage a, const double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator&=( VImage a, const std::vector<double> b );
|
||||
|
||||
friend VIPS_CPLUSPLUS_API VImage operator|( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator|( double a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator|( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator|( std::vector<double> a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator|( VImage a, std::vector<double> b );
|
||||
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator|=( VImage a, const VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator|=( VImage a, const double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator|=( VImage a, const std::vector<double> b );
|
||||
|
||||
friend VIPS_CPLUSPLUS_API VImage operator^( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator^( double a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator^( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator^( std::vector<double> a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator^( VImage a, std::vector<double> b );
|
||||
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator^=( VImage a, const VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator^=( VImage a, const double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator^=( VImage a, const std::vector<double> b );
|
||||
|
||||
friend VIPS_CPLUSPLUS_API VImage operator<<( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator<<( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator<<( VImage a, std::vector<double> b );
|
||||
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator<<=( VImage a, const VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator<<=( VImage a, const double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator<<=( VImage a, const std::vector<double> b );
|
||||
|
||||
friend VIPS_CPLUSPLUS_API VImage operator>>( VImage a, VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator>>( VImage a, double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage operator>>( VImage a, std::vector<double> b );
|
||||
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator>>=( VImage a, const VImage b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator>>=( VImage a, const double b );
|
||||
friend VIPS_CPLUSPLUS_API VImage & operator>>=( VImage a, const std::vector<double> b );
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ import re
|
|||
import logging
|
||||
#logging.basicConfig(level = logging.DEBUG)
|
||||
|
||||
import gi
|
||||
gi.require_version('Vips', '8.0')
|
||||
from gi.repository import Vips, GObject
|
||||
|
||||
vips_type_image = GObject.GType.from_name("VipsImage")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// headers for vips operations
|
||||
// Fri Feb 12 20:04:03 GMT 2016
|
||||
// Wed 2 Nov 13:48:15 GMT 2016
|
||||
// this file is generated automatically, do not edit!
|
||||
|
||||
static void system( char * cmd_format , VOption *options = 0 );
|
||||
|
@ -103,13 +103,15 @@ VImage invertlut( VOption *options = 0 );
|
|||
static VImage tonelut( VOption *options = 0 );
|
||||
static VImage identity( VOption *options = 0 );
|
||||
static VImage fractsurf( int width , int height , double fractal_dimension , VOption *options = 0 );
|
||||
static VImage radload( char * filename , VOption *options = 0 );
|
||||
static VImage ppmload( char * filename , VOption *options = 0 );
|
||||
static VImage worley( int width , int height , VOption *options = 0 );
|
||||
static VImage perlin( int width , int height , VOption *options = 0 );
|
||||
static VImage csvload( char * filename , VOption *options = 0 );
|
||||
static VImage matrixload( char * filename , VOption *options = 0 );
|
||||
static VImage analyzeload( char * filename , VOption *options = 0 );
|
||||
static VImage rawload( char * filename , int width , int height , int bands , VOption *options = 0 );
|
||||
static VImage vipsload( char * filename , VOption *options = 0 );
|
||||
static VImage analyzeload( char * filename , VOption *options = 0 );
|
||||
static VImage ppmload( char * filename , VOption *options = 0 );
|
||||
static VImage radload( char * filename , VOption *options = 0 );
|
||||
static VImage pdfload( char * filename , VOption *options = 0 );
|
||||
static VImage pdfload_buffer( VipsBlob * buffer , VOption *options = 0 );
|
||||
static VImage svgload( char * filename , VOption *options = 0 );
|
||||
|
@ -130,15 +132,17 @@ static VImage magickload( char * filename , VOption *options = 0 );
|
|||
static VImage magickload_buffer( VipsBlob * buffer , VOption *options = 0 );
|
||||
static VImage fitsload( char * filename , VOption *options = 0 );
|
||||
static VImage openexrload( char * filename , VOption *options = 0 );
|
||||
void radsave( char * filename , VOption *options = 0 );
|
||||
void ppmsave( char * filename , VOption *options = 0 );
|
||||
void csvsave( char * filename , VOption *options = 0 );
|
||||
void matrixsave( char * filename , VOption *options = 0 );
|
||||
void matrixprint( VOption *options = 0 );
|
||||
void rawsave( char * filename , VOption *options = 0 );
|
||||
void rawsave_fd( int fd , VOption *options = 0 );
|
||||
void vipssave( char * filename , VOption *options = 0 );
|
||||
void ppmsave( char * filename , VOption *options = 0 );
|
||||
void radsave( char * filename , VOption *options = 0 );
|
||||
VipsBlob * radsave_buffer( VOption *options = 0 );
|
||||
void dzsave( char * filename , VOption *options = 0 );
|
||||
VipsBlob * dzsave_buffer( VOption *options = 0 );
|
||||
void pngsave( char * filename , VOption *options = 0 );
|
||||
VipsBlob * pngsave_buffer( VOption *options = 0 );
|
||||
void jpegsave( char * filename , VOption *options = 0 );
|
||||
|
@ -147,14 +151,17 @@ void jpegsave_mime( VOption *options = 0 );
|
|||
void webpsave( char * filename , VOption *options = 0 );
|
||||
VipsBlob * webpsave_buffer( VOption *options = 0 );
|
||||
void tiffsave( char * filename , VOption *options = 0 );
|
||||
VipsBlob * tiffsave_buffer( VOption *options = 0 );
|
||||
void fitssave( char * filename , VOption *options = 0 );
|
||||
static VImage thumbnail( char * filename , int width , VOption *options = 0 );
|
||||
static VImage thumbnail_buffer( VipsBlob * buffer , int width , VOption *options = 0 );
|
||||
VImage mapim( VImage index , VOption *options = 0 );
|
||||
VImage shrink( double xshrink , double yshrink , VOption *options = 0 );
|
||||
VImage shrinkh( int xshrink , VOption *options = 0 );
|
||||
VImage shrinkv( int yshrink , VOption *options = 0 );
|
||||
VImage reduceh( double xshrink , VOption *options = 0 );
|
||||
VImage reducev( double yshrink , VOption *options = 0 );
|
||||
VImage reduce( double xshrink , double yshrink , VOption *options = 0 );
|
||||
VImage shrink( double hshrink , double vshrink , VOption *options = 0 );
|
||||
VImage shrinkh( int hshrink , VOption *options = 0 );
|
||||
VImage shrinkv( int vshrink , VOption *options = 0 );
|
||||
VImage reduceh( double hshrink , VOption *options = 0 );
|
||||
VImage reducev( double vshrink , VOption *options = 0 );
|
||||
VImage reduce( double hshrink , double vshrink , VOption *options = 0 );
|
||||
VImage quadratic( VImage coeff , VOption *options = 0 );
|
||||
VImage affine( std::vector<double> matrix , VOption *options = 0 );
|
||||
VImage similarity( VOption *options = 0 );
|
||||
|
@ -202,8 +209,12 @@ VImage hist_local( int width , int height , VOption *options = 0 );
|
|||
bool hist_ismonotonic( VOption *options = 0 );
|
||||
double hist_entropy( VOption *options = 0 );
|
||||
VImage conv( VImage mask , VOption *options = 0 );
|
||||
VImage conva( VImage mask , VOption *options = 0 );
|
||||
VImage convf( VImage mask , VOption *options = 0 );
|
||||
VImage convi( VImage mask , VOption *options = 0 );
|
||||
VImage compass( VImage mask , VOption *options = 0 );
|
||||
VImage convsep( VImage mask , VOption *options = 0 );
|
||||
VImage convasep( VImage mask , VOption *options = 0 );
|
||||
VImage fastcor( VImage ref , VOption *options = 0 );
|
||||
VImage spcor( VImage ref , VOption *options = 0 );
|
||||
VImage sharpen( VOption *options = 0 );
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// bodies for vips operations
|
||||
// Fri Feb 12 20:03:53 GMT 2016
|
||||
// Wed 2 Nov 13:48:04 GMT 2016
|
||||
// this file is generated automatically, do not edit!
|
||||
|
||||
void VImage::system( char * cmd_format , VOption *options )
|
||||
|
@ -1321,26 +1321,28 @@ VImage VImage::fractsurf( int width , int height , double fractal_dimension , VO
|
|||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::radload( char * filename , VOption *options )
|
||||
VImage VImage::worley( int width , int height , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
call( "radload" ,
|
||||
call( "worley" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "filename", filename ) ->
|
||||
set( "out", &out ) );
|
||||
set( "out", &out ) ->
|
||||
set( "width", width ) ->
|
||||
set( "height", height ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::ppmload( char * filename , VOption *options )
|
||||
VImage VImage::perlin( int width , int height , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
call( "ppmload" ,
|
||||
call( "perlin" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "filename", filename ) ->
|
||||
set( "out", &out ) );
|
||||
set( "out", &out ) ->
|
||||
set( "width", width ) ->
|
||||
set( "height", height ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
@ -1369,18 +1371,6 @@ VImage VImage::matrixload( char * filename , VOption *options )
|
|||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::analyzeload( char * filename , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
call( "analyzeload" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "filename", filename ) ->
|
||||
set( "out", &out ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::rawload( char * filename , int width , int height , int bands , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
@ -1408,6 +1398,42 @@ VImage VImage::vipsload( char * filename , VOption *options )
|
|||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::analyzeload( char * filename , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
call( "analyzeload" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "filename", filename ) ->
|
||||
set( "out", &out ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::ppmload( char * filename , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
call( "ppmload" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "filename", filename ) ->
|
||||
set( "out", &out ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::radload( char * filename , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
call( "radload" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "filename", filename ) ->
|
||||
set( "out", &out ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::pdfload( char * filename , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
@ -1648,22 +1674,6 @@ VImage VImage::openexrload( char * filename , VOption *options )
|
|||
return( out );
|
||||
}
|
||||
|
||||
void VImage::radsave( char * filename , VOption *options )
|
||||
{
|
||||
call( "radsave" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "filename", filename ) );
|
||||
}
|
||||
|
||||
void VImage::ppmsave( char * filename , VOption *options )
|
||||
{
|
||||
call( "ppmsave" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "filename", filename ) );
|
||||
}
|
||||
|
||||
void VImage::csvsave( char * filename , VOption *options )
|
||||
{
|
||||
call( "csvsave" ,
|
||||
|
@ -1711,6 +1721,34 @@ void VImage::vipssave( char * filename , VOption *options )
|
|||
set( "filename", filename ) );
|
||||
}
|
||||
|
||||
void VImage::ppmsave( char * filename , VOption *options )
|
||||
{
|
||||
call( "ppmsave" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "filename", filename ) );
|
||||
}
|
||||
|
||||
void VImage::radsave( char * filename , VOption *options )
|
||||
{
|
||||
call( "radsave" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "filename", filename ) );
|
||||
}
|
||||
|
||||
VipsBlob * VImage::radsave_buffer( VOption *options )
|
||||
{
|
||||
VipsBlob * buffer;
|
||||
|
||||
call( "radsave_buffer" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "buffer", &buffer ) );
|
||||
|
||||
return( buffer );
|
||||
}
|
||||
|
||||
void VImage::dzsave( char * filename , VOption *options )
|
||||
{
|
||||
call( "dzsave" ,
|
||||
|
@ -1719,6 +1757,18 @@ void VImage::dzsave( char * filename , VOption *options )
|
|||
set( "filename", filename ) );
|
||||
}
|
||||
|
||||
VipsBlob * VImage::dzsave_buffer( VOption *options )
|
||||
{
|
||||
VipsBlob * buffer;
|
||||
|
||||
call( "dzsave_buffer" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "buffer", &buffer ) );
|
||||
|
||||
return( buffer );
|
||||
}
|
||||
|
||||
void VImage::pngsave( char * filename , VOption *options )
|
||||
{
|
||||
call( "pngsave" ,
|
||||
|
@ -1794,6 +1844,18 @@ void VImage::tiffsave( char * filename , VOption *options )
|
|||
set( "filename", filename ) );
|
||||
}
|
||||
|
||||
VipsBlob * VImage::tiffsave_buffer( VOption *options )
|
||||
{
|
||||
VipsBlob * buffer;
|
||||
|
||||
call( "tiffsave_buffer" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "buffer", &buffer ) );
|
||||
|
||||
return( buffer );
|
||||
}
|
||||
|
||||
void VImage::fitssave( char * filename , VOption *options )
|
||||
{
|
||||
call( "fitssave" ,
|
||||
|
@ -1802,6 +1864,32 @@ void VImage::fitssave( char * filename , VOption *options )
|
|||
set( "filename", filename ) );
|
||||
}
|
||||
|
||||
VImage VImage::thumbnail( char * filename , int width , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
call( "thumbnail" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "filename", filename ) ->
|
||||
set( "out", &out ) ->
|
||||
set( "width", width ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::thumbnail_buffer( VipsBlob * buffer , int width , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
call( "thumbnail_buffer" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "buffer", buffer ) ->
|
||||
set( "out", &out ) ->
|
||||
set( "width", width ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::mapim( VImage index , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
@ -1815,7 +1903,7 @@ VImage VImage::mapim( VImage index , VOption *options )
|
|||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::shrink( double xshrink , double yshrink , VOption *options )
|
||||
VImage VImage::shrink( double hshrink , double vshrink , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
|
@ -1823,13 +1911,13 @@ VImage VImage::shrink( double xshrink , double yshrink , VOption *options )
|
|||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "out", &out ) ->
|
||||
set( "xshrink", xshrink ) ->
|
||||
set( "yshrink", yshrink ) );
|
||||
set( "hshrink", hshrink ) ->
|
||||
set( "vshrink", vshrink ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::shrinkh( int xshrink , VOption *options )
|
||||
VImage VImage::shrinkh( int hshrink , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
|
@ -1837,12 +1925,12 @@ VImage VImage::shrinkh( int xshrink , VOption *options )
|
|||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "out", &out ) ->
|
||||
set( "xshrink", xshrink ) );
|
||||
set( "hshrink", hshrink ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::shrinkv( int yshrink , VOption *options )
|
||||
VImage VImage::shrinkv( int vshrink , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
|
@ -1850,12 +1938,12 @@ VImage VImage::shrinkv( int yshrink , VOption *options )
|
|||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "out", &out ) ->
|
||||
set( "yshrink", yshrink ) );
|
||||
set( "vshrink", vshrink ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::reduceh( double xshrink , VOption *options )
|
||||
VImage VImage::reduceh( double hshrink , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
|
@ -1863,12 +1951,12 @@ VImage VImage::reduceh( double xshrink , VOption *options )
|
|||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "out", &out ) ->
|
||||
set( "xshrink", xshrink ) );
|
||||
set( "hshrink", hshrink ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::reducev( double yshrink , VOption *options )
|
||||
VImage VImage::reducev( double vshrink , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
|
@ -1876,12 +1964,12 @@ VImage VImage::reducev( double yshrink , VOption *options )
|
|||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "out", &out ) ->
|
||||
set( "yshrink", yshrink ) );
|
||||
set( "vshrink", vshrink ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::reduce( double xshrink , double yshrink , VOption *options )
|
||||
VImage VImage::reduce( double hshrink , double vshrink , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
|
@ -1889,8 +1977,8 @@ VImage VImage::reduce( double xshrink , double yshrink , VOption *options )
|
|||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "out", &out ) ->
|
||||
set( "xshrink", xshrink ) ->
|
||||
set( "yshrink", yshrink ) );
|
||||
set( "hshrink", hshrink ) ->
|
||||
set( "vshrink", vshrink ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
@ -2475,6 +2563,45 @@ VImage VImage::conv( VImage mask , VOption *options )
|
|||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::conva( VImage mask , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
call( "conva" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "out", &out ) ->
|
||||
set( "mask", mask ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::convf( VImage mask , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
call( "convf" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "out", &out ) ->
|
||||
set( "mask", mask ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::convi( VImage mask , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
call( "convi" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "out", &out ) ->
|
||||
set( "mask", mask ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::compass( VImage mask , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
@ -2501,6 +2628,19 @@ VImage VImage::convsep( VImage mask , VOption *options )
|
|||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::convasep( VImage mask , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
||||
call( "convasep" ,
|
||||
(options ? options : VImage::option()) ->
|
||||
set( "in", *this ) ->
|
||||
set( "out", &out ) ->
|
||||
set( "mask", mask ) );
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
VImage VImage::fastcor( VImage ref , VOption *options )
|
||||
{
|
||||
VImage out;
|
||||
|
|
|
@ -208,6 +208,14 @@ rm t1.v
|
|||
leak-test on exit, and also display an estimate of peak memory use.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Set <code>G_MESSAGES_DEBUG=VIPS</code> and GLib will display
|
||||
informational and debug messages from libvips.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
|
|
|
@ -491,7 +491,8 @@ result_image = image1.bandjoin([image2, 255])
|
|||
<refsect3 id="python-memory">
|
||||
<title>Reading and writing areas of memory</title>
|
||||
<para>
|
||||
You can use the C API functions vips_image_new_from_memory() and
|
||||
You can use the C API functions vips_image_new_from_memory(),
|
||||
vips_image_new_from_memory_copy() and
|
||||
vips_image_write_to_memory() directly from Python to read and write
|
||||
areas of memory. This can be useful if you need to get images to and
|
||||
from other other image processing libraries, like PIL or numpy.
|
||||
|
@ -540,8 +541,11 @@ image2 = Vips.Image.new_from_memory(memory_area,
|
|||
collector and
|
||||
you later try to use <code>image2</code>, you'll get a crash.
|
||||
Make sure you keep a reference to <code>memory_area</code> around
|
||||
for as long as you need it.
|
||||
for as long as you need it. A simple solution is to use
|
||||
<code>new_from_memory_copy</code> instead. This will take a copy of the
|
||||
memory area for vips. Of course this will raise memory usage.
|
||||
</para>
|
||||
|
||||
</refsect3>
|
||||
|
||||
<refsect3 id="python-modify">
|
||||
|
|
|
@ -64,6 +64,13 @@ EXTRA_DIST = \
|
|||
|
||||
CLEANFILES =
|
||||
|
||||
install-exec-hook:
|
||||
echo '/* This file is autogenerated, do not edit. */' > soname.h && \
|
||||
source libvips.la && \
|
||||
echo "#define VIPS_SONAME \"$$dlname\"" >> soname.h && \
|
||||
cp soname.h $(DESTDIR)$(pkgincludedir) && \
|
||||
rm soname.h
|
||||
|
||||
-include $(INTROSPECTION_MAKEFILE)
|
||||
INTROSPECTION_GIRS =
|
||||
INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir)
|
||||
|
@ -98,14 +105,14 @@ Vips_8_0_gir_LIBS = libvips.la
|
|||
Vips_8_0_gir_FILES = $(introspection_sources)
|
||||
INTROSPECTION_GIRS += Vips-8.0.gir
|
||||
|
||||
# don't use --warn-all --verbose
|
||||
# we have an unusual markup with optional args and we don't want to see all
|
||||
# those warnings
|
||||
# don't use
|
||||
# --warn-all --verbose
|
||||
# too annoying
|
||||
Vips_8_0_gir_SCANNERFLAGS = \
|
||||
--program=./introspect \
|
||||
--identifier-prefix=Vips \
|
||||
--identifier-prefix=vips \
|
||||
--symbol-prefix=vips
|
||||
--program=./introspect \
|
||||
--identifier-prefix=Vips \
|
||||
--identifier-prefix=vips \
|
||||
--symbol-prefix=vips
|
||||
|
||||
girdir = $(datadir)/gir-1.0
|
||||
gir_DATA = $(INTROSPECTION_GIRS)
|
||||
|
|
|
@ -516,11 +516,10 @@ vips_arithmetic_gen( VipsRegion *or,
|
|||
|
||||
/* Prepare all input regions and make buffer pointers.
|
||||
*/
|
||||
for( i = 0; ir[i]; i++ ) {
|
||||
if( vips_region_prepare( ir[i], r ) )
|
||||
return( -1 );
|
||||
if( vips_reorder_prepare_many( or->im, ir, r ) )
|
||||
return( -1 );
|
||||
for( i = 0; ir[i]; i++ )
|
||||
p[i] = (VipsPel *) VIPS_REGION_ADDR( ir[i], r->left, r->top );
|
||||
}
|
||||
p[i] = NULL;
|
||||
q = (VipsPel *) VIPS_REGION_ADDR( or, r->left, r->top );
|
||||
|
||||
|
@ -536,6 +535,8 @@ vips_arithmetic_gen( VipsRegion *or,
|
|||
|
||||
VIPS_GATE_STOP( "vips_arithmetic_gen: work" );
|
||||
|
||||
VIPS_COUNT_PIXELS( or, VIPS_OBJECT_CLASS( class )->nickname );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
* - redo as a class
|
||||
* 16/12/14
|
||||
* - free the input region much earlier
|
||||
* 14/10/16
|
||||
* - crop to a memory image rather than using a region ... this means we
|
||||
* use workers to calculate pixels and therefore use per-thread caching
|
||||
* in the revised buffer system
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -86,9 +90,8 @@ vips_getpoint_build( VipsObject *object )
|
|||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsGetpoint *getpoint = (VipsGetpoint *) object;
|
||||
VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 );
|
||||
|
||||
VipsRect area;
|
||||
VipsRegion *region;
|
||||
double *vector;
|
||||
int n;
|
||||
VipsArrayDouble *out_array;
|
||||
|
@ -96,31 +99,15 @@ vips_getpoint_build( VipsObject *object )
|
|||
if( VIPS_OBJECT_CLASS( vips_getpoint_parent_class )->build( object ) )
|
||||
return( -1 );
|
||||
|
||||
/* <0 ruled out already.
|
||||
*/
|
||||
if( getpoint->x >= getpoint->in->Xsize ||
|
||||
getpoint->y >= getpoint->in->Ysize ) {
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "coordinates out of range" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( vips_check_coding_known( class->nickname, getpoint->in ) ||
|
||||
!(region = vips_region_new( getpoint->in )) )
|
||||
t[1] = vips_image_new_memory();
|
||||
if( vips_crop( getpoint->in, &t[0],
|
||||
getpoint->x, getpoint->y, 1, 1, NULL ) ||
|
||||
vips_image_write( t[0], t[1] ) )
|
||||
return( -1 );
|
||||
|
||||
area.left = getpoint->x;
|
||||
area.top = getpoint->y;
|
||||
area.width = 1;
|
||||
area.height = 1;
|
||||
if( vips_region_prepare( region, &area ) ||
|
||||
!(vector = vips__ink_to_vector( class->nickname, getpoint->in,
|
||||
VIPS_REGION_ADDR( region, area.left, area.top ),
|
||||
&n )) ) {
|
||||
VIPS_UNREF( region );
|
||||
if( !(vector = vips__ink_to_vector( class->nickname,
|
||||
getpoint->in, VIPS_IMAGE_ADDR( t[1], 0, 0 ), &n )) )
|
||||
return( -1 );
|
||||
}
|
||||
VIPS_UNREF( region );
|
||||
|
||||
out_array = vips_array_double_new( vector, n );
|
||||
g_object_set( object,
|
||||
|
|
|
@ -392,7 +392,8 @@ vips_math2_const_class_init( VipsMath2ConstClass *class )
|
|||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "math2_const";
|
||||
object_class->description = _( "pow( @in, @c )" );
|
||||
object_class->description =
|
||||
_( "binary math operations with a constant" );
|
||||
object_class->build = vips_math2_const_build;
|
||||
|
||||
aclass->process_line = vips_math2_const_buffer;
|
||||
|
|
|
@ -165,9 +165,11 @@ vips_measure_build( VipsObject *object )
|
|||
*/
|
||||
if( dev * 5 > VIPS_FABS( avg ) &&
|
||||
VIPS_FABS( avg ) > 3 )
|
||||
vips_warn( class->nickname,
|
||||
_( "patch %d x %d, band %d: "
|
||||
"avg = %g, sdev = %g" ),
|
||||
g_warning( _( "%s: "
|
||||
"patch %d x %d, "
|
||||
"band %d: "
|
||||
"avg = %g, sdev = %g" ),
|
||||
class->nickname,
|
||||
i, j, b, avg, dev );
|
||||
|
||||
*VIPS_MATRIX( measure->out,
|
||||
|
|
|
@ -95,11 +95,9 @@ vips_Lab2LabQ_line( VipsColour *colour, VipsPel *out, VipsPel **in, int width )
|
|||
int lsbs;
|
||||
int intv;
|
||||
|
||||
/* Scale L up to 10 bits. Add 0.5 rather than call VIPS_RINT
|
||||
* for speed. This will not round negatives correctly! But
|
||||
* this does not matter, since L is >0. L*=100.0 -> 1023.
|
||||
/* Scale L up to 10 bits.
|
||||
*/
|
||||
intv = 10.23 * p[0] + 0.5; /* scale L up to 10 bits */
|
||||
intv = VIPS_ROUND_UINT( 10.23 * p[0] );
|
||||
intv = VIPS_CLIP( 0, intv, 1023 );
|
||||
lsbs = (intv & 0x3) << 6; /* 00000011 -> 11000000 */
|
||||
q[0] = intv >> 2; /* drop bot 2 bits and store */
|
||||
|
|
|
@ -197,9 +197,9 @@ vips_Lab2XYZ_init( VipsLab2XYZ *Lab2XYZ )
|
|||
* @out: output image
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* optional arguments:
|
||||
* Optional arguments:
|
||||
*
|
||||
* @temp: colour temperature
|
||||
* * @temp: #VipsArrayDouble, colour temperature
|
||||
*
|
||||
* Turn Lab to XYZ. The colour temperature defaults to D65, but can be
|
||||
* specified with @temp.
|
||||
|
|
|
@ -169,17 +169,9 @@ calcul_tables_8( void *client )
|
|||
static void
|
||||
vips_col_make_tables_RGB_8( void )
|
||||
{
|
||||
static gboolean made_tables = FALSE;
|
||||
static GOnce once = G_ONCE_INIT;
|
||||
|
||||
/* We want to avoid having a mutex in this path, so use gonce and a
|
||||
* static var instead.
|
||||
*/
|
||||
if( !made_tables ) {
|
||||
static GOnce once = G_ONCE_INIT;
|
||||
|
||||
(void) g_once( &once, calcul_tables_8, NULL );
|
||||
made_tables = TRUE;
|
||||
}
|
||||
(void) g_once( &once, calcul_tables_8, NULL );
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -201,17 +193,9 @@ calcul_tables_16( void *client )
|
|||
static void
|
||||
vips_col_make_tables_RGB_16( void )
|
||||
{
|
||||
static gboolean made_tables = FALSE;
|
||||
static GOnce once = G_ONCE_INIT;
|
||||
|
||||
/* We want to avoid having a mutex in this path, so use gonce and a
|
||||
* static var instead.
|
||||
*/
|
||||
if( !made_tables ) {
|
||||
static GOnce once = G_ONCE_INIT;
|
||||
|
||||
(void) g_once( &once, calcul_tables_16, NULL );
|
||||
made_tables = TRUE;
|
||||
}
|
||||
(void) g_once( &once, calcul_tables_16, NULL );
|
||||
}
|
||||
|
||||
int
|
||||
|
|
|
@ -250,9 +250,9 @@ vips_XYZ2Lab_init( VipsXYZ2Lab *XYZ2Lab )
|
|||
* @out: output image
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* optional arguments:
|
||||
* Optional arguments:
|
||||
*
|
||||
* @temp: colour temperature
|
||||
* * @temp: #VipsArrayDouble, colour temperature
|
||||
*
|
||||
* Turn XYZ to Lab, optionally specifying the colour temperature. @temp
|
||||
* defaults to D65.
|
||||
|
|
|
@ -234,9 +234,8 @@ vips_colour_gen( VipsRegion *or,
|
|||
int i, y;
|
||||
VipsPel *p[MAX_INPUT_IMAGES], *q;
|
||||
|
||||
for( i = 0; ir[i]; i++ )
|
||||
if( vips_region_prepare( ir[i], r ) )
|
||||
return( -1 );
|
||||
if( vips_reorder_prepare_many( or->im, ir, r ) )
|
||||
return( -1 );
|
||||
|
||||
VIPS_GATE_START( "vips_colour_gen: work" );
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ vips_scRGB2RGB16( VipsImage *in, VipsImage **out, ... )
|
|||
static int
|
||||
vips_scRGB2BW16( VipsImage *in, VipsImage **out, ... )
|
||||
{
|
||||
return( vips_scRGB2BW16( in, out, "depth", 16, NULL ) );
|
||||
return( vips_scRGB2BW( in, out, "depth", 16, NULL ) );
|
||||
}
|
||||
|
||||
/* Do these two with a simple cast ... since we're just cast shifting, we can
|
||||
|
@ -545,7 +545,7 @@ vips_colourspace_build( VipsObject *object )
|
|||
break;
|
||||
if( i == VIPS_NUMBER( vips_colour_routes ) ) {
|
||||
vips_error( "vips_colourspace",
|
||||
_( "no known route between '%s' and '%s'" ),
|
||||
_( "no known route from '%s' to '%s'" ),
|
||||
vips_enum_nick( VIPS_TYPE_INTERPRETATION,
|
||||
interpretation ),
|
||||
vips_enum_nick( VIPS_TYPE_INTERPRETATION,
|
||||
|
|
|
@ -193,9 +193,9 @@ static int
|
|||
icc_error( int code, const char *text )
|
||||
{
|
||||
if( code == LCMS_ERRC_WARNING )
|
||||
vips_warn( "VipsIcc", "%s", text );
|
||||
g_warning( "%s", text );
|
||||
else
|
||||
vips_error( "VipsIcc", "%s", text );
|
||||
vips_error( "VipsIcc", text );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
@ -452,9 +452,9 @@ vips_check_intent( const char *domain,
|
|||
{
|
||||
if( profile &&
|
||||
!cmsIsIntentSupported( profile, intent, direction ) )
|
||||
vips_warn( domain,
|
||||
_( "intent %d (%s) not supported by "
|
||||
g_warning( _( "%s: intent %d (%s) not supported by "
|
||||
"%s profile; falling back to default intent" ),
|
||||
domain,
|
||||
intent, vips_enum_nick( VIPS_TYPE_INTENT, intent ),
|
||||
direction == LCMS_USED_AS_INPUT ?
|
||||
_( "input" ) : _( "output" ) );
|
||||
|
@ -542,7 +542,7 @@ vips_image_expected_bands( VipsImage *image )
|
|||
}
|
||||
|
||||
static cmsHPROFILE
|
||||
vips_icc_load_profile_image( const char *domain, VipsImage *image )
|
||||
vips_icc_load_profile_image( VipsImage *image )
|
||||
{
|
||||
void *data;
|
||||
size_t data_length;
|
||||
|
@ -554,15 +554,15 @@ vips_icc_load_profile_image( const char *domain, VipsImage *image )
|
|||
if( vips_image_get_blob( image, VIPS_META_ICC_NAME,
|
||||
&data, &data_length ) ||
|
||||
!(profile = cmsOpenProfileFromMem( data, data_length )) ) {
|
||||
vips_warn( domain, "%s", _( "corrupt embedded profile" ) );
|
||||
g_warning( "%s", _( "corrupt embedded profile" ) );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
if( vips_image_expected_bands( image ) !=
|
||||
vips_icc_profile_needs_bands( profile ) ) {
|
||||
VIPS_FREEF( cmsCloseProfile, profile );
|
||||
vips_warn( domain,
|
||||
"%s", _( "embedded profile incompatible with image" ) );
|
||||
g_warning( "%s",
|
||||
_( "embedded profile incompatible with image" ) );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
|
@ -584,8 +584,7 @@ vips_icc_load_profile_file( const char *domain,
|
|||
if( vips_image_expected_bands( image ) !=
|
||||
vips_icc_profile_needs_bands( profile ) ) {
|
||||
VIPS_FREEF( cmsCloseProfile, profile );
|
||||
vips_warn( domain,
|
||||
_( "profile \"%s\" incompatible with image" ),
|
||||
g_warning( _( "profile \"%s\" incompatible with image" ),
|
||||
filename );
|
||||
return( NULL );
|
||||
}
|
||||
|
@ -615,8 +614,7 @@ vips_icc_import_build( VipsObject *object )
|
|||
if( code->in &&
|
||||
(import->embedded ||
|
||||
!import->input_profile_filename) )
|
||||
icc->in_profile = vips_icc_load_profile_image( class->nickname,
|
||||
code->in );
|
||||
icc->in_profile = vips_icc_load_profile_image( code->in );
|
||||
|
||||
if( !icc->in_profile &&
|
||||
code->in &&
|
||||
|
@ -1027,8 +1025,7 @@ vips_icc_transform_build( VipsObject *object )
|
|||
if( code->in &&
|
||||
(transform->embedded ||
|
||||
!transform->input_profile_filename) )
|
||||
icc->in_profile = vips_icc_load_profile_image( class->nickname,
|
||||
code->in );
|
||||
icc->in_profile = vips_icc_load_profile_image( code->in );
|
||||
|
||||
if( !icc->in_profile &&
|
||||
code->in &&
|
||||
|
@ -1265,7 +1262,11 @@ vips_icc_ac2rc( VipsImage *in, VipsImage **out, const char *profile_filename )
|
|||
*
|
||||
* If @embedded is set, the input profile is taken from the input image
|
||||
* metadata. If there is no embedded profile,
|
||||
* @input_profile_filename is used as a fall-back.
|
||||
* @input_profile_filename is used as a fall-back.
|
||||
* You can test for the
|
||||
* presence of an embedded profile with
|
||||
* vips_image_get_typeof() with #VIPS_META_ICC_NAME as an argument. This will
|
||||
* return %GType 0 if there is no profile.
|
||||
*
|
||||
* If @embedded is not set, the input profile is taken from
|
||||
* @input_profile. If @input_profile is not supplied, the
|
||||
|
@ -1342,11 +1343,18 @@ vips_icc_export( VipsImage *in, VipsImage **out, ... )
|
|||
* If @embedded is set, the input profile is taken from the input image
|
||||
* metadata, if present. If there is no embedded profile,
|
||||
* @input_profile is used as a fall-back.
|
||||
* You can test for the
|
||||
* presence of an embedded profile with
|
||||
* vips_image_get_typeof() with #VIPS_META_ICC_NAME as an argument. This will
|
||||
* return %GType 0 if there is no profile.
|
||||
*
|
||||
* If @embedded is not set, the input profile is taken from
|
||||
* @input_profile. If @input_profile is not supplied, the
|
||||
* metadata profile, if any, is used as a fall-back.
|
||||
*
|
||||
* The output image has the output profile attached to the #VIPS_META_ICC_NAME
|
||||
* field.
|
||||
*
|
||||
* Use vips_icc_import() and vips_icc_export() to do either the first or
|
||||
* second half of this operation in isolation.
|
||||
*
|
||||
|
|
|
@ -50,14 +50,6 @@
|
|||
|
||||
#include "pconversion.h"
|
||||
|
||||
/* Round N down to P boundary.
|
||||
*/
|
||||
#define ROUND_DOWN( N, P ) ((N) - ((N) % P))
|
||||
|
||||
/* Round N up to P boundary.
|
||||
*/
|
||||
#define ROUND_UP( N, P ) (ROUND_DOWN( (N) + (P) - 1, (P) ))
|
||||
|
||||
typedef struct _VipsArrayjoin {
|
||||
VipsConversion parent_instance;
|
||||
|
||||
|
@ -179,7 +171,7 @@ vips_arrayjoin_build( VipsObject *object )
|
|||
|
||||
/* How many images down the grid?
|
||||
*/
|
||||
join->down = ROUND_UP( n, join->across ) / join->across;
|
||||
join->down = VIPS_ROUND_UP( n, join->across ) / join->across;
|
||||
|
||||
/* The output size.
|
||||
*/
|
||||
|
@ -394,16 +386,17 @@ vips_arrayjoinv( VipsImage **in, VipsImage **out, int n, va_list ap )
|
|||
* @in: (array length=n) (transfer none): array of input images
|
||||
* @out: output image
|
||||
* @n: number of input images
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @across: number of images per row
|
||||
* * @shim: space between images, in pixels
|
||||
* * @background: background ink colour
|
||||
* * @halign: low, centre or high alignment
|
||||
* * @valign: low, centre or high alignment
|
||||
* * @hspacing: horizontal distance between images
|
||||
* * @vspacing: vertical distance between images
|
||||
* * @across: %gint, number of images per row
|
||||
* * @shim: %gint, space between images, in pixels
|
||||
* * @background: #VipsArrayDouble, background ink colour
|
||||
* * @halign: #VipsAlign, low, centre or high alignment
|
||||
* * @valign: #VipsAlign, low, centre or high alignment
|
||||
* * @hspacing: %gint, horizontal distance between images
|
||||
* * @vspacing: %gint, vertical distance between images
|
||||
*
|
||||
* Lay out the images in @in in a grid. The grid is @across images across and
|
||||
* however high is necessary to use up all of @in. Images are set down
|
||||
|
|
|
@ -59,42 +59,6 @@ typedef VipsConversionClass VipsAutorotClass;
|
|||
|
||||
G_DEFINE_TYPE( VipsAutorot, vips_autorot, VIPS_TYPE_CONVERSION );
|
||||
|
||||
static void *
|
||||
vips_autorot_get_angle_sub( VipsImage *image,
|
||||
const char *field, GValue *value, void *my_data )
|
||||
{
|
||||
VipsAngle *angle = (VipsAngle *) my_data;
|
||||
|
||||
const char *orientation;
|
||||
|
||||
if( vips_isprefix( "exif-", field ) &&
|
||||
vips_ispostfix( field, "-Orientation" ) &&
|
||||
!vips_image_get_string( image, field, &orientation ) ) {
|
||||
if( vips_isprefix( "6", orientation ) )
|
||||
*angle = VIPS_ANGLE_D90;
|
||||
else if( vips_isprefix( "8", orientation ) )
|
||||
*angle = VIPS_ANGLE_D270;
|
||||
else if( vips_isprefix( "3", orientation ) )
|
||||
*angle = VIPS_ANGLE_D180;
|
||||
else
|
||||
*angle = VIPS_ANGLE_D0;
|
||||
|
||||
/* Other values do rotate + mirror, don't bother handling them
|
||||
* though, how common can mirroring be.
|
||||
*
|
||||
* See:
|
||||
*
|
||||
* http://www.80sidea.com/archives/2316
|
||||
*/
|
||||
|
||||
/* Stop searching.
|
||||
*/
|
||||
return( image );
|
||||
}
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_autorot_get_angle:
|
||||
* @image: image to fetch orientation from
|
||||
|
@ -107,18 +71,41 @@ vips_autorot_get_angle_sub( VipsImage *image,
|
|||
* Returns: the #VipsAngle to rotate by to make the image upright.
|
||||
*/
|
||||
VipsAngle
|
||||
vips_autorot_get_angle( VipsImage *image )
|
||||
vips_autorot_get_angle( VipsImage *im )
|
||||
{
|
||||
int orientation;
|
||||
VipsAngle angle;
|
||||
|
||||
angle = VIPS_ANGLE_D0;
|
||||
(void) vips_image_map( image, vips_autorot_get_angle_sub, &angle );
|
||||
if( !vips_image_get_typeof( im, VIPS_META_ORIENTATION ) ||
|
||||
vips_image_get_int( im, VIPS_META_ORIENTATION, &orientation ) )
|
||||
orientation = 1;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_autorot_get_angle: %d\n", angle );
|
||||
#endif /*DEBUG*/
|
||||
switch( orientation ) {
|
||||
case 6:
|
||||
angle = VIPS_ANGLE_D90;
|
||||
break;
|
||||
|
||||
return( angle );
|
||||
case 8:
|
||||
angle = VIPS_ANGLE_D270;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
angle = VIPS_ANGLE_D180;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Other values do rotate + mirror, don't bother handling them
|
||||
* though, how common can mirroring be.
|
||||
*
|
||||
* See:
|
||||
*
|
||||
* http://www.80sidea.com/archives/2316
|
||||
*/
|
||||
angle = VIPS_ANGLE_D0;
|
||||
break;
|
||||
}
|
||||
|
||||
return( angle );
|
||||
}
|
||||
|
||||
static void *
|
||||
|
@ -139,15 +126,16 @@ vips_autorot_remove_angle_sub( VipsImage *image,
|
|||
|
||||
/**
|
||||
* vips_autorot_remove_angle:
|
||||
* @im: image to remove orientation from
|
||||
* @image: image to remove orientation from
|
||||
*
|
||||
* Remove any EXIF tag on @im which looks like orientation.
|
||||
* Remove the orientation tag on @image. Also remove any exif orientation tags.
|
||||
*
|
||||
* See also: vips_autorot_get_angle().
|
||||
*/
|
||||
void
|
||||
vips_autorot_remove_angle( VipsImage *image )
|
||||
{
|
||||
(void) vips_image_remove( image, VIPS_META_ORIENTATION );
|
||||
(void) vips_image_map( image, vips_autorot_remove_angle_sub, NULL );
|
||||
}
|
||||
|
||||
|
@ -216,12 +204,16 @@ vips_autorot_init( VipsAutorot *autorot )
|
|||
*
|
||||
* * @angle: output #VipsAngle the image was rotated by
|
||||
*
|
||||
* Look at the exif tags and rotate the image to make it upright. The
|
||||
* orientation tag is removed from @out to prevent accidental double rotation.
|
||||
* Look at the image metadata and rotate the image to make it upright. The
|
||||
* #VIPS_META_ORIENTATION tag is removed from @out to prevent accidental
|
||||
* double rotation.
|
||||
*
|
||||
* Read @angle to find the amount the image was rotated by.
|
||||
*
|
||||
* See also: vips_rot().
|
||||
* vips only supports the four simple rotations, it does not support the
|
||||
* various mirror modes.
|
||||
*
|
||||
* See also: vips_autorot_get_angle(), vips_autorot_remove_angle(), vips_rot().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
|
|
|
@ -92,11 +92,10 @@ vips_bandary_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
|
|||
VipsPel *p[MAX_INPUT_IMAGES], *q;
|
||||
int y, i;
|
||||
|
||||
for( i = 0; i < bandary->n; i++ ) {
|
||||
if( vips_region_prepare( ir[i], r ) )
|
||||
return( -1 );
|
||||
if( vips_reorder_prepare_many( or->im, ir, r ) )
|
||||
return( -1 );
|
||||
for( i = 0; i < bandary->n; i++ )
|
||||
p[i] = VIPS_REGION_ADDR( ir[i], r->left, r->top );
|
||||
}
|
||||
p[i] = NULL;
|
||||
q = VIPS_REGION_ADDR( or, r->left, r->top );
|
||||
|
||||
|
|
|
@ -186,7 +186,7 @@ vips_bandbool_buffer( VipsBandary *bandary,
|
|||
#define D VIPS_FORMAT_DOUBLE
|
||||
#define DX VIPS_FORMAT_DPCOMPLEX
|
||||
|
||||
/* Type conversions for boolean.
|
||||
/* Format conversions for boolean.
|
||||
*/
|
||||
static const VipsBandFormat vips_bandbool_format_table[10] = {
|
||||
/* UC C US S UI I F X D DX */
|
||||
|
|
|
@ -142,7 +142,9 @@ vips_bandjoin_build( VipsObject *object )
|
|||
|
||||
bandary->out_bands = 0;
|
||||
for( i = 0; i < bandary->n; i++ )
|
||||
bandary->out_bands += bandary->in[i]->Bands;
|
||||
if( bandary->in[i] )
|
||||
bandary->out_bands +=
|
||||
bandary->in[i]->Bands;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -126,11 +126,9 @@ vips_cast_preeval( VipsImage *image, VipsProgress *progress, VipsCast *cast )
|
|||
static void
|
||||
vips_cast_posteval( VipsImage *image, VipsProgress *progress, VipsCast *cast )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( cast );
|
||||
|
||||
if( cast->overflow || cast->underflow )
|
||||
vips_warn( class->nickname,
|
||||
_( "%d underflows and %d overflows detected" ),
|
||||
if( cast->overflow ||
|
||||
cast->underflow )
|
||||
g_warning( _( "%d underflows and %d overflows detected" ),
|
||||
cast->underflow, cast->overflow );
|
||||
}
|
||||
|
||||
|
@ -587,7 +585,7 @@ vips_castv( VipsImage *in, VipsImage **out, VipsBandFormat format, va_list ap )
|
|||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @shift: integer values are shifted
|
||||
* * @shift: %gboolean, integer values are shifted
|
||||
*
|
||||
* Convert @in to @format. You can convert between any pair of formats.
|
||||
* Floats are truncated (not rounded). Out of range values are clipped.
|
||||
|
|
|
@ -177,7 +177,7 @@ vips_copy_build( VipsObject *object )
|
|||
return( -1 );
|
||||
|
||||
if( copy->swap )
|
||||
vips_warn( class->nickname, "%s",
|
||||
g_warning( "%s",
|
||||
_( "copy swap is deprecated, use byteswap instead" ) );
|
||||
|
||||
if( vips_image_pipelinev( conversion->out,
|
||||
|
@ -366,16 +366,16 @@ vips_copy_init( VipsCopy *copy )
|
|||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @width: set image width
|
||||
* * @height: set image height
|
||||
* * @bands: set image bands
|
||||
* * @format: set image format
|
||||
* * @coding: set image coding
|
||||
* * @interpretation: set image interpretation
|
||||
* * @xres: set image xres
|
||||
* * @yres: set image yres
|
||||
* * @xoffset: set image xoffset
|
||||
* * @yoffset: set image yoffset
|
||||
* * @width: %gint, set image width
|
||||
* * @height: %gint, set image height
|
||||
* * @bands: %gint, set image bands
|
||||
* * @format: #VipsBandFormat, set image format
|
||||
* * @coding: #VipsCoding, set image coding
|
||||
* * @interpretation: #VipsInterpretation, set image interpretation
|
||||
* * @xres: %gdouble, set image xres
|
||||
* * @yres: %gdouble, set image yres
|
||||
* * @xoffset: %gint, set image xoffset
|
||||
* * @yoffset: %gint, set image yoffset
|
||||
*
|
||||
* Copy an image, optionally modifying the header. VIPS copies images by
|
||||
* copying pointers, so this operation is instant, even for very large images.
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
* - better rounding
|
||||
* 9/5/15
|
||||
* - add max_alpha to match vips_premultiply() etc.
|
||||
* 25/5/16
|
||||
* - max_alpha defaults to 65535 for RGB16/GREY16
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -320,6 +322,14 @@ vips_flatten_build( VipsObject *object )
|
|||
|
||||
conversion->out->Bands -= 1;
|
||||
|
||||
/* Is max-alpha unset? Default to the correct value for this
|
||||
* interpretation.
|
||||
*/
|
||||
if( !vips_object_argument_isset( object, "max_alpha" ) )
|
||||
if( in->Type == VIPS_INTERPRETATION_GREY16 ||
|
||||
in->Type == VIPS_INTERPRETATION_RGB16 )
|
||||
flatten->max_alpha = 65535;
|
||||
|
||||
/* Is the background black? We have a special path for this.
|
||||
*/
|
||||
black = TRUE;
|
||||
|
@ -421,9 +431,9 @@ vips_flatten_init( VipsFlatten *flatten )
|
|||
* Non-complex images only.
|
||||
* @background defaults to zero (black).
|
||||
*
|
||||
* @max_alpha has the default value 255. You will need to set this to 65535
|
||||
* for images with a 16-bit alpha, or perhaps 1.0 for images with a float
|
||||
* alpha.
|
||||
* @max_alpha has the default value 255, or 65535 for images tagged as
|
||||
* #VIPS_INTERPRETATION_RGB16 or
|
||||
* #VIPS_INTERPRETATION_GREY16.
|
||||
*
|
||||
* Useful for flattening PNG images to RGB.
|
||||
*
|
||||
|
|
|
@ -283,6 +283,9 @@ vips_blend_gen( VipsRegion *or, void *seq, void *client1, void *client2,
|
|||
else {
|
||||
/* Mix of set and clear ... ask for both then and else parts
|
||||
* and interleave.
|
||||
*
|
||||
* We can't use vips_reorder_prepare_many() since we always
|
||||
* want the c image first.
|
||||
*/
|
||||
if( vips_region_prepare( ir[0], r ) ||
|
||||
vips_region_prepare( ir[1], r ) )
|
||||
|
|
|
@ -269,7 +269,7 @@ vips_msb_init( VipsMsb *msb )
|
|||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @band: msb just this band
|
||||
* * @band: %gint, msb just this band
|
||||
*
|
||||
* Turn any integer image to 8-bit unsigned char by discarding all but the most
|
||||
* significant byte. Signed values are converted to unsigned by adding 128.
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
*
|
||||
* 11/4/16 Lovell
|
||||
* - fix RGBA path
|
||||
* 25/5/16
|
||||
* - max_alpha defaults to 65535 for RGB16/GREY16
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -214,6 +216,14 @@ vips_premultiply_build( VipsObject *object )
|
|||
VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
/* Is max-alpha unset? Default to the correct value for this
|
||||
* interpretation.
|
||||
*/
|
||||
if( !vips_object_argument_isset( object, "max_alpha" ) )
|
||||
if( in->Type == VIPS_INTERPRETATION_GREY16 ||
|
||||
in->Type == VIPS_INTERPRETATION_RGB16 )
|
||||
premultiply->max_alpha = 65535;
|
||||
|
||||
if( in->BandFmt == VIPS_FORMAT_DOUBLE )
|
||||
conversion->out->BandFmt = VIPS_FORMAT_DOUBLE;
|
||||
else
|
||||
|
@ -294,9 +304,9 @@ vips_premultiply_init( VipsPremultiply *premultiply )
|
|||
* #VIPS_FORMAT_FLOAT unless the input format is #VIPS_FORMAT_DOUBLE, in which
|
||||
* case the output is double as well.
|
||||
*
|
||||
* @max_alpha has the default value 255. You will need to set this to 65535
|
||||
* for images with a 16-bit alpha, or perhaps 1.0 for images with a float
|
||||
* alpha.
|
||||
* @max_alpha has the default value 255, or 65535 for images tagged as
|
||||
* #VIPS_INTERPRETATION_RGB16 or
|
||||
* #VIPS_INTERPRETATION_GREY16.
|
||||
*
|
||||
* Non-complex images only.
|
||||
*
|
||||
|
|
|
@ -383,7 +383,10 @@ vips_rot_init( VipsRot *rot )
|
|||
*
|
||||
* Rotate @in by a multiple of 90 degrees.
|
||||
*
|
||||
* See also: vips_flip().
|
||||
* Use vips_similarity() to rotate by an arbitary angle. vips_rot45() is
|
||||
* useful for rotating convolution masks by 45 degrees.
|
||||
*
|
||||
* See also: vips_flip(), vips_similarity(), vips_rot45().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
|
|
|
@ -291,12 +291,15 @@ vips_rot45_init( VipsRot45 *rot45 )
|
|||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @angle: rotation angle
|
||||
* * @angle: #VipsAngle45 rotation angle
|
||||
*
|
||||
* Rotate @in by a multiple of 45 degrees. Odd-length sides and square images
|
||||
* only.
|
||||
*
|
||||
* See also: vips_rot().
|
||||
* This operation is useful for rotating convolution masks. Use
|
||||
* vips_similarity() to rotate images by arbitrary angles.
|
||||
*
|
||||
* See also: vips_rot(), vips_similarity().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
|
|
|
@ -124,7 +124,6 @@ vips_sequential_generate( VipsRegion *or,
|
|||
void *seq, void *a, void *b, gboolean *stop )
|
||||
{
|
||||
VipsSequential *sequential = (VipsSequential *) b;
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( sequential );
|
||||
VipsRect *r = &or->valid;
|
||||
VipsRegion *ir = (VipsRegion *) seq;
|
||||
|
||||
|
@ -132,8 +131,7 @@ vips_sequential_generate( VipsRegion *or,
|
|||
g_thread_self(), r->top, r->height );
|
||||
|
||||
if( sequential->trace )
|
||||
vips_info( class->nickname,
|
||||
"request for line %d, height %d",
|
||||
g_info( "request for line %d, height %d",
|
||||
r->top, r->height );
|
||||
|
||||
VIPS_GATE_START( "vips_sequential_generate: wait" );
|
||||
|
@ -184,7 +182,7 @@ vips_sequential_generate( VipsRegion *or,
|
|||
/* Exit the loop on timeout or condition passes. We have to
|
||||
* be wary of spurious wakeups.
|
||||
*/
|
||||
while( r->top > sequential->y_pos )
|
||||
while( r->top > sequential->y_pos ) {
|
||||
#ifdef HAVE_COND_INIT
|
||||
if( !g_cond_wait_until( sequential->ready,
|
||||
sequential->lock, time ) )
|
||||
|
@ -195,6 +193,14 @@ vips_sequential_generate( VipsRegion *or,
|
|||
break;
|
||||
#endif
|
||||
|
||||
/* We may have woken up because of an eval error.
|
||||
*/
|
||||
if( sequential->error ) {
|
||||
g_mutex_unlock( sequential->lock );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
VIPS_GATE_STOP( "vips_sequential_generate: wait" );
|
||||
|
||||
VIPS_DEBUG_MSG_GREEN( "thread %p awake again ...\n",
|
||||
|
@ -220,7 +226,7 @@ vips_sequential_generate( VipsRegion *or,
|
|||
area.width = 1;
|
||||
area.height = r->top - sequential->y_pos;
|
||||
if( vips_region_prepare( ir, &area ) ) {
|
||||
VIPS_DEBUG_MSG( "thread %p error, unlocking ...\n",
|
||||
VIPS_DEBUG_MSG( "thread %p error, unlocking #1 ...\n",
|
||||
g_thread_self() );
|
||||
sequential->error = -1;
|
||||
g_cond_broadcast( sequential->ready );
|
||||
|
@ -237,7 +243,7 @@ vips_sequential_generate( VipsRegion *or,
|
|||
VIPS_DEBUG_MSG_GREEN( "thread %p reading ...\n", g_thread_self() );
|
||||
if( vips_region_prepare( ir, r ) ||
|
||||
vips_region_region( or, ir, r, r->left, r->top ) ) {
|
||||
VIPS_DEBUG_MSG( "thread %p error, unlocking ...\n",
|
||||
VIPS_DEBUG_MSG( "thread %p error, unlocking #2 ...\n",
|
||||
g_thread_self() );
|
||||
sequential->error = -1;
|
||||
g_cond_broadcast( sequential->ready );
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
* - cache minimisation is optional, see "persistent" flag
|
||||
* 26/8/14 Lovell
|
||||
* - free the hash table in _dispose()
|
||||
* 11/7/16
|
||||
* - terminate on tile calc error
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -603,12 +605,14 @@ vips_tile_cache_gen( VipsRegion *or,
|
|||
{
|
||||
VipsRegion *in = (VipsRegion *) seq;
|
||||
VipsBlockCache *cache = (VipsBlockCache *) b;
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( cache );
|
||||
VipsRect *r = &or->valid;
|
||||
|
||||
VipsTile *tile;
|
||||
GSList *work;
|
||||
GSList *p;
|
||||
int result;
|
||||
|
||||
result = 0;
|
||||
|
||||
VIPS_GATE_START( "vips_tile_cache_gen: wait1" );
|
||||
|
||||
|
@ -658,8 +662,6 @@ vips_tile_cache_gen( VipsRegion *or,
|
|||
tile = (VipsTile *) p->data;
|
||||
|
||||
if( tile->state == VIPS_TILE_STATE_PEND ) {
|
||||
int result;
|
||||
|
||||
tile->state = VIPS_TILE_STATE_CALC;
|
||||
|
||||
VIPS_DEBUG_MSG_RED( "vips_tile_cache_gen: "
|
||||
|
@ -688,25 +690,23 @@ vips_tile_cache_gen( VipsRegion *or,
|
|||
}
|
||||
|
||||
/* If there was an error calculating this
|
||||
* tile, just warn and carry on.
|
||||
* tile, black it out and terminate
|
||||
* calculation. We have to stop so we can
|
||||
* support things like --fail on jpegload.
|
||||
*
|
||||
* This can happen with things like reading
|
||||
* .scn files via openslide. We don't want the
|
||||
* read to fail because of one broken tile.
|
||||
* Don't return early, we'd deadlock.
|
||||
*/
|
||||
if( result ) {
|
||||
VIPS_DEBUG_MSG_RED(
|
||||
"vips_tile_cache_gen: "
|
||||
"error on tile %p\n", tile );
|
||||
|
||||
vips_warn( class->nickname,
|
||||
_( "error reading tile %dx%d: "
|
||||
"%s" ),
|
||||
tile->pos.left, tile->pos.top,
|
||||
vips_error_buffer() );
|
||||
vips_error_clear();
|
||||
g_warning( _( "error in tile %d x %d" ),
|
||||
tile->pos.left, tile->pos.top );
|
||||
|
||||
vips_region_black( tile->region );
|
||||
|
||||
*stop = TRUE;
|
||||
}
|
||||
|
||||
tile->state = VIPS_TILE_STATE_DATA;
|
||||
|
@ -749,7 +749,7 @@ vips_tile_cache_gen( VipsRegion *or,
|
|||
|
||||
g_mutex_unlock( cache->lock );
|
||||
|
||||
return( 0 );
|
||||
return( result );
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* Author: John Cupitt
|
||||
* Written on: 7/5/15
|
||||
*
|
||||
* 25/5/16
|
||||
* - max_alpha defaults to 65535 for RGB16/GREY16
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -65,7 +67,7 @@ typedef VipsConversionClass VipsUnpremultiplyClass;
|
|||
|
||||
G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION );
|
||||
|
||||
/* Unpremultiply a greyscale (two band) image.
|
||||
/* Unpremultiply an N-band image.
|
||||
*/
|
||||
#define UNPRE_MANY( IN, OUT ) { \
|
||||
IN * restrict p = (IN *) in; \
|
||||
|
@ -223,6 +225,14 @@ vips_unpremultiply_build( VipsObject *object )
|
|||
VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
/* Is max-alpha unset? Default to the correct value for this
|
||||
* interpretation.
|
||||
*/
|
||||
if( !vips_object_argument_isset( object, "max_alpha" ) )
|
||||
if( in->Type == VIPS_INTERPRETATION_GREY16 ||
|
||||
in->Type == VIPS_INTERPRETATION_RGB16 )
|
||||
unpremultiply->max_alpha = 65535;
|
||||
|
||||
if( in->BandFmt == VIPS_FORMAT_DOUBLE )
|
||||
conversion->out->BandFmt = VIPS_FORMAT_DOUBLE;
|
||||
else
|
||||
|
@ -307,9 +317,9 @@ vips_unpremultiply_init( VipsUnpremultiply *unpremultiply )
|
|||
* #VIPS_FORMAT_FLOAT unless the input format is #VIPS_FORMAT_DOUBLE, in which
|
||||
* case the output is double as well.
|
||||
*
|
||||
* @max_alpha has the default value 255. You will need to set this to 65535
|
||||
* for images with a 16-bit alpha, or perhaps 1.0 for images with a float
|
||||
* alpha.
|
||||
* @max_alpha has the default value 255, or 65535 for images tagged as
|
||||
* #VIPS_INTERPRETATION_RGB16 or
|
||||
* #VIPS_INTERPRETATION_GREY16.
|
||||
*
|
||||
* Non-complex images only.
|
||||
*
|
||||
|
|
|
@ -98,14 +98,6 @@ typedef VipsConversionClass VipsZoomClass;
|
|||
|
||||
G_DEFINE_TYPE( VipsZoom, vips_zoom, VIPS_TYPE_CONVERSION );
|
||||
|
||||
/* Round N down to P boundary.
|
||||
*/
|
||||
#define ROUND_DOWN( N, P ) ((N) - ((N) % P))
|
||||
|
||||
/* Round N up to P boundary.
|
||||
*/
|
||||
#define ROUND_UP( N, P ) (ROUND_DOWN( (N) + (P) - 1, (P) ))
|
||||
|
||||
/* Paint the part of the region containing only whole pels.
|
||||
*/
|
||||
static void
|
||||
|
@ -265,10 +257,10 @@ vips_zoom_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
|
|||
/* Area of input we need. We have to round out, as we may have
|
||||
* part-pixels all around the edges.
|
||||
*/
|
||||
left = ROUND_DOWN( r->left, zoom->xfac );
|
||||
right = ROUND_UP( ri, zoom->xfac );
|
||||
top = ROUND_DOWN( r->top, zoom->yfac );
|
||||
bottom = ROUND_UP( bo, zoom->yfac );
|
||||
left = VIPS_ROUND_DOWN( r->left, zoom->xfac );
|
||||
right = VIPS_ROUND_UP( ri, zoom->xfac );
|
||||
top = VIPS_ROUND_DOWN( r->top, zoom->yfac );
|
||||
bottom = VIPS_ROUND_UP( bo, zoom->yfac );
|
||||
width = right - left;
|
||||
height = bottom - top;
|
||||
s.left = left / zoom->xfac;
|
||||
|
@ -280,10 +272,10 @@ vips_zoom_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
|
|||
|
||||
/* Find the part of the output (if any) which uses only whole pels.
|
||||
*/
|
||||
left = ROUND_UP( r->left, zoom->xfac );
|
||||
right = ROUND_DOWN( ri, zoom->xfac );
|
||||
top = ROUND_UP( r->top, zoom->yfac );
|
||||
bottom = ROUND_DOWN( bo, zoom->yfac );
|
||||
left = VIPS_ROUND_UP( r->left, zoom->xfac );
|
||||
right = VIPS_ROUND_DOWN( ri, zoom->xfac );
|
||||
top = VIPS_ROUND_UP( r->top, zoom->yfac );
|
||||
bottom = VIPS_ROUND_DOWN( bo, zoom->yfac );
|
||||
width = right - left;
|
||||
height = bottom - top;
|
||||
|
||||
|
|
|
@ -6,15 +6,15 @@ libconvolution_la_SOURCES = \
|
|||
correlation.c \
|
||||
correlation.h \
|
||||
conv.c \
|
||||
conva.c \
|
||||
convf.c \
|
||||
convi.c \
|
||||
convasep.c \
|
||||
convsep.c \
|
||||
compass.c \
|
||||
fastcor.c \
|
||||
spcor.c \
|
||||
sharpen.c \
|
||||
gaussblur.c \
|
||||
im_aconv.c \
|
||||
im_aconvsep.c \
|
||||
im_conv.c \
|
||||
im_conv_f.c
|
||||
gaussblur.c
|
||||
|
||||
AM_CPPFLAGS = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@
|
||||
|
|
|
@ -63,21 +63,17 @@ G_DEFINE_TYPE( VipsConv, vips_conv, VIPS_TYPE_CONVOLUTION );
|
|||
static int
|
||||
vips_conv_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsConvolution *convolution = (VipsConvolution *) object;
|
||||
VipsConv *conv = (VipsConv *) object;
|
||||
VipsImage **t = (VipsImage **)
|
||||
vips_object_local_array( object, 4 );
|
||||
VipsImage **t = (VipsImage **) vips_object_local_array( object, 4 );
|
||||
|
||||
VipsImage *in;
|
||||
INTMASK *imsk;
|
||||
DOUBLEMASK *dmsk;
|
||||
|
||||
g_object_set( conv, "out", vips_image_new(), NULL );
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_conv_parent_class )->build( object ) )
|
||||
return( -1 );
|
||||
|
||||
g_object_set( conv, "out", vips_image_new(), NULL );
|
||||
|
||||
in = convolution->in;
|
||||
|
||||
/*
|
||||
|
@ -85,13 +81,6 @@ vips_conv_build( VipsObject *object )
|
|||
vips_matrixprint( convolution->M, NULL );
|
||||
*/
|
||||
|
||||
if( !(imsk = im_vips2imask( convolution->M, class->nickname )) ||
|
||||
!im_local_imask( convolution->out, imsk ) )
|
||||
return( -1 );
|
||||
if( !(dmsk = im_vips2mask( convolution->M, class->nickname )) ||
|
||||
!im_local_dmask( convolution->out, dmsk ) )
|
||||
return( -1 );
|
||||
|
||||
/* Unpack for processing.
|
||||
*/
|
||||
if( vips_image_decode( in, &t[0] ) )
|
||||
|
@ -99,19 +88,24 @@ vips_conv_build( VipsObject *object )
|
|||
in = t[0];
|
||||
|
||||
switch( conv->precision ) {
|
||||
case VIPS_PRECISION_INTEGER:
|
||||
if( im_conv( in, convolution->out, imsk ) )
|
||||
case VIPS_PRECISION_FLOAT:
|
||||
if( vips_convf( in, &t[1], convolution->M, NULL ) ||
|
||||
vips_image_write( t[1], convolution->out ) )
|
||||
return( -1 );
|
||||
break;
|
||||
|
||||
case VIPS_PRECISION_FLOAT:
|
||||
if( im_conv_f( in, convolution->out, dmsk ) )
|
||||
case VIPS_PRECISION_INTEGER:
|
||||
if( vips_convi( in, &t[1], convolution->M, NULL ) ||
|
||||
vips_image_write( t[1], convolution->out ) )
|
||||
return( -1 );
|
||||
break;
|
||||
|
||||
case VIPS_PRECISION_APPROXIMATE:
|
||||
if( im_aconv( in, convolution->out, dmsk,
|
||||
conv->layers, conv->cluster ) )
|
||||
if( vips_conva( in, &t[1], convolution->M,
|
||||
"layers", conv->layers,
|
||||
"cluster", conv->cluster,
|
||||
NULL ) ||
|
||||
vips_image_write( t[1], convolution->out ) )
|
||||
return( -1 );
|
||||
break;
|
||||
|
||||
|
@ -119,6 +113,9 @@ vips_conv_build( VipsObject *object )
|
|||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
vips_reorder_margin_hint( convolution->out,
|
||||
convolution->M->Xsize * convolution->M->Ysize );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
|
@ -175,31 +172,40 @@ vips_conv_init( VipsConv *conv )
|
|||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @precision: calculation accuracy
|
||||
* * @layers: number of layers for approximation
|
||||
* * @cluster: cluster lines closer than this distance
|
||||
* * @precision: #VipsPrecision, calculation accuracy
|
||||
* * @layers: %gint, number of layers for approximation
|
||||
* * @cluster: %gint, cluster lines closer than this distance
|
||||
*
|
||||
* Convolution.
|
||||
*
|
||||
* Perform a convolution of @in with @mask.
|
||||
* Each output pixel is
|
||||
* calculated as sigma[i]{pixel[i] * mask[i]} / scale + offset, where scale
|
||||
* and offset are part of @mask.
|
||||
* Each output pixel is calculated as:
|
||||
*
|
||||
* If @precision is #VIPS_PRECISION_INTEGER then the convolution is performed
|
||||
* with integer arithmetic and the output image
|
||||
* |[
|
||||
* sigma[i]{pixel[i] * mask[i]} / scale + offset
|
||||
* ]|
|
||||
*
|
||||
* where scale and offset are part of @mask.
|
||||
*
|
||||
* If @precision is #VIPS_PRECISION_INTEGER, then
|
||||
* elements of @mask are converted to
|
||||
* integers before convolution, using rint(),
|
||||
* and the output image
|
||||
* always has the same #VipsBandFormat as the input image.
|
||||
*
|
||||
* Convolutions on unsigned 8-bit images are calculated with the
|
||||
* processor's vector unit, if possible. Disable this with --vips-novector or
|
||||
* IM_NOVECTOR.
|
||||
* For #VIPS_FORMAT_UCHAR images, vips_conv() uses a fast vector path based on
|
||||
* fixed-point arithmetic. This can produce slightly different results.
|
||||
* Disable the vector path with `--vips-novector` or `VIPS_NOVECTOR` or
|
||||
* vips_vector_set_enabled().
|
||||
*
|
||||
* If @precision is #VIPS_PRECISION_FLOAT then the convolution is performed
|
||||
* with floating-point arithmetic. The output image
|
||||
* is always %VIPS_FORMAT_FLOAT unless @in is %VIPS_FORMAT_DOUBLE, in which case
|
||||
* @out is also %VIPS_FORMAT_DOUBLE.
|
||||
* is always #VIPS_FORMAT_FLOAT unless @in is #VIPS_FORMAT_DOUBLE, in which case
|
||||
* @out is also #VIPS_FORMAT_DOUBLE.
|
||||
*
|
||||
* If @precision is #VIPS_PRECISION_APPROXIMATE then the output image
|
||||
* If @precision is #VIPS_PRECISION_APPROXIMATE then, like
|
||||
* #VIPS_PRECISION_INTEGER, @mask is converted to int before convolution, and
|
||||
* the output image
|
||||
* always has the same #VipsBandFormat as the input image.
|
||||
*
|
||||
* Larger values for @layers give more accurate
|
||||
|
@ -211,6 +217,8 @@ vips_conv_init( VipsConv *conv )
|
|||
* Smaller values of @cluster will give more accurate results, but be slower
|
||||
* and use more memory. 10% of the mask radius is a good rule of thumb.
|
||||
*
|
||||
* See also: vips_convsep().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,961 @@
|
|||
/* convasep ... separable approximate convolution
|
||||
*
|
||||
* This operation does an approximate, seperable convolution.
|
||||
*
|
||||
* Author: John Cupitt & Nicolas Robidoux
|
||||
* Written on: 31/5/11
|
||||
* Modified on:
|
||||
* 31/5/11
|
||||
* - from im_conv()
|
||||
* 5/7/16
|
||||
* - redone as a class
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
See:
|
||||
|
||||
http://incubator.quasimondo.com/processing/stackblur.pde
|
||||
|
||||
This thing is a little like stackblur, but generalised to any separable
|
||||
mask.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
TODO
|
||||
|
||||
- how about making a cumulative image and then subtracting points in
|
||||
that, rather than keeping a set of running totals
|
||||
|
||||
faster?
|
||||
|
||||
we could then use orc to write a bit of code to implement this set
|
||||
of lines
|
||||
|
||||
stackoverflow has an algorithm for cumulativization using SIMD and
|
||||
threads, see that font rasterization with rust piece on medium by
|
||||
ralph levien
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
#define VIPS_DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/vector.h>
|
||||
#include <vips/debug.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "pconvolution.h"
|
||||
|
||||
/* Maximum number of lines we can break the mask into.
|
||||
*/
|
||||
#define MAX_LINES (1000)
|
||||
|
||||
/* Euclid's algorithm. Use this to common up mults.
|
||||
*/
|
||||
static int
|
||||
gcd( int a, int b )
|
||||
{
|
||||
if( b == 0 )
|
||||
return( abs( a ) );
|
||||
else
|
||||
return( gcd( b, a % b ) );
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
VipsConvolution parent_instance;
|
||||
|
||||
int layers;
|
||||
|
||||
int divisor;
|
||||
int rounding;
|
||||
int offset;
|
||||
|
||||
/* The "width" of the mask, ie. n for our 1xn or nx1 argument, plus
|
||||
* an int version of our mask.
|
||||
*/
|
||||
int width;
|
||||
VipsImage *iM;
|
||||
|
||||
/* The mask broken into a set of lines.
|
||||
*
|
||||
* Start is the left-most pixel in the line, end is one beyond the
|
||||
* right-most pixel.
|
||||
*/
|
||||
int n_lines;
|
||||
int start[MAX_LINES];
|
||||
int end[MAX_LINES];
|
||||
int factor[MAX_LINES];
|
||||
} VipsConvasep;
|
||||
|
||||
typedef VipsConvolutionClass VipsConvasepClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsConvasep, vips_convasep, VIPS_TYPE_CONVOLUTION );
|
||||
|
||||
static void
|
||||
vips_convasep_line_start( VipsConvasep *convasep, int x, int factor )
|
||||
{
|
||||
convasep->start[convasep->n_lines] = x;
|
||||
convasep->factor[convasep->n_lines] = factor;
|
||||
}
|
||||
|
||||
static int
|
||||
vips_convasep_line_end( VipsConvasep *convasep, int x )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( convasep );
|
||||
|
||||
convasep->end[convasep->n_lines] = x;
|
||||
|
||||
if( convasep->n_lines >= MAX_LINES - 1 ) {
|
||||
vips_error( class->nickname, "%s", _( "mask too complex" ) );
|
||||
return( -1 );
|
||||
}
|
||||
convasep->n_lines += 1;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Break a mask into lines.
|
||||
*/
|
||||
static int
|
||||
vips_convasep_decompose( VipsConvasep *convasep )
|
||||
{
|
||||
VipsImage *iM = convasep->iM;
|
||||
double *coeff = (double *) VIPS_IMAGE_ADDR( iM, 0, 0 );
|
||||
double scale = vips_image_get_scale( iM );
|
||||
double offset = vips_image_get_offset( iM );
|
||||
|
||||
double max;
|
||||
double min;
|
||||
double depth;
|
||||
double sum;
|
||||
double area;
|
||||
int layers;
|
||||
int layers_above;
|
||||
int layers_below;
|
||||
int z, n, x;
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_convasep_decompose: "
|
||||
"breaking into %d layers ...\n", convasep->layers );
|
||||
|
||||
/* Find mask range. We must always include the zero axis in the mask.
|
||||
*/
|
||||
max = 0;
|
||||
min = 0;
|
||||
for( x = 0; x < convasep->width; x++ ) {
|
||||
if( coeff[x] > max )
|
||||
max = coeff[x];
|
||||
if( coeff[x] < min )
|
||||
min = coeff[x];
|
||||
}
|
||||
|
||||
/* The zero axis must fall on a layer boundary. Estimate the
|
||||
* depth, find n-lines-above-zero, get exact depth, then calculate a
|
||||
* fixed n-lines which includes any negative parts.
|
||||
*/
|
||||
depth = (max - min) / convasep->layers;
|
||||
layers_above = ceil( max / depth );
|
||||
depth = max / layers_above;
|
||||
layers_below = floor( min / depth );
|
||||
layers = layers_above - layers_below;
|
||||
|
||||
VIPS_DEBUG_MSG( "depth = %g, layers = %d\n", depth, layers );
|
||||
|
||||
/* For each layer, generate a set of lines which are inside the
|
||||
* perimeter. Work down from the top.
|
||||
*/
|
||||
for( z = 0; z < layers; z++ ) {
|
||||
double y = max - (1 + z) * depth;
|
||||
|
||||
/* y plus half depth ... ie. the layer midpoint.
|
||||
*/
|
||||
double y_ph = y + depth / 2;
|
||||
|
||||
/* Odd, but we must avoid rounding errors that make us miss 0
|
||||
* in the line above.
|
||||
*/
|
||||
int y_positive = z < layers_above;
|
||||
|
||||
int inside;
|
||||
|
||||
/* Start outside the perimeter.
|
||||
*/
|
||||
inside = 0;
|
||||
|
||||
for( x = 0; x < convasep->width; x++ ) {
|
||||
/* The vertical line from mask[z] to 0 is inside. Is
|
||||
* our current square (x, y) part of that line?
|
||||
*/
|
||||
if( (y_positive && coeff[x] >= y_ph) ||
|
||||
(!y_positive && coeff[x] <= y_ph) ) {
|
||||
if( !inside ) {
|
||||
vips_convasep_line_start( convasep, x,
|
||||
y_positive ? 1 : -1 );
|
||||
inside = 1;
|
||||
}
|
||||
}
|
||||
else if( inside ) {
|
||||
if( vips_convasep_line_end( convasep, x ) )
|
||||
return( -1 );
|
||||
inside = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( inside &&
|
||||
vips_convasep_line_end( convasep, convasep->width ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Can we common up any lines? Search for lines with identical
|
||||
* start/end.
|
||||
*/
|
||||
for( z = 0; z < convasep->n_lines; z++ ) {
|
||||
for( n = z + 1; n < convasep->n_lines; n++ ) {
|
||||
if( convasep->start[z] == convasep->start[n] &&
|
||||
convasep->end[z] == convasep->end[n] ) {
|
||||
convasep->factor[z] += convasep->factor[n];
|
||||
|
||||
/* n can be deleted. Do this in a separate
|
||||
* pass below.
|
||||
*/
|
||||
convasep->factor[n] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we can remove all factor 0 lines.
|
||||
*/
|
||||
for( z = 0; z < convasep->n_lines; z++ ) {
|
||||
if( convasep->factor[z] == 0 ) {
|
||||
for( x = z; x < convasep->n_lines; x++ ) {
|
||||
convasep->start[x] = convasep->start[x + 1];
|
||||
convasep->end[x] = convasep->end[x + 1];
|
||||
convasep->factor[x] = convasep->factor[x + 1];
|
||||
}
|
||||
convasep->n_lines -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the area of the lines.
|
||||
*/
|
||||
area = 0;
|
||||
for( z = 0; z < convasep->n_lines; z++ )
|
||||
area += convasep->factor[z] *
|
||||
(convasep->end[z] - convasep->start[z]);
|
||||
|
||||
/* Strength reduction: if all lines are divisible by n, we can move
|
||||
* that n out into the ->area factor. The aim is to produce as many
|
||||
* factor 1 lines as we can and to reduce the chance of overflow.
|
||||
*/
|
||||
x = convasep->factor[0];
|
||||
for( z = 1; z < convasep->n_lines; z++ )
|
||||
x = gcd( x, convasep->factor[z] );
|
||||
for( z = 0; z < convasep->n_lines; z++ )
|
||||
convasep->factor[z] /= x;
|
||||
area *= x;
|
||||
|
||||
/* Find the area of the original mask.
|
||||
*/
|
||||
sum = 0;
|
||||
for( z = 0; z < convasep->width; z++ )
|
||||
sum += coeff[z];
|
||||
|
||||
convasep->divisor = VIPS_RINT( sum * area / scale );
|
||||
if( convasep->divisor == 0 )
|
||||
convasep->divisor = 1;
|
||||
convasep->rounding = (convasep->divisor + 1) / 2;
|
||||
convasep->offset = offset;
|
||||
|
||||
#ifdef DEBUG
|
||||
/* ASCII-art layer drawing.
|
||||
*/
|
||||
printf( "lines:\n" );
|
||||
for( z = 0; z < convasep->n_lines; z++ ) {
|
||||
printf( "%3d - %2d x ", z, convasep->factor[z] );
|
||||
for( x = 0; x < 55; x++ ) {
|
||||
int rx = x * (convasep->width + 1) / 55;
|
||||
|
||||
if( rx >= convasep->start[z] && rx < convasep->end[z] )
|
||||
printf( "#" );
|
||||
else
|
||||
printf( " " );
|
||||
}
|
||||
printf( " %3d .. %3d\n", convasep->start[z], convasep->end[z] );
|
||||
}
|
||||
printf( "divisor = %d\n", convasep->divisor );
|
||||
printf( "rounding = %d\n", convasep->rounding );
|
||||
printf( "offset = %d\n", convasep->offset );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Our sequence value.
|
||||
*/
|
||||
typedef struct {
|
||||
VipsConvasep *convasep;
|
||||
|
||||
VipsRegion *ir; /* Input region */
|
||||
|
||||
int *start; /* Offsets for start and stop */
|
||||
int *end;
|
||||
|
||||
/* The sums for each line. int for integer types, double for floating
|
||||
* point types.
|
||||
*/
|
||||
int *isum;
|
||||
double *dsum;
|
||||
|
||||
int last_stride; /* Avoid recalcing offsets, if we can */
|
||||
} VipsConvasepSeq;
|
||||
|
||||
/* Free a sequence value.
|
||||
*/
|
||||
static int
|
||||
vips_convasep_stop( void *vseq, void *a, void *b )
|
||||
{
|
||||
VipsConvasepSeq *seq = (VipsConvasepSeq *) vseq;
|
||||
|
||||
VIPS_UNREF( seq->ir );
|
||||
VIPS_FREE( seq->start );
|
||||
VIPS_FREE( seq->end );
|
||||
VIPS_FREE( seq->isum );
|
||||
VIPS_FREE( seq->dsum );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Convolution start function.
|
||||
*/
|
||||
static void *
|
||||
vips_convasep_start( VipsImage *out, void *a, void *b )
|
||||
{
|
||||
VipsImage *in = (IMAGE *) a;
|
||||
VipsConvasep *convasep = (VipsConvasep *) b;
|
||||
|
||||
VipsConvasepSeq *seq;
|
||||
|
||||
if( !(seq = VIPS_NEW( out, VipsConvasepSeq )) )
|
||||
return( NULL );
|
||||
|
||||
/* Init!
|
||||
*/
|
||||
seq->convasep = convasep;
|
||||
seq->ir = vips_region_new( in );
|
||||
seq->start = VIPS_ARRAY( NULL, convasep->n_lines, int );
|
||||
seq->end = VIPS_ARRAY( NULL, convasep->n_lines, int );
|
||||
seq->isum = NULL;
|
||||
seq->dsum = NULL;
|
||||
if( vips_band_format_isint( out->BandFmt ) )
|
||||
seq->isum = VIPS_ARRAY( NULL, convasep->n_lines, int );
|
||||
else
|
||||
seq->dsum = VIPS_ARRAY( NULL, convasep->n_lines, double );
|
||||
seq->last_stride = -1;
|
||||
|
||||
if( !seq->ir ||
|
||||
!seq->start ||
|
||||
!seq->end ||
|
||||
(!seq->isum && !seq->dsum) ) {
|
||||
vips_convasep_stop( seq, in, convasep );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( seq );
|
||||
}
|
||||
|
||||
#define CLIP_UCHAR( V ) \
|
||||
G_STMT_START { \
|
||||
if( (V) < 0 ) \
|
||||
(V) = 0; \
|
||||
else if( (V) > UCHAR_MAX ) \
|
||||
(V) = UCHAR_MAX; \
|
||||
} G_STMT_END
|
||||
|
||||
#define CLIP_CHAR( V ) \
|
||||
G_STMT_START { \
|
||||
if( (V) < SCHAR_MIN ) \
|
||||
(V) = SCHAR_MIN; \
|
||||
else if( (V) > SCHAR_MAX ) \
|
||||
(V) = SCHAR_MAX; \
|
||||
} G_STMT_END
|
||||
|
||||
#define CLIP_USHORT( V ) \
|
||||
G_STMT_START { \
|
||||
if( (V) < 0 ) \
|
||||
(V) = 0; \
|
||||
else if( (V) > USHRT_MAX ) \
|
||||
(V) = USHRT_MAX; \
|
||||
} G_STMT_END
|
||||
|
||||
#define CLIP_SHORT( V ) \
|
||||
G_STMT_START { \
|
||||
if( (V) < SHRT_MIN ) \
|
||||
(V) = SHRT_MIN; \
|
||||
else if( (V) > SHRT_MAX ) \
|
||||
(V) = SHRT_MAX; \
|
||||
} G_STMT_END
|
||||
|
||||
#define CLIP_NONE( V ) {}
|
||||
|
||||
/* The h and v loops are very similar, but also annoyingly different. Keep
|
||||
* them separate for easy debugging.
|
||||
*/
|
||||
|
||||
#define HCONV_INT( TYPE, CLIP ) { \
|
||||
for( i = 0; i < bands; i++ ) { \
|
||||
int *isum = seq->isum; \
|
||||
\
|
||||
TYPE *q; \
|
||||
TYPE *p; \
|
||||
int sum; \
|
||||
\
|
||||
p = i + (TYPE *) VIPS_REGION_ADDR( ir, r->left, r->top + y ); \
|
||||
q = i + (TYPE *) VIPS_REGION_ADDR( or, r->left, r->top + y ); \
|
||||
\
|
||||
sum = 0; \
|
||||
for( z = 0; z < n_lines; z++ ) { \
|
||||
isum[z] = 0; \
|
||||
for( x = seq->start[z]; x < seq->end[z]; x += istride ) \
|
||||
isum[z] += p[x]; \
|
||||
sum += convasep->factor[z] * isum[z]; \
|
||||
} \
|
||||
\
|
||||
/* Don't add offset ... we only want to do that once, do it on \
|
||||
* the vertical pass. \
|
||||
*/ \
|
||||
sum = (sum + convasep->rounding) / convasep->divisor; \
|
||||
CLIP( sum ); \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
\
|
||||
for( x = 1; x < r->width; x++ ) { \
|
||||
sum = 0; \
|
||||
for( z = 0; z < n_lines; z++ ) { \
|
||||
isum[z] += p[seq->end[z]]; \
|
||||
isum[z] -= p[seq->start[z]]; \
|
||||
sum += convasep->factor[z] * isum[z]; \
|
||||
} \
|
||||
p += istride; \
|
||||
sum = (sum + convasep->rounding) / convasep->divisor; \
|
||||
CLIP( sum ); \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define HCONV_FLOAT( TYPE ) { \
|
||||
for( i = 0; i < bands; i++ ) { \
|
||||
double *dsum = seq->dsum; \
|
||||
\
|
||||
TYPE *q; \
|
||||
TYPE *p; \
|
||||
double sum; \
|
||||
\
|
||||
p = i + (TYPE *) VIPS_REGION_ADDR( ir, r->left, r->top + y ); \
|
||||
q = i + (TYPE *) VIPS_REGION_ADDR( or, r->left, r->top + y ); \
|
||||
\
|
||||
sum = 0; \
|
||||
for( z = 0; z < n_lines; z++ ) { \
|
||||
dsum[z] = 0; \
|
||||
for( x = seq->start[z]; x < seq->end[z]; x += istride ) \
|
||||
dsum[z] += p[x]; \
|
||||
sum += convasep->factor[z] * dsum[z]; \
|
||||
} \
|
||||
\
|
||||
/* Don't add offset ... we only want to do that once, do it on \
|
||||
* the vertical pass. \
|
||||
*/ \
|
||||
sum = sum / convasep->divisor; \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
\
|
||||
for( x = 1; x < r->width; x++ ) { \
|
||||
sum = 0; \
|
||||
for( z = 0; z < n_lines; z++ ) { \
|
||||
dsum[z] += p[seq->end[z]]; \
|
||||
dsum[z] -= p[seq->start[z]]; \
|
||||
sum += convasep->factor[z] * dsum[z]; \
|
||||
} \
|
||||
p += istride; \
|
||||
sum = sum / convasep->divisor; \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Do horizontal masks ... we scan the mask along scanlines.
|
||||
*/
|
||||
static int
|
||||
vips_convasep_generate_horizontal( VipsRegion *or,
|
||||
void *vseq, void *a, void *b, gboolean *stop )
|
||||
{
|
||||
VipsConvasepSeq *seq = (VipsConvasepSeq *) vseq;
|
||||
VipsImage *in = (VipsImage *) a;
|
||||
VipsConvasep *convasep = (VipsConvasep *) b;
|
||||
VipsConvolution *convolution = (VipsConvolution *) convasep;
|
||||
|
||||
VipsRegion *ir = seq->ir;
|
||||
const int n_lines = convasep->n_lines;
|
||||
VipsRect *r = &or->valid;
|
||||
|
||||
/* Double the bands (notionally) for complex.
|
||||
*/
|
||||
int bands = vips_band_format_iscomplex( in->BandFmt ) ?
|
||||
2 * in->Bands : in->Bands;
|
||||
|
||||
VipsRect s;
|
||||
int x, y, z, i;
|
||||
int istride;
|
||||
int ostride;
|
||||
|
||||
/* Prepare the section of the input image we need. A little larger
|
||||
* than the section of the output image we are producing.
|
||||
*/
|
||||
s = *r;
|
||||
s.width += convasep->width - 1;
|
||||
if( vips_region_prepare( ir, &s ) )
|
||||
return( -1 );
|
||||
|
||||
/* Stride can be different for the vertical case, keep this here for
|
||||
* ease of direction change.
|
||||
*/
|
||||
istride = VIPS_IMAGE_SIZEOF_PEL( in ) /
|
||||
VIPS_IMAGE_SIZEOF_ELEMENT( in );
|
||||
ostride = VIPS_IMAGE_SIZEOF_PEL( convolution->out ) /
|
||||
VIPS_IMAGE_SIZEOF_ELEMENT( convolution->out );
|
||||
|
||||
/* Init offset array.
|
||||
*/
|
||||
if( seq->last_stride != istride ) {
|
||||
seq->last_stride = istride;
|
||||
|
||||
for( z = 0; z < n_lines; z++ ) {
|
||||
seq->start[z] = convasep->start[z] * istride;
|
||||
seq->end[z] = convasep->end[z] * istride;
|
||||
}
|
||||
}
|
||||
|
||||
for( y = 0; y < r->height; y++ ) {
|
||||
switch( in->BandFmt ) {
|
||||
case VIPS_FORMAT_UCHAR:
|
||||
HCONV_INT( unsigned char, CLIP_UCHAR );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_CHAR:
|
||||
HCONV_INT( signed char, CLIP_CHAR );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_USHORT:
|
||||
HCONV_INT( unsigned short, CLIP_USHORT );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_SHORT:
|
||||
HCONV_INT( signed short, CLIP_SHORT );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_UINT:
|
||||
HCONV_INT( unsigned int, CLIP_NONE );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_INT:
|
||||
HCONV_INT( signed int, CLIP_NONE );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_FLOAT:
|
||||
case VIPS_FORMAT_COMPLEX:
|
||||
HCONV_FLOAT( float );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_DOUBLE:
|
||||
case VIPS_FORMAT_DPCOMPLEX:
|
||||
HCONV_FLOAT( double );
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#define VCONV_INT( TYPE, CLIP ) { \
|
||||
for( x = 0; x < sz; x++ ) { \
|
||||
int *isum = seq->isum; \
|
||||
\
|
||||
TYPE *q; \
|
||||
TYPE *p; \
|
||||
int sum; \
|
||||
\
|
||||
p = x + (TYPE *) VIPS_REGION_ADDR( ir, r->left, r->top ); \
|
||||
q = x + (TYPE *) VIPS_REGION_ADDR( or, r->left, r->top ); \
|
||||
\
|
||||
sum = 0; \
|
||||
for( z = 0; z < n_lines; z++ ) { \
|
||||
isum[z] = 0; \
|
||||
for( y = seq->start[z]; y < seq->end[z]; y += istride ) \
|
||||
isum[z] += p[y]; \
|
||||
sum += convasep->factor[z] * isum[z]; \
|
||||
} \
|
||||
sum = (sum + convasep->rounding) / convasep->divisor + \
|
||||
convasep->offset; \
|
||||
CLIP( sum ); \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
\
|
||||
for( y = 1; y < r->height; y++ ) { \
|
||||
sum = 0; \
|
||||
for( z = 0; z < n_lines; z++ ) { \
|
||||
isum[z] += p[seq->end[z]]; \
|
||||
isum[z] -= p[seq->start[z]]; \
|
||||
sum += convasep->factor[z] * isum[z]; \
|
||||
} \
|
||||
p += istride; \
|
||||
sum = (sum + convasep->rounding) / convasep->divisor + \
|
||||
convasep->offset; \
|
||||
CLIP( sum ); \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define VCONV_FLOAT( TYPE ) { \
|
||||
for( x = 0; x < sz; x++ ) { \
|
||||
double *dsum = seq->dsum; \
|
||||
\
|
||||
TYPE *q; \
|
||||
TYPE *p; \
|
||||
double sum; \
|
||||
\
|
||||
p = x + (TYPE *) VIPS_REGION_ADDR( ir, r->left, r->top ); \
|
||||
q = x + (TYPE *) VIPS_REGION_ADDR( or, r->left, r->top ); \
|
||||
\
|
||||
sum = 0; \
|
||||
for( z = 0; z < n_lines; z++ ) { \
|
||||
dsum[z] = 0; \
|
||||
for( y = seq->start[z]; y < seq->end[z]; y += istride ) \
|
||||
dsum[z] += p[y]; \
|
||||
sum += convasep->factor[z] * dsum[z]; \
|
||||
} \
|
||||
sum = sum / convasep->divisor + convasep->offset; \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
\
|
||||
for( y = 1; y < r->height; y++ ) { \
|
||||
sum = 0; \
|
||||
for( z = 0; z < n_lines; z++ ) { \
|
||||
dsum[z] += p[seq->end[z]]; \
|
||||
dsum[z] -= p[seq->start[z]]; \
|
||||
sum += convasep->factor[z] * dsum[z]; \
|
||||
} \
|
||||
p += istride; \
|
||||
sum = sum / convasep->divisor + convasep->offset; \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Do vertical masks ... we scan the mask down columns of pixels. Copy-paste
|
||||
* from above with small changes.
|
||||
*/
|
||||
static int
|
||||
vips_convasep_generate_vertical( VipsRegion *or,
|
||||
void *vseq, void *a, void *b, gboolean *stop )
|
||||
{
|
||||
VipsConvasepSeq *seq = (VipsConvasepSeq *) vseq;
|
||||
VipsImage *in = (VipsImage *) a;
|
||||
VipsConvasep *convasep = (VipsConvasep *) b;
|
||||
VipsConvolution *convolution = (VipsConvolution *) convasep;
|
||||
|
||||
VipsRegion *ir = seq->ir;
|
||||
const int n_lines = convasep->n_lines;
|
||||
VipsRect *r = &or->valid;
|
||||
|
||||
/* Double the width (notionally) for complex.
|
||||
*/
|
||||
int sz = vips_band_format_iscomplex( in->BandFmt ) ?
|
||||
2 * VIPS_REGION_N_ELEMENTS( or ) : VIPS_REGION_N_ELEMENTS( or );
|
||||
|
||||
VipsRect s;
|
||||
int x, y, z;
|
||||
int istride;
|
||||
int ostride;
|
||||
|
||||
/* Prepare the section of the input image we need. A little larger
|
||||
* than the section of the output image we are producing.
|
||||
*/
|
||||
s = *r;
|
||||
s.height += convasep->width - 1;
|
||||
if( vips_region_prepare( ir, &s ) )
|
||||
return( -1 );
|
||||
|
||||
/* Stride can be different for the vertical case, keep this here for
|
||||
* ease of direction change.
|
||||
*/
|
||||
istride = VIPS_REGION_LSKIP( ir ) / VIPS_IMAGE_SIZEOF_ELEMENT( in );
|
||||
ostride = VIPS_REGION_LSKIP( or ) /
|
||||
VIPS_IMAGE_SIZEOF_ELEMENT( convolution->out );
|
||||
|
||||
/* Init offset array.
|
||||
*/
|
||||
if( seq->last_stride != istride ) {
|
||||
seq->last_stride = istride;
|
||||
|
||||
for( z = 0; z < n_lines; z++ ) {
|
||||
seq->start[z] = convasep->start[z] * istride;
|
||||
seq->end[z] = convasep->end[z] * istride;
|
||||
}
|
||||
}
|
||||
|
||||
switch( in->BandFmt ) {
|
||||
case VIPS_FORMAT_UCHAR:
|
||||
VCONV_INT( unsigned char, CLIP_UCHAR );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_CHAR:
|
||||
VCONV_INT( signed char, CLIP_CHAR );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_USHORT:
|
||||
VCONV_INT( unsigned short, CLIP_USHORT );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_SHORT:
|
||||
VCONV_INT( signed short, CLIP_SHORT );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_UINT:
|
||||
VCONV_INT( unsigned int, CLIP_NONE );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_INT:
|
||||
VCONV_INT( signed int, CLIP_NONE );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_FLOAT:
|
||||
case VIPS_FORMAT_COMPLEX:
|
||||
VCONV_FLOAT( float );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_DOUBLE:
|
||||
case VIPS_FORMAT_DPCOMPLEX:
|
||||
VCONV_FLOAT( double );
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_convasep_pass( VipsConvasep *convasep,
|
||||
VipsImage *in, VipsImage **out, VipsDirection direction )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( convasep );
|
||||
|
||||
VipsGenerateFn gen;
|
||||
|
||||
*out = vips_image_new();
|
||||
if( vips_image_pipelinev( *out,
|
||||
VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
if( direction == VIPS_DIRECTION_HORIZONTAL ) {
|
||||
(*out)->Xsize -= convasep->width - 1;
|
||||
gen = vips_convasep_generate_horizontal;
|
||||
}
|
||||
else {
|
||||
(*out)->Ysize -= convasep->width - 1;
|
||||
gen = vips_convasep_generate_vertical;
|
||||
}
|
||||
|
||||
if( (*out)->Xsize <= 0 ||
|
||||
(*out)->Ysize <= 0 ) {
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "image too small for mask" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( vips_image_generate( *out,
|
||||
vips_convasep_start, gen, vips_convasep_stop, in, convasep ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_convasep_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsConvolution *convolution = (VipsConvolution *) object;
|
||||
VipsConvasep *convasep = (VipsConvasep *) object;
|
||||
VipsImage **t = (VipsImage **) vips_object_local_array( object, 4 );
|
||||
|
||||
VipsImage *in;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_convasep_parent_class )->build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips_check_separable( class->nickname, convolution->M ) )
|
||||
return( -1 );
|
||||
|
||||
/* An int version of our mask.
|
||||
*/
|
||||
if( vips__image_intize( convolution->M, &t[3] ) )
|
||||
return( -1 );
|
||||
convasep->iM = t[3];
|
||||
convasep->width = convasep->iM->Xsize * convasep->iM->Ysize;
|
||||
in = convolution->in;
|
||||
|
||||
if( vips_convasep_decompose( convasep ) )
|
||||
return( -1 );
|
||||
|
||||
g_object_set( convasep, "out", vips_image_new(), NULL );
|
||||
if(
|
||||
vips_embed( in, &t[0],
|
||||
convasep->width / 2,
|
||||
convasep->width / 2,
|
||||
in->Xsize + convasep->width - 1,
|
||||
in->Ysize + convasep->width - 1,
|
||||
"extend", VIPS_EXTEND_COPY,
|
||||
NULL ) ||
|
||||
vips_convasep_pass( convasep,
|
||||
t[0], &t[1], VIPS_DIRECTION_HORIZONTAL ) ||
|
||||
vips_convasep_pass( convasep,
|
||||
t[1], &t[2], VIPS_DIRECTION_VERTICAL ) ||
|
||||
vips_image_write( t[2], convolution->out ) )
|
||||
return( -1 );
|
||||
|
||||
convolution->out->Xoffset = 0;
|
||||
convolution->out->Yoffset = 0;
|
||||
|
||||
vips_reorder_margin_hint( convolution->out,
|
||||
convolution->M->Xsize * convolution->M->Ysize );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_convasep_class_init( VipsConvasepClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "convasep";
|
||||
object_class->description =
|
||||
_( "approximate separable integer convolution" );
|
||||
object_class->build = vips_convasep_build;
|
||||
|
||||
VIPS_ARG_INT( class, "layers", 104,
|
||||
_( "Layers" ),
|
||||
_( "Use this many layers in approximation" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsConvasep, layers ),
|
||||
1, 1000, 5 );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_convasep_init( VipsConvasep *convasep )
|
||||
{
|
||||
convasep->layers = 5;
|
||||
convasep->n_lines = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_convasep:
|
||||
* @in: input image
|
||||
* @out: output image
|
||||
* @mask: convolve with this mask
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @layers: %gint, number of layers for approximation
|
||||
*
|
||||
* Approximate separable integer convolution. This is a low-level operation, see
|
||||
* vips_convsep() for something more convenient.
|
||||
*
|
||||
* The image is convolved twice: once with @mask and then again with @mask
|
||||
* rotated by 90 degrees.
|
||||
* @mask must be 1xn or nx1 elements.
|
||||
* Elements of @mask are converted to
|
||||
* integers before convolution.
|
||||
*
|
||||
* Larger values for @layers give more accurate
|
||||
* results, but are slower. As @layers approaches the mask radius, the
|
||||
* accuracy will become close to exact convolution and the speed will drop to
|
||||
* match. For many large masks, such as Gaussian, @layers need be only 10% of
|
||||
* this value and accuracy will still be good.
|
||||
*
|
||||
* The output image
|
||||
* always has the same #VipsBandFormat as the input image.
|
||||
*
|
||||
* See also: vips_convsep().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
vips_convasep( VipsImage *in, VipsImage **out, VipsImage *mask, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, mask );
|
||||
result = vips_call_split( "convasep", ap, in, out, mask );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
|
@ -0,0 +1,412 @@
|
|||
/* convf
|
||||
*
|
||||
* Copyright: 1990, N. Dessipris.
|
||||
*
|
||||
* Author: Nicos Dessipris & Kirk Martinez
|
||||
* Written on: 29/04/1991
|
||||
* Modified on: 19/05/1991
|
||||
* 8/7/93 JC
|
||||
* - adapted for partial v2
|
||||
* - memory leaks fixed
|
||||
* - ANSIfied
|
||||
* 12/7/93 JC
|
||||
* - adapted im_convbi() to im_convbf()
|
||||
* 7/10/94 JC
|
||||
* - new IM_ARRAY() macro
|
||||
* - evalend callbacks
|
||||
* - more typedef
|
||||
* 9/3/01 JC
|
||||
* - redone from im_conv()
|
||||
* 27/7/01 JC
|
||||
* - rejects masks with scale == 0
|
||||
* 7/4/04
|
||||
* - now uses im_embed() with edge stretching on the input, not
|
||||
* the output
|
||||
* - sets Xoffset / Yoffset
|
||||
* 11/11/05
|
||||
* - simpler inner loop avoids gcc4 bug
|
||||
* 12/11/09
|
||||
* - only rebuild the buffer offsets if bpl changes
|
||||
* - tiny speedups and cleanups
|
||||
* - add restrict, though it doesn't seem to help gcc
|
||||
* - add mask-all-zero check
|
||||
* 13/11/09
|
||||
* - rename as im_conv_f() to make it easier for vips.c to make the
|
||||
* overloaded version
|
||||
* 3/2/10
|
||||
* - gtkdoc
|
||||
* - more cleanups
|
||||
* 1/10/10
|
||||
* - support complex (just double the bands)
|
||||
* 29/10/10
|
||||
* - get rid of im_convsep_f(), just call this twice, no longer worth
|
||||
* keeping two versions
|
||||
* 15/10/11 Nicolas
|
||||
* - handle offset correctly in seperable convolutions
|
||||
* 26/1/16 Lovell Fuller
|
||||
* - remove Duff for a 25% speedup
|
||||
* 23/6/16
|
||||
* - redone as a class
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#include "pconvolution.h"
|
||||
|
||||
typedef struct {
|
||||
VipsConvolution parent_instance;
|
||||
|
||||
/* We make a smaller version of the mask with the zeros squeezed out.
|
||||
*/
|
||||
int nnz; /* Number of non-zero mask elements */
|
||||
double *coeff; /* Array of non-zero mask coefficients */
|
||||
int *coeff_pos; /* Index of each nnz element in mask->coeff */
|
||||
} VipsConvf;
|
||||
|
||||
typedef VipsConvolutionClass VipsConvfClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsConvf, vips_convf, VIPS_TYPE_CONVOLUTION );
|
||||
|
||||
/* Our sequence value.
|
||||
*/
|
||||
typedef struct {
|
||||
VipsConvf *convf;
|
||||
VipsRegion *ir; /* Input region */
|
||||
|
||||
int *offsets; /* Offsets for each non-zero matrix element */
|
||||
VipsPel **pts; /* Per-non-zero mask element image pointers */
|
||||
|
||||
int last_bpl; /* Avoid recalcing offsets, if we can */
|
||||
} VipsConvfSequence;
|
||||
|
||||
/* Free a sequence value.
|
||||
*/
|
||||
static int
|
||||
vips_convf_stop( void *vseq, void *a, void *b )
|
||||
{
|
||||
VipsConvfSequence *seq = (VipsConvfSequence *) vseq;
|
||||
|
||||
VIPS_UNREF( seq->ir );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Convolution start function.
|
||||
*/
|
||||
static void *
|
||||
vips_convf_start( VipsImage *out, void *a, void *b )
|
||||
{
|
||||
VipsImage *in = (VipsImage *) a;
|
||||
VipsConvf *convf = (VipsConvf *) b;
|
||||
VipsConvfSequence *seq;
|
||||
|
||||
if( !(seq = VIPS_NEW( out, VipsConvfSequence )) )
|
||||
return( NULL );
|
||||
|
||||
seq->convf = convf;
|
||||
seq->ir = NULL;
|
||||
seq->pts = NULL;
|
||||
seq->last_bpl = -1;
|
||||
|
||||
seq->ir = vips_region_new( in );
|
||||
if( !(seq->offsets = VIPS_ARRAY( out, convf->nnz, int )) ||
|
||||
!(seq->pts = VIPS_ARRAY( out, convf->nnz, VipsPel * )) ) {
|
||||
vips_convf_stop( seq, in, convf );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( (void *) seq );
|
||||
}
|
||||
|
||||
#define CONV_FLOAT( ITYPE, OTYPE ) { \
|
||||
ITYPE ** restrict p = (ITYPE **) seq->pts; \
|
||||
OTYPE * restrict q = (OTYPE *) VIPS_REGION_ADDR( or, le, y ); \
|
||||
\
|
||||
for( x = 0; x < sz; x++ ) { \
|
||||
double sum; \
|
||||
int i; \
|
||||
\
|
||||
sum = 0; \
|
||||
for ( i = 0; i < nnz; i++ ) \
|
||||
sum += t[i] * p[i][x]; \
|
||||
\
|
||||
sum = (sum / scale) + offset; \
|
||||
\
|
||||
q[x] = sum; \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Convolve!
|
||||
*/
|
||||
static int
|
||||
vips_convf_gen( REGION *or, void *vseq, void *a, void *b, gboolean *stop )
|
||||
{
|
||||
VipsConvfSequence *seq = (VipsConvfSequence *) vseq;
|
||||
VipsConvf *convf = (VipsConvf *) b;
|
||||
VipsConvolution *convolution = (VipsConvolution *) convf;
|
||||
VipsImage *M = convolution->M;
|
||||
double scale = vips_image_get_scale( M );
|
||||
double offset = vips_image_get_offset( M );
|
||||
VipsImage *in = (VipsImage *) a;
|
||||
VipsRegion *ir = seq->ir;
|
||||
double * restrict t = convf->coeff;
|
||||
const int nnz = convf->nnz;
|
||||
VipsRect *r = &or->valid;
|
||||
int le = r->left;
|
||||
int to = r->top;
|
||||
int bo = VIPS_RECT_BOTTOM( r );
|
||||
int sz = VIPS_REGION_N_ELEMENTS( or ) *
|
||||
(vips_band_format_iscomplex( in->BandFmt ) ? 2 : 1);
|
||||
|
||||
VipsRect s;
|
||||
int x, y, z, i;
|
||||
|
||||
/* Prepare the section of the input image we need. A little larger
|
||||
* than the section of the output image we are producing.
|
||||
*/
|
||||
s = *r;
|
||||
s.width += M->Xsize - 1;
|
||||
s.height += M->Ysize - 1;
|
||||
if( vips_region_prepare( ir, &s ) )
|
||||
return( -1 );
|
||||
|
||||
/* Fill offset array. Only do this if the bpl has changed since the
|
||||
* previous vips_region_prepare().
|
||||
*/
|
||||
if( seq->last_bpl != VIPS_REGION_LSKIP( ir ) ) {
|
||||
seq->last_bpl = VIPS_REGION_LSKIP( ir );
|
||||
|
||||
for( i = 0; i < nnz; i++ ) {
|
||||
z = convf->coeff_pos[i];
|
||||
x = z % M->Xsize;
|
||||
y = z / M->Xsize;
|
||||
|
||||
seq->offsets[i] =
|
||||
VIPS_REGION_ADDR( ir, x + le, y + to ) -
|
||||
VIPS_REGION_ADDR( ir, le, to );
|
||||
}
|
||||
}
|
||||
|
||||
VIPS_GATE_START( "vips_convf_gen: work" );
|
||||
|
||||
for( y = to; y < bo; y++ ) {
|
||||
/* Init pts for this line of PELs.
|
||||
*/
|
||||
for( z = 0; z < nnz; z++ )
|
||||
seq->pts[z] = seq->offsets[z] +
|
||||
VIPS_REGION_ADDR( ir, le, y );
|
||||
|
||||
switch( in->BandFmt ) {
|
||||
case VIPS_FORMAT_UCHAR:
|
||||
CONV_FLOAT( unsigned char, float );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_CHAR:
|
||||
CONV_FLOAT( signed char, float );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_USHORT:
|
||||
CONV_FLOAT( unsigned short, float );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_SHORT:
|
||||
CONV_FLOAT( signed short, float );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_UINT:
|
||||
CONV_FLOAT( unsigned int, float );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_INT:
|
||||
CONV_FLOAT( signed int, float );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_FLOAT:
|
||||
case VIPS_FORMAT_COMPLEX:
|
||||
CONV_FLOAT( float, float );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_DOUBLE:
|
||||
case VIPS_FORMAT_DPCOMPLEX:
|
||||
CONV_FLOAT( double, double );
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
VIPS_GATE_STOP( "vips_convf_gen: work" );
|
||||
|
||||
VIPS_COUNT_PIXELS( or, "vips_convf_gen" );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_convf_build( VipsObject *object )
|
||||
{
|
||||
VipsConvolution *convolution = (VipsConvolution *) object;
|
||||
VipsConvf *convf = (VipsConvf *) object;
|
||||
VipsImage **t = (VipsImage **) vips_object_local_array( object, 4 );
|
||||
|
||||
VipsImage *in;
|
||||
VipsImage *M;
|
||||
double *coeff;
|
||||
int ne;
|
||||
int i;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_convf_parent_class )->build( object ) )
|
||||
return( -1 );
|
||||
|
||||
M = convolution->M;
|
||||
coeff = (double *) VIPS_IMAGE_ADDR( M, 0, 0 );
|
||||
ne = M->Xsize * M->Ysize;
|
||||
if( !(convf->coeff = VIPS_ARRAY( object, ne, double )) ||
|
||||
!(convf->coeff_pos = VIPS_ARRAY( object, ne, int )) )
|
||||
return( -1 );
|
||||
|
||||
/* Find non-zero mask elements.
|
||||
*/
|
||||
for( i = 0; i < ne; i++ )
|
||||
if( coeff[i] ) {
|
||||
convf->coeff[convf->nnz] = coeff[i];
|
||||
convf->coeff_pos[convf->nnz] = i;
|
||||
convf->nnz += 1;
|
||||
}
|
||||
|
||||
/* Was the whole mask zero? We must have at least 1 element in there:
|
||||
* set it to zero.
|
||||
*/
|
||||
if( convf->nnz == 0 ) {
|
||||
convf->coeff[0] = 0;
|
||||
convf->coeff_pos[0] = 0;
|
||||
convf->nnz = 1;
|
||||
}
|
||||
|
||||
in = convolution->in;
|
||||
|
||||
if( vips_embed( in, &t[0],
|
||||
M->Xsize / 2, M->Ysize / 2,
|
||||
in->Xsize + M->Xsize - 1, in->Ysize + M->Ysize - 1,
|
||||
"extend", VIPS_EXTEND_COPY,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
in = t[0];
|
||||
|
||||
g_object_set( convf, "out", vips_image_new(), NULL );
|
||||
if( vips_image_pipelinev( convolution->out,
|
||||
VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
convolution->out->Xoffset = 0;
|
||||
convolution->out->Yoffset = 0;
|
||||
|
||||
/* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output
|
||||
* would be 1x1.
|
||||
*/
|
||||
if( vips_bandfmt_isint( in->BandFmt ) )
|
||||
convolution->out->BandFmt = IM_BANDFMT_FLOAT;
|
||||
convolution->out->Xsize -= M->Xsize - 1;
|
||||
convolution->out->Ysize -= M->Ysize - 1;
|
||||
|
||||
if( vips_image_generate( convolution->out,
|
||||
vips_convf_start, vips_convf_gen, vips_convf_stop, in, convf ) )
|
||||
return( -1 );
|
||||
|
||||
convolution->out->Xoffset = -M->Xsize / 2;
|
||||
convolution->out->Yoffset = -M->Ysize / 2;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_convf_class_init( VipsConvfClass *class )
|
||||
{
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
object_class->nickname = "convf";
|
||||
object_class->description = _( "float convolution operation" );
|
||||
object_class->build = vips_convf_build;
|
||||
}
|
||||
|
||||
static void
|
||||
vips_convf_init( VipsConvf *convf )
|
||||
{
|
||||
convf->nnz = 0;
|
||||
convf->coeff = NULL;
|
||||
convf->coeff_pos = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_convf:
|
||||
* @in: input image
|
||||
* @out: output image
|
||||
* @mask: convolve with this mask
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Convolution. This is a low-level operation, see vips_conv() for something
|
||||
* more convenient.
|
||||
*
|
||||
* Perform a convolution of @in with @mask.
|
||||
* Each output pixel is
|
||||
* calculated as sigma[i]{pixel[i] * mask[i]} / scale + offset, where scale
|
||||
* and offset are part of @mask.
|
||||
*
|
||||
* The convolution is performed with floating-point arithmetic. The output image
|
||||
* is always #VIPS_FORMAT_FLOAT unless @in is #VIPS_FORMAT_DOUBLE, in which case
|
||||
* @out is also #VIPS_FORMAT_DOUBLE.
|
||||
*
|
||||
* See also: vips_conv().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
vips_convf( VipsImage *in, VipsImage **out, VipsImage *mask, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, mask );
|
||||
result = vips_call_split( "convf", ap, in, out, mask );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -157,16 +157,24 @@ void
|
|||
vips_convolution_operation_init( void )
|
||||
{
|
||||
extern int vips_conv_get_type( void );
|
||||
extern int vips_compass_get_type( void );
|
||||
extern int vips_conva_get_type( void );
|
||||
extern int vips_convf_get_type( void );
|
||||
extern int vips_convi_get_type( void );
|
||||
extern int vips_convsep_get_type( void );
|
||||
extern int vips_convasep_get_type( void );
|
||||
extern int vips_compass_get_type( void );
|
||||
extern int vips_fastcor_get_type( void );
|
||||
extern int vips_spcor_get_type( void );
|
||||
extern int vips_sharpen_get_type( void );
|
||||
extern int vips_gaussblur_get_type( void );
|
||||
|
||||
vips_conv_get_type();
|
||||
vips_conva_get_type();
|
||||
vips_convf_get_type();
|
||||
vips_convi_get_type();
|
||||
vips_compass_get_type();
|
||||
vips_convsep_get_type();
|
||||
vips_convasep_get_type();
|
||||
vips_fastcor_get_type();
|
||||
vips_spcor_get_type();
|
||||
vips_sharpen_get_type();
|
||||
|
|
|
@ -63,6 +63,8 @@ vips_convsep_build( VipsObject *object )
|
|||
VipsImage **t = (VipsImage **)
|
||||
vips_object_local_array( object, 3 );
|
||||
|
||||
VipsImage *in;
|
||||
|
||||
g_object_set( convsep, "out", vips_image_new(), NULL );
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_convsep_parent_class )->build( object ) )
|
||||
|
@ -71,20 +73,38 @@ vips_convsep_build( VipsObject *object )
|
|||
if( vips_check_separable( class->nickname, convolution->M ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips_rot( convolution->M, &t[0], VIPS_ANGLE_D90, NULL ) ||
|
||||
vips_conv( convolution->in, &t[1], convolution->M,
|
||||
"precision", convsep->precision,
|
||||
"layers", convsep->layers,
|
||||
"cluster", convsep->cluster,
|
||||
NULL ) ||
|
||||
vips_conv( t[1], &t[2], t[0],
|
||||
"precision", convsep->precision,
|
||||
"layers", convsep->layers,
|
||||
"cluster", convsep->cluster,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
in = convolution->in;
|
||||
|
||||
if( vips_image_write( t[2], convolution->out ) )
|
||||
if( convsep->precision == VIPS_PRECISION_APPROXIMATE ) {
|
||||
if( vips_convasep( convolution->in, &t[0], convolution->M,
|
||||
"layers", convsep->layers,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
in = t[0];
|
||||
}
|
||||
else {
|
||||
if( vips_rot( convolution->M, &t[0], VIPS_ANGLE_D90, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
/* We must only add the offset once.
|
||||
*/
|
||||
vips_image_set_double( t[0], "offset", 0 );
|
||||
|
||||
if( vips_conv( convolution->in, &t[1], convolution->M,
|
||||
"precision", convsep->precision,
|
||||
"layers", convsep->layers,
|
||||
"cluster", convsep->cluster,
|
||||
NULL ) ||
|
||||
vips_conv( t[1], &t[2], t[0],
|
||||
"precision", convsep->precision,
|
||||
"layers", convsep->layers,
|
||||
"cluster", convsep->cluster,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
in = t[2];
|
||||
}
|
||||
|
||||
if( vips_image_write( in, convolution->out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
|
|
|
@ -125,6 +125,9 @@ vips_correlation_build( VipsObject *object )
|
|||
correlation->in_ready, correlation ) )
|
||||
return( -1 );
|
||||
|
||||
vips_reorder_margin_hint( correlation->out,
|
||||
correlation->ref->Xsize * correlation->ref->Ysize );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,6 @@ G_DEFINE_TYPE( VipsGaussblur, vips_gaussblur, VIPS_TYPE_OPERATION );
|
|||
static int
|
||||
vips_gaussblur_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsGaussblur *gaussblur = (VipsGaussblur *) object;
|
||||
VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 );
|
||||
|
||||
|
@ -85,7 +84,7 @@ vips_gaussblur_build( VipsObject *object )
|
|||
vips_matrixprint( t[0], NULL );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
vips_info( class->nickname, "gaussblur mask width %d", t[0]->Xsize );
|
||||
g_info( "gaussblur mask width %d", t[0]->Xsize );
|
||||
|
||||
if( vips_convsep( gaussblur->in, &t[1], t[0],
|
||||
"precision", gaussblur->precision,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,879 +0,0 @@
|
|||
/* im_aconvsep ... separable approximate convolution
|
||||
*
|
||||
* This operation does an approximate, seperable convolution.
|
||||
*
|
||||
* Author: John Cupitt & Nicolas Robidoux
|
||||
* Written on: 31/5/11
|
||||
* Modified on:
|
||||
* 31/5/11
|
||||
* - from im_conv()
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
See:
|
||||
|
||||
http://incubator.quasimondo.com/processing/stackblur.pde
|
||||
|
||||
This thing is a little like stackblur, but generalised to any separable
|
||||
mask.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
TODO
|
||||
|
||||
- are we handling mask offset correctly?
|
||||
|
||||
*/
|
||||
|
||||
/* Show sample pixels as they are transformed.
|
||||
#define DEBUG_PIXELS
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
#define VIPS_DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/vector.h>
|
||||
#include <vips/debug.h>
|
||||
|
||||
/* Maximum number of lines we can break the mask into.
|
||||
*/
|
||||
#define MAX_LINES (1000)
|
||||
|
||||
/* Euclid's algorithm. Use this to common up mults.
|
||||
*/
|
||||
static int
|
||||
gcd( int a, int b )
|
||||
{
|
||||
if( b == 0 )
|
||||
return( abs( a ) );
|
||||
else
|
||||
return( gcd( b, a % b ) );
|
||||
}
|
||||
|
||||
/* A set of lines.
|
||||
*/
|
||||
typedef struct _Lines {
|
||||
/* Copy of our arguments.
|
||||
*/
|
||||
IMAGE *in;
|
||||
IMAGE *out;
|
||||
DOUBLEMASK *mask;
|
||||
int n_layers;
|
||||
|
||||
int area;
|
||||
int rounding;
|
||||
|
||||
/* Start is the left-most pixel in the line, end is one beyond the
|
||||
* right-most pixel.
|
||||
*/
|
||||
int n_lines;
|
||||
int start[MAX_LINES];
|
||||
int end[MAX_LINES];
|
||||
int factor[MAX_LINES];
|
||||
} Lines;
|
||||
|
||||
static void
|
||||
lines_start( Lines *lines, int x, int factor )
|
||||
{
|
||||
lines->start[lines->n_lines] = x;
|
||||
lines->factor[lines->n_lines] = factor;
|
||||
}
|
||||
|
||||
static int
|
||||
lines_end( Lines *lines, int x )
|
||||
{
|
||||
lines->end[lines->n_lines] = x;
|
||||
|
||||
if( lines->n_lines >= MAX_LINES - 1 ) {
|
||||
vips_error( "im_aconvsep", "%s", _( "mask too complex" ) );
|
||||
return( -1 );
|
||||
}
|
||||
lines->n_lines += 1;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Break a mask into lines.
|
||||
*/
|
||||
static Lines *
|
||||
lines_new( IMAGE *in, IMAGE *out, DOUBLEMASK *mask, int n_layers )
|
||||
{
|
||||
const int width = mask->xsize * mask->ysize;
|
||||
|
||||
Lines *lines;
|
||||
double max;
|
||||
double min;
|
||||
double depth;
|
||||
double sum;
|
||||
int layers_above;
|
||||
int layers_below;
|
||||
int z, n, x;
|
||||
|
||||
/* Check parameters.
|
||||
*/
|
||||
if( im_piocheck( in, out ) ||
|
||||
im_check_uncoded( "im_aconvsep", in ) ||
|
||||
vips_check_dmask_1d( "im_aconvsep", mask ) )
|
||||
return( NULL );
|
||||
|
||||
lines = VIPS_NEW( out, Lines );
|
||||
lines->in = in;
|
||||
lines->out = out;
|
||||
if( !(lines->mask = (DOUBLEMASK *) im_local( out,
|
||||
(im_construct_fn) im_dup_dmask,
|
||||
(im_callback_fn) im_free_dmask, mask, mask->filename, NULL )) )
|
||||
return( NULL );
|
||||
lines->n_layers = n_layers;
|
||||
lines->n_lines = 0;
|
||||
|
||||
VIPS_DEBUG_MSG( "lines_new: breaking into %d layers ...\n", n_layers );
|
||||
|
||||
/* Find mask range. We must always include the zero axis in the mask.
|
||||
*/
|
||||
max = 0;
|
||||
min = 0;
|
||||
for( x = 0; x < width; x++ ) {
|
||||
if( mask->coeff[x] > max )
|
||||
max = mask->coeff[x];
|
||||
if( mask->coeff[x] < min )
|
||||
min = mask->coeff[x];
|
||||
}
|
||||
|
||||
/* The zero axis must fall on a layer boundary. Estimate the
|
||||
* depth, find n-lines-above-zero, get exact depth, then calculate a
|
||||
* fixed n-lines which includes any negative parts.
|
||||
*/
|
||||
depth = (max - min) / n_layers;
|
||||
layers_above = ceil( max / depth );
|
||||
depth = max / layers_above;
|
||||
layers_below = floor( min / depth );
|
||||
n_layers = layers_above - layers_below;
|
||||
|
||||
VIPS_DEBUG_MSG( "depth = %g, n_layers = %d\n", depth, n_layers );
|
||||
|
||||
/* For each layer, generate a set of lines which are inside the
|
||||
* perimeter. Work down from the top.
|
||||
*/
|
||||
for( z = 0; z < n_layers; z++ ) {
|
||||
double y = max - (1 + z) * depth;
|
||||
|
||||
/* y plus half depth ... ie. the layer midpoint.
|
||||
*/
|
||||
double y_ph = y + depth / 2;
|
||||
|
||||
/* Odd, but we must avoid rounding errors that make us miss 0
|
||||
* in the line above.
|
||||
*/
|
||||
int y_positive = z < layers_above;
|
||||
|
||||
int inside;
|
||||
|
||||
/* Start outside the perimeter.
|
||||
*/
|
||||
inside = 0;
|
||||
|
||||
for( x = 0; x < width; x++ ) {
|
||||
/* The vertical line from mask[z] to 0 is inside. Is
|
||||
* our current square (x, y) part of that line?
|
||||
*/
|
||||
if( (y_positive && mask->coeff[x] >= y_ph) ||
|
||||
(!y_positive && mask->coeff[x] <= y_ph) ) {
|
||||
if( !inside ) {
|
||||
lines_start( lines, x,
|
||||
y_positive ? 1 : -1 );
|
||||
inside = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( inside ) {
|
||||
if( lines_end( lines, x ) )
|
||||
return( NULL );
|
||||
inside = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( inside &&
|
||||
lines_end( lines, width ) )
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/* Can we common up any lines? Search for lines with identical
|
||||
* start/end.
|
||||
*/
|
||||
for( z = 0; z < lines->n_lines; z++ ) {
|
||||
for( n = z + 1; n < lines->n_lines; n++ ) {
|
||||
if( lines->start[z] == lines->start[n] &&
|
||||
lines->end[z] == lines->end[n] ) {
|
||||
lines->factor[z] += lines->factor[n];
|
||||
|
||||
/* n can be deleted. Do this in a separate
|
||||
* pass below.
|
||||
*/
|
||||
lines->factor[n] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we can remove all factor 0 lines.
|
||||
*/
|
||||
for( z = 0; z < lines->n_lines; z++ ) {
|
||||
if( lines->factor[z] == 0 ) {
|
||||
for( x = z; x < lines->n_lines; x++ ) {
|
||||
lines->start[x] = lines->start[x + 1];
|
||||
lines->end[x] = lines->end[x + 1];
|
||||
lines->factor[x] = lines->factor[x + 1];
|
||||
}
|
||||
lines->n_lines -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the area of the lines.
|
||||
*/
|
||||
lines->area = 0;
|
||||
for( z = 0; z < lines->n_lines; z++ )
|
||||
lines->area += lines->factor[z] *
|
||||
(lines->end[z] - lines->start[z]);
|
||||
|
||||
/* Strength reduction: if all lines are divisible by n, we can move
|
||||
* that n out into the ->area factor. The aim is to produce as many
|
||||
* factor 1 lines as we can and to reduce the chance of overflow.
|
||||
*/
|
||||
x = lines->factor[0];
|
||||
for( z = 1; z < lines->n_lines; z++ )
|
||||
x = gcd( x, lines->factor[z] );
|
||||
for( z = 0; z < lines->n_lines; z++ )
|
||||
lines->factor[z] /= x;
|
||||
lines->area *= x;
|
||||
|
||||
/* Find the area of the original mask.
|
||||
*/
|
||||
sum = 0;
|
||||
for( z = 0; z < width; z++ )
|
||||
sum += mask->coeff[z];
|
||||
|
||||
lines->area = rint( sum * lines->area / mask->scale );
|
||||
lines->rounding = (lines->area + 1) / 2 + mask->offset * lines->area;
|
||||
|
||||
/* ASCII-art layer drawing.
|
||||
printf( "lines:\n" );
|
||||
for( z = 0; z < lines->n_lines; z++ ) {
|
||||
printf( "%3d - %2d x ", z, lines->factor[z] );
|
||||
for( x = 0; x < 55; x++ ) {
|
||||
int rx = x * (width + 1) / 55;
|
||||
|
||||
if( rx >= lines->start[z] && rx < lines->end[z] )
|
||||
printf( "#" );
|
||||
else
|
||||
printf( " " );
|
||||
}
|
||||
printf( " %3d .. %3d\n", lines->start[z], lines->end[z] );
|
||||
}
|
||||
printf( "area = %d\n", lines->area );
|
||||
printf( "rounding = %d\n", lines->rounding );
|
||||
*/
|
||||
|
||||
return( lines );
|
||||
}
|
||||
|
||||
/* Our sequence value.
|
||||
*/
|
||||
typedef struct {
|
||||
Lines *lines;
|
||||
REGION *ir; /* Input region */
|
||||
|
||||
int *start; /* Offsets for start and stop */
|
||||
int *end;
|
||||
|
||||
/* The sums for each line. int for integer types, double for floating
|
||||
* point types.
|
||||
*/
|
||||
void *sum;
|
||||
|
||||
int last_stride; /* Avoid recalcing offsets, if we can */
|
||||
} AConvSep;
|
||||
|
||||
/* Free a sequence value.
|
||||
*/
|
||||
static int
|
||||
aconvsep_stop( void *vseq, void *a, void *b )
|
||||
{
|
||||
AConvSep *seq = (AConvSep *) vseq;
|
||||
|
||||
IM_FREEF( im_region_free, seq->ir );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Convolution start function.
|
||||
*/
|
||||
static void *
|
||||
aconvsep_start( IMAGE *out, void *a, void *b )
|
||||
{
|
||||
IMAGE *in = (IMAGE *) a;
|
||||
Lines *lines = (Lines *) b;
|
||||
|
||||
AConvSep *seq;
|
||||
|
||||
if( !(seq = IM_NEW( out, AConvSep )) )
|
||||
return( NULL );
|
||||
|
||||
/* Init!
|
||||
*/
|
||||
seq->lines = lines;
|
||||
seq->ir = im_region_create( in );
|
||||
seq->start = IM_ARRAY( out, lines->n_lines, int );
|
||||
seq->end = IM_ARRAY( out, lines->n_lines, int );
|
||||
if( vips_band_format_isint( out->BandFmt ) )
|
||||
seq->sum = IM_ARRAY( out, lines->n_lines, int );
|
||||
else
|
||||
seq->sum = IM_ARRAY( out, lines->n_lines, double );
|
||||
seq->last_stride = -1;
|
||||
|
||||
if( !seq->ir || !seq->start || !seq->end || !seq->sum ) {
|
||||
aconvsep_stop( seq, in, lines );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( seq );
|
||||
}
|
||||
|
||||
#define CLIP_UCHAR( V ) \
|
||||
G_STMT_START { \
|
||||
if( (V) < 0 ) \
|
||||
(V) = 0; \
|
||||
else if( (V) > UCHAR_MAX ) \
|
||||
(V) = UCHAR_MAX; \
|
||||
} G_STMT_END
|
||||
|
||||
#define CLIP_CHAR( V ) \
|
||||
G_STMT_START { \
|
||||
if( (V) < SCHAR_MIN ) \
|
||||
(V) = SCHAR_MIN; \
|
||||
else if( (V) > SCHAR_MAX ) \
|
||||
(V) = SCHAR_MAX; \
|
||||
} G_STMT_END
|
||||
|
||||
#define CLIP_USHORT( V ) \
|
||||
G_STMT_START { \
|
||||
if( (V) < 0 ) \
|
||||
(V) = 0; \
|
||||
else if( (V) > USHRT_MAX ) \
|
||||
(V) = USHRT_MAX; \
|
||||
} G_STMT_END
|
||||
|
||||
#define CLIP_SHORT( V ) \
|
||||
G_STMT_START { \
|
||||
if( (V) < SHRT_MIN ) \
|
||||
(V) = SHRT_MIN; \
|
||||
else if( (V) > SHRT_MAX ) \
|
||||
(V) = SHRT_MAX; \
|
||||
} G_STMT_END
|
||||
|
||||
#define CLIP_NONE( V ) {}
|
||||
|
||||
/* The h and v loops are very similar, but also annoyingly different. Keep
|
||||
* them separate for easy debugging.
|
||||
*/
|
||||
|
||||
#define HCONV_INT( TYPE, CLIP ) { \
|
||||
for( i = 0; i < bands; i++ ) { \
|
||||
int *seq_sum = (int *) seq->sum; \
|
||||
\
|
||||
TYPE *q; \
|
||||
TYPE *p; \
|
||||
int sum; \
|
||||
\
|
||||
p = i + (TYPE *) IM_REGION_ADDR( ir, r->left, r->top + y ); \
|
||||
q = i + (TYPE *) IM_REGION_ADDR( or, r->left, r->top + y ); \
|
||||
\
|
||||
sum = 0; \
|
||||
for( z = 0; z < n_lines; z++ ) { \
|
||||
seq_sum[z] = 0; \
|
||||
for( x = lines->start[z]; x < lines->end[z]; x++ ) \
|
||||
seq_sum[z] += p[x * istride]; \
|
||||
sum += lines->factor[z] * seq_sum[z]; \
|
||||
} \
|
||||
sum = (sum + lines->rounding) / lines->area; \
|
||||
CLIP( sum ); \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
\
|
||||
for( x = 1; x < r->width; x++ ) { \
|
||||
sum = 0; \
|
||||
for( z = 0; z < n_lines; z++ ) { \
|
||||
seq_sum[z] += p[seq->end[z]]; \
|
||||
seq_sum[z] -= p[seq->start[z]]; \
|
||||
sum += lines->factor[z] * seq_sum[z]; \
|
||||
} \
|
||||
p += istride; \
|
||||
sum = (sum + lines->rounding) / lines->area; \
|
||||
CLIP( sum ); \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define HCONV_FLOAT( TYPE ) { \
|
||||
for( i = 0; i < bands; i++ ) { \
|
||||
double *seq_sum = (double *) seq->sum; \
|
||||
\
|
||||
TYPE *q; \
|
||||
TYPE *p; \
|
||||
double sum; \
|
||||
\
|
||||
p = i + (TYPE *) IM_REGION_ADDR( ir, r->left, r->top + y ); \
|
||||
q = i + (TYPE *) IM_REGION_ADDR( or, r->left, r->top + y ); \
|
||||
\
|
||||
sum = 0; \
|
||||
for( z = 0; z < lines->n_lines; z++ ) { \
|
||||
seq_sum[z] = 0; \
|
||||
for( x = lines->start[z]; x < lines->end[z]; x++ ) \
|
||||
seq_sum[z] += p[x * istride]; \
|
||||
sum += lines->factor[z] * seq_sum[z]; \
|
||||
} \
|
||||
sum = sum / lines->area + mask->offset; \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
\
|
||||
for( x = 1; x < r->width; x++ ) { \
|
||||
sum = 0; \
|
||||
for( z = 0; z < lines->n_lines; z++ ) { \
|
||||
seq_sum[z] += p[seq->end[z]]; \
|
||||
seq_sum[z] -= p[seq->start[z]]; \
|
||||
sum += lines->factor[z] * seq_sum[z]; \
|
||||
} \
|
||||
p += istride; \
|
||||
sum = sum / lines->area + mask->offset; \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Do horizontal masks ... we scan the mask along scanlines.
|
||||
*/
|
||||
static int
|
||||
aconvsep_generate_horizontal( REGION *or, void *vseq, void *a, void *b )
|
||||
{
|
||||
AConvSep *seq = (AConvSep *) vseq;
|
||||
IMAGE *in = (IMAGE *) a;
|
||||
Lines *lines = (Lines *) b;
|
||||
|
||||
REGION *ir = seq->ir;
|
||||
const int n_lines = lines->n_lines;
|
||||
DOUBLEMASK *mask = lines->mask;
|
||||
Rect *r = &or->valid;
|
||||
|
||||
/* Double the bands (notionally) for complex.
|
||||
*/
|
||||
int bands = vips_band_format_iscomplex( in->BandFmt ) ?
|
||||
2 * in->Bands : in->Bands;
|
||||
|
||||
Rect s;
|
||||
int x, y, z, i;
|
||||
int istride;
|
||||
int ostride;
|
||||
|
||||
/* Prepare the section of the input image we need. A little larger
|
||||
* than the section of the output image we are producing.
|
||||
*/
|
||||
s = *r;
|
||||
s.width += mask->xsize - 1;
|
||||
s.height += mask->ysize - 1;
|
||||
if( im_prepare( ir, &s ) )
|
||||
return( -1 );
|
||||
|
||||
/* Stride can be different for the vertical case, keep this here for
|
||||
* ease of direction change.
|
||||
*/
|
||||
istride = IM_IMAGE_SIZEOF_PEL( in ) /
|
||||
IM_IMAGE_SIZEOF_ELEMENT( in );
|
||||
ostride = IM_IMAGE_SIZEOF_PEL( lines->out ) /
|
||||
IM_IMAGE_SIZEOF_ELEMENT( lines->out );
|
||||
|
||||
/* Init offset array.
|
||||
*/
|
||||
if( seq->last_stride != istride ) {
|
||||
seq->last_stride = istride;
|
||||
|
||||
for( z = 0; z < n_lines; z++ ) {
|
||||
seq->start[z] = lines->start[z] * istride;
|
||||
seq->end[z] = lines->end[z] * istride;
|
||||
}
|
||||
}
|
||||
|
||||
for( y = 0; y < r->height; y++ ) {
|
||||
switch( in->BandFmt ) {
|
||||
case IM_BANDFMT_UCHAR:
|
||||
HCONV_INT( unsigned char, CLIP_UCHAR );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_CHAR:
|
||||
HCONV_INT( signed char, CLIP_UCHAR );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_USHORT:
|
||||
HCONV_INT( unsigned short, CLIP_USHORT );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_SHORT:
|
||||
HCONV_INT( signed short, CLIP_SHORT );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_UINT:
|
||||
HCONV_INT( unsigned int, CLIP_NONE );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_INT:
|
||||
HCONV_INT( signed int, CLIP_NONE );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_FLOAT:
|
||||
HCONV_FLOAT( float );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_DOUBLE:
|
||||
HCONV_FLOAT( double );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_COMPLEX:
|
||||
HCONV_FLOAT( float );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_DPCOMPLEX:
|
||||
HCONV_FLOAT( double );
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#define VCONV_INT( TYPE, CLIP ) { \
|
||||
for( x = 0; x < sz; x++ ) { \
|
||||
int *seq_sum = (int *) seq->sum; \
|
||||
\
|
||||
TYPE *q; \
|
||||
TYPE *p; \
|
||||
int sum; \
|
||||
\
|
||||
p = x + (TYPE *) IM_REGION_ADDR( ir, r->left, r->top ); \
|
||||
q = x + (TYPE *) IM_REGION_ADDR( or, r->left, r->top ); \
|
||||
\
|
||||
sum = 0; \
|
||||
for( z = 0; z < lines->n_lines; z++ ) { \
|
||||
seq_sum[z] = 0; \
|
||||
for( y = lines->start[z]; y < lines->end[z]; y++ ) \
|
||||
seq_sum[z] += p[y * istride]; \
|
||||
sum += lines->factor[z] * seq_sum[z]; \
|
||||
} \
|
||||
sum = (sum + lines->rounding) / lines->area; \
|
||||
CLIP( sum ); \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
\
|
||||
for( y = 1; y < r->height; y++ ) { \
|
||||
sum = 0; \
|
||||
for( z = 0; z < lines->n_lines; z++ ) { \
|
||||
seq_sum[z] += p[seq->end[z]]; \
|
||||
seq_sum[z] -= p[seq->start[z]]; \
|
||||
sum += lines->factor[z] * seq_sum[z]; \
|
||||
} \
|
||||
p += istride; \
|
||||
sum = (sum + lines->rounding) / lines->area; \
|
||||
CLIP( sum ); \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define VCONV_FLOAT( TYPE ) { \
|
||||
for( x = 0; x < sz; x++ ) { \
|
||||
double *seq_sum = (double *) seq->sum; \
|
||||
\
|
||||
TYPE *q; \
|
||||
TYPE *p; \
|
||||
double sum; \
|
||||
\
|
||||
p = x + (TYPE *) IM_REGION_ADDR( ir, r->left, r->top ); \
|
||||
q = x + (TYPE *) IM_REGION_ADDR( or, r->left, r->top ); \
|
||||
\
|
||||
sum = 0; \
|
||||
for( z = 0; z < lines->n_lines; z++ ) { \
|
||||
seq_sum[z] = 0; \
|
||||
for( y = lines->start[z]; y < lines->end[z]; y++ ) \
|
||||
seq_sum[z] += p[y * istride]; \
|
||||
sum += lines->factor[z] * seq_sum[z]; \
|
||||
} \
|
||||
sum = sum / lines->area + mask->offset; \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
\
|
||||
for( y = 1; y < r->height; y++ ) { \
|
||||
sum = 0; \
|
||||
for( z = 0; z < lines->n_lines; z++ ) { \
|
||||
seq_sum[z] += p[seq->end[z]]; \
|
||||
seq_sum[z] -= p[seq->start[z]]; \
|
||||
sum += lines->factor[z] * seq_sum[z]; \
|
||||
} \
|
||||
p += istride; \
|
||||
sum = sum / lines->area + mask->offset; \
|
||||
*q = sum; \
|
||||
q += ostride; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Do vertical masks ... we scan the mask down columns of pixels. Copy-paste
|
||||
* from above with small changes.
|
||||
*/
|
||||
static int
|
||||
aconvsep_generate_vertical( REGION *or, void *vseq, void *a, void *b )
|
||||
{
|
||||
AConvSep *seq = (AConvSep *) vseq;
|
||||
IMAGE *in = (IMAGE *) a;
|
||||
Lines *lines = (Lines *) b;
|
||||
|
||||
REGION *ir = seq->ir;
|
||||
const int n_lines = lines->n_lines;
|
||||
DOUBLEMASK *mask = lines->mask;
|
||||
Rect *r = &or->valid;
|
||||
|
||||
/* Double the width (notionally) for complex.
|
||||
*/
|
||||
int sz = vips_band_format_iscomplex( in->BandFmt ) ?
|
||||
2 * IM_REGION_N_ELEMENTS( or ) : IM_REGION_N_ELEMENTS( or );
|
||||
|
||||
Rect s;
|
||||
int x, y, z;
|
||||
int istride;
|
||||
int ostride;
|
||||
|
||||
/* Prepare the section of the input image we need. A little larger
|
||||
* than the section of the output image we are producing.
|
||||
*/
|
||||
s = *r;
|
||||
s.width += mask->xsize - 1;
|
||||
s.height += mask->ysize - 1;
|
||||
if( im_prepare( ir, &s ) )
|
||||
return( -1 );
|
||||
|
||||
/* Stride can be different for the vertical case, keep this here for
|
||||
* ease of direction change.
|
||||
*/
|
||||
istride = IM_REGION_LSKIP( ir ) /
|
||||
IM_IMAGE_SIZEOF_ELEMENT( lines->in );
|
||||
ostride = IM_REGION_LSKIP( or ) /
|
||||
IM_IMAGE_SIZEOF_ELEMENT( lines->out );
|
||||
|
||||
/* Init offset array.
|
||||
*/
|
||||
if( seq->last_stride != istride ) {
|
||||
seq->last_stride = istride;
|
||||
|
||||
for( z = 0; z < n_lines; z++ ) {
|
||||
seq->start[z] = lines->start[z] * istride;
|
||||
seq->end[z] = lines->end[z] * istride;
|
||||
}
|
||||
}
|
||||
|
||||
switch( in->BandFmt ) {
|
||||
case IM_BANDFMT_UCHAR:
|
||||
VCONV_INT( unsigned char, CLIP_UCHAR );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_CHAR:
|
||||
VCONV_INT( signed char, CLIP_UCHAR );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_USHORT:
|
||||
VCONV_INT( unsigned short, CLIP_USHORT );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_SHORT:
|
||||
VCONV_INT( signed short, CLIP_SHORT );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_UINT:
|
||||
VCONV_INT( unsigned int, CLIP_NONE );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_INT:
|
||||
VCONV_INT( signed int, CLIP_NONE );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_FLOAT:
|
||||
VCONV_FLOAT( float );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_DOUBLE:
|
||||
VCONV_FLOAT( double );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_COMPLEX:
|
||||
VCONV_FLOAT( float );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_DPCOMPLEX:
|
||||
VCONV_FLOAT( double );
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
aconvsep_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask, int n_layers )
|
||||
{
|
||||
Lines *lines;
|
||||
im_generate_fn generate;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "aconvsep_raw: starting with matrix:\n" );
|
||||
im_print_dmask( mask );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( !(lines = lines_new( in, out, mask, n_layers )) )
|
||||
return( -1 );
|
||||
|
||||
/* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output
|
||||
* would be 1x1.
|
||||
*/
|
||||
if( im_cp_desc( out, in ) )
|
||||
return( -1 );
|
||||
out->Xsize -= mask->xsize - 1;
|
||||
out->Ysize -= mask->ysize - 1;
|
||||
if( out->Xsize <= 0 || out->Ysize <= 0 ) {
|
||||
im_error( "im_aconvsep", "%s", _( "image too small for mask" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( mask->xsize == 1 )
|
||||
generate = aconvsep_generate_vertical;
|
||||
else
|
||||
generate = aconvsep_generate_horizontal;
|
||||
|
||||
if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ||
|
||||
im_generate( out,
|
||||
aconvsep_start, generate, aconvsep_stop, in, lines ) )
|
||||
return( -1 );
|
||||
|
||||
out->Xoffset = -mask->xsize / 2;
|
||||
out->Yoffset = -mask->ysize / 2;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* im_aconvsep:
|
||||
* @in: input image
|
||||
* @out: output image
|
||||
* @mask: convolution mask
|
||||
* @n_layers: number of layers for approximation
|
||||
*
|
||||
* Perform an approximate separable convolution of @in with @mask.
|
||||
*
|
||||
* The mask must be 1xn or nx1 elements.
|
||||
* The output image
|
||||
* always has the same #VipsBandFormat as the input image.
|
||||
*
|
||||
* The image is convolved twice: once with @mask and then again with @mask
|
||||
* rotated by 90 degrees.
|
||||
*
|
||||
* Larger values for @n_layers give more accurate
|
||||
* results, but are slower. As @n_layers approaches the mask radius, the
|
||||
* accuracy will become close to exact convolution and the speed will drop to
|
||||
* match. For many large masks, such as Gaussian, @n_layers need be only 10% of
|
||||
* this value and accuracy will still be good.
|
||||
*
|
||||
* See also: im_convsep_f(), im_create_dmaskv().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
im_aconvsep( IMAGE *in, IMAGE *out, DOUBLEMASK *mask, int n_layers )
|
||||
{
|
||||
IMAGE *t[2];
|
||||
const int n_mask = mask->xsize * mask->ysize;
|
||||
DOUBLEMASK *rmask;
|
||||
|
||||
if( im_open_local_array( out, t, 2, "im_aconvsep", "p" ) ||
|
||||
!(rmask = (DOUBLEMASK *) im_local( out,
|
||||
(im_construct_fn) im_dup_dmask,
|
||||
(im_callback_fn) im_free_dmask, mask, mask->filename, NULL )) )
|
||||
return( -1 );
|
||||
|
||||
rmask->xsize = mask->ysize;
|
||||
rmask->ysize = mask->xsize;
|
||||
|
||||
/*
|
||||
*/
|
||||
if( im_embed( in, t[0], 1, n_mask / 2, n_mask / 2,
|
||||
in->Xsize + n_mask - 1, in->Ysize + n_mask - 1 ) ||
|
||||
aconvsep_raw( t[0], t[1], mask, n_layers ) ||
|
||||
aconvsep_raw( t[1], out, rmask, n_layers ) )
|
||||
return( -1 );
|
||||
|
||||
/* For testing .. just try one direction.
|
||||
if( aconvsep_raw( in, out, mask, n_layers ) )
|
||||
return( -1 );
|
||||
*/
|
||||
|
||||
out->Xoffset = 0;
|
||||
out->Yoffset = 0;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,389 +0,0 @@
|
|||
/* im_conv_f
|
||||
*
|
||||
* Copyright: 1990, N. Dessipris.
|
||||
*
|
||||
* Author: Nicos Dessipris & Kirk Martinez
|
||||
* Written on: 29/04/1991
|
||||
* Modified on: 19/05/1991
|
||||
* 8/7/93 JC
|
||||
* - adapted for partial v2
|
||||
* - memory leaks fixed
|
||||
* - ANSIfied
|
||||
* 12/7/93 JC
|
||||
* - adapted im_convbi() to im_convbf()
|
||||
* 7/10/94 JC
|
||||
* - new IM_ARRAY() macro
|
||||
* - evalend callbacks
|
||||
* - more typedef
|
||||
* 9/3/01 JC
|
||||
* - redone from im_conv()
|
||||
* 27/7/01 JC
|
||||
* - rejects masks with scale == 0
|
||||
* 7/4/04
|
||||
* - now uses im_embed() with edge stretching on the input, not
|
||||
* the output
|
||||
* - sets Xoffset / Yoffset
|
||||
* 11/11/05
|
||||
* - simpler inner loop avoids gcc4 bug
|
||||
* 12/11/09
|
||||
* - only rebuild the buffer offsets if bpl changes
|
||||
* - tiny speedups and cleanups
|
||||
* - add restrict, though it doesn't seem to help gcc
|
||||
* - add mask-all-zero check
|
||||
* 13/11/09
|
||||
* - rename as im_conv_f() to make it easier for vips.c to make the
|
||||
* overloaded version
|
||||
* 3/2/10
|
||||
* - gtkdoc
|
||||
* - more cleanups
|
||||
* 1/10/10
|
||||
* - support complex (just double the bands)
|
||||
* 29/10/10
|
||||
* - get rid of im_convsep_f(), just call this twice, no longer worth
|
||||
* keeping two versions
|
||||
* 15/10/11 Nicolas
|
||||
* - handle offset correctly in seperable convolutions
|
||||
* 26/1/16 Lovell Fuller
|
||||
* - remove Duff for a 25% speedup
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
/* Our parameters ... we take a copy of the mask argument, plus we make a
|
||||
* smaller version with the zeros squeezed out.
|
||||
*/
|
||||
typedef struct {
|
||||
IMAGE *in;
|
||||
IMAGE *out;
|
||||
DOUBLEMASK *mask; /* Copy of mask arg */
|
||||
|
||||
int nnz; /* Number of non-zero mask elements */
|
||||
double *coeff; /* Array of non-zero mask coefficients */
|
||||
int *coeff_pos; /* Index of each nnz element in mask->coeff */
|
||||
} Conv;
|
||||
|
||||
static int
|
||||
conv_close( Conv *conv )
|
||||
{
|
||||
IM_FREEF( im_free_dmask, conv->mask );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static Conv *
|
||||
conv_new( IMAGE *in, IMAGE *out, DOUBLEMASK *mask )
|
||||
{
|
||||
Conv *conv = IM_NEW( out, Conv );
|
||||
const int ne = mask->xsize * mask->ysize;
|
||||
int i;
|
||||
|
||||
if( !conv )
|
||||
return( NULL );
|
||||
|
||||
conv->in = in;
|
||||
conv->out = out;
|
||||
conv->mask = NULL;
|
||||
conv->nnz = 0;
|
||||
conv->coeff = NULL;
|
||||
|
||||
if( im_add_close_callback( out,
|
||||
(im_callback_fn) conv_close, conv, NULL ) ||
|
||||
!(conv->coeff = IM_ARRAY( out, ne, double )) ||
|
||||
!(conv->coeff_pos = IM_ARRAY( out, ne, int )) ||
|
||||
!(conv->mask = im_dup_dmask( mask, "conv_mask" )) )
|
||||
return( NULL );
|
||||
|
||||
/* Find non-zero mask elements.
|
||||
*/
|
||||
for( i = 0; i < ne; i++ )
|
||||
if( mask->coeff[i] ) {
|
||||
conv->coeff[conv->nnz] = mask->coeff[i];
|
||||
conv->coeff_pos[conv->nnz] = i;
|
||||
conv->nnz += 1;
|
||||
}
|
||||
|
||||
/* Was the whole mask zero? We must have at least 1 element in there:
|
||||
* set it to zero.
|
||||
*/
|
||||
if( conv->nnz == 0 ) {
|
||||
conv->coeff[0] = mask->coeff[0];
|
||||
conv->coeff_pos[0] = 0;
|
||||
conv->nnz = 1;
|
||||
}
|
||||
|
||||
return( conv );
|
||||
}
|
||||
|
||||
/* Our sequence value.
|
||||
*/
|
||||
typedef struct {
|
||||
Conv *conv;
|
||||
REGION *ir; /* Input region */
|
||||
|
||||
int *offsets; /* Offsets for each non-zero matrix element */
|
||||
VipsPel **pts; /* Per-non-zero mask element image pointers */
|
||||
|
||||
int last_bpl; /* Avoid recalcing offsets, if we can */
|
||||
} ConvSequence;
|
||||
|
||||
/* Free a sequence value.
|
||||
*/
|
||||
static int
|
||||
conv_stop( void *vseq, void *a, void *b )
|
||||
{
|
||||
ConvSequence *seq = (ConvSequence *) vseq;
|
||||
|
||||
IM_FREEF( im_region_free, seq->ir );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Convolution start function.
|
||||
*/
|
||||
static void *
|
||||
conv_start( IMAGE *out, void *a, void *b )
|
||||
{
|
||||
IMAGE *in = (IMAGE *) a;
|
||||
Conv *conv = (Conv *) b;
|
||||
ConvSequence *seq;
|
||||
|
||||
if( !(seq = IM_NEW( out, ConvSequence )) )
|
||||
return( NULL );
|
||||
|
||||
/* Init!
|
||||
*/
|
||||
seq->conv = conv;
|
||||
seq->ir = NULL;
|
||||
seq->pts = NULL;
|
||||
seq->last_bpl = -1;
|
||||
|
||||
/* Attach region and arrays.
|
||||
*/
|
||||
seq->ir = im_region_create( in );
|
||||
seq->offsets = IM_ARRAY( out, conv->nnz, int );
|
||||
seq->pts = IM_ARRAY( out, conv->nnz, VipsPel * );
|
||||
if( !seq->ir || !seq->offsets || !seq->pts ) {
|
||||
conv_stop( seq, in, conv );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( (void *) seq );
|
||||
}
|
||||
|
||||
#define CONV_FLOAT( ITYPE, OTYPE ) { \
|
||||
ITYPE ** restrict p = (ITYPE **) seq->pts; \
|
||||
OTYPE * restrict q = (OTYPE *) IM_REGION_ADDR( or, le, y ); \
|
||||
\
|
||||
for( x = 0; x < sz; x++ ) { \
|
||||
double sum; \
|
||||
int i; \
|
||||
\
|
||||
sum = 0; \
|
||||
for ( i = 0; i < nnz; i++ ) \
|
||||
sum += t[i] * p[i][x]; \
|
||||
\
|
||||
sum = (sum / mask->scale) + mask->offset; \
|
||||
\
|
||||
q[x] = sum; \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Convolve!
|
||||
*/
|
||||
static int
|
||||
conv_gen( REGION *or, void *vseq, void *a, void *b )
|
||||
{
|
||||
ConvSequence *seq = (ConvSequence *) vseq;
|
||||
IMAGE *in = (IMAGE *) a;
|
||||
Conv *conv = (Conv *) b;
|
||||
REGION *ir = seq->ir;
|
||||
DOUBLEMASK *mask = conv->mask;
|
||||
double * restrict t = conv->coeff;
|
||||
const int nnz = conv->nnz;
|
||||
|
||||
Rect *r = &or->valid;
|
||||
Rect s;
|
||||
int le = r->left;
|
||||
int to = r->top;
|
||||
int bo = IM_RECT_BOTTOM(r);
|
||||
int sz = IM_REGION_N_ELEMENTS( or ) *
|
||||
(vips_band_format_iscomplex( in->BandFmt ) ? 2 : 1);
|
||||
|
||||
int x, y, z, i;
|
||||
|
||||
/* Prepare the section of the input image we need. A little larger
|
||||
* than the section of the output image we are producing.
|
||||
*/
|
||||
s = *r;
|
||||
s.width += mask->xsize - 1;
|
||||
s.height += mask->ysize - 1;
|
||||
if( im_prepare( ir, &s ) )
|
||||
return( -1 );
|
||||
|
||||
/* Fill offset array. Only do this if the bpl has changed since the
|
||||
* previous im_prepare().
|
||||
*/
|
||||
if( seq->last_bpl != IM_REGION_LSKIP( ir ) ) {
|
||||
seq->last_bpl = IM_REGION_LSKIP( ir );
|
||||
|
||||
for( i = 0; i < nnz; i++ ) {
|
||||
z = conv->coeff_pos[i];
|
||||
x = z % conv->mask->xsize;
|
||||
y = z / conv->mask->xsize;
|
||||
|
||||
seq->offsets[i] =
|
||||
IM_REGION_ADDR( ir, x + le, y + to ) -
|
||||
IM_REGION_ADDR( ir, le, to );
|
||||
}
|
||||
}
|
||||
|
||||
for( y = to; y < bo; y++ ) {
|
||||
/* Init pts for this line of PELs.
|
||||
*/
|
||||
for( z = 0; z < nnz; z++ )
|
||||
seq->pts[z] = seq->offsets[z] +
|
||||
IM_REGION_ADDR( ir, le, y );
|
||||
|
||||
switch( in->BandFmt ) {
|
||||
case IM_BANDFMT_UCHAR:
|
||||
CONV_FLOAT( unsigned char, float ); break;
|
||||
case IM_BANDFMT_CHAR:
|
||||
CONV_FLOAT( signed char, float ); break;
|
||||
case IM_BANDFMT_USHORT:
|
||||
CONV_FLOAT( unsigned short, float ); break;
|
||||
case IM_BANDFMT_SHORT:
|
||||
CONV_FLOAT( signed short, float ); break;
|
||||
case IM_BANDFMT_UINT:
|
||||
CONV_FLOAT( unsigned int, float ); break;
|
||||
case IM_BANDFMT_INT:
|
||||
CONV_FLOAT( signed int, float ); break;
|
||||
case IM_BANDFMT_FLOAT:
|
||||
case IM_BANDFMT_COMPLEX:
|
||||
CONV_FLOAT( float, float ); break;
|
||||
case IM_BANDFMT_DOUBLE:
|
||||
case IM_BANDFMT_DPCOMPLEX:
|
||||
CONV_FLOAT( double, double ); break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_conv_f_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask )
|
||||
{
|
||||
Conv *conv;
|
||||
|
||||
/* Check parameters.
|
||||
*/
|
||||
if( im_piocheck( in, out ) ||
|
||||
im_check_uncoded( "im_conv", in ) ||
|
||||
im_check_dmask( "im_conv", mask ) )
|
||||
return( -1 );
|
||||
if( mask->scale == 0 ) {
|
||||
im_error( "im_conv_f", "%s", "mask scale must be non-zero" );
|
||||
return( -1 );
|
||||
}
|
||||
if( !(conv = conv_new( in, out, mask )) )
|
||||
return( -1 );
|
||||
|
||||
/* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output
|
||||
* would be 1x1.
|
||||
*/
|
||||
if( im_cp_desc( out, in ) )
|
||||
return( -1 );
|
||||
if( vips_bandfmt_isint( in->BandFmt ) )
|
||||
out->BandFmt = IM_BANDFMT_FLOAT;
|
||||
out->Xsize -= mask->xsize - 1;
|
||||
out->Ysize -= mask->ysize - 1;
|
||||
if( out->Xsize <= 0 || out->Ysize <= 0 ) {
|
||||
im_error( "im_conv_f", "%s", _( "image too small for mask" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
if( im_generate( out, conv_start, conv_gen, conv_stop, in, conv ) )
|
||||
return( -1 );
|
||||
|
||||
out->Xoffset = -mask->xsize / 2;
|
||||
out->Yoffset = -mask->ysize / 2;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* im_conv_f:
|
||||
* @in: input image
|
||||
* @out: output image
|
||||
* @mask: convolution mask
|
||||
*
|
||||
* Convolve @in with @mask using floating-point arithmetic. The output image
|
||||
* is always %IM_BANDFMT_FLOAT unless @in is %IM_BANDFMT_DOUBLE, in which case
|
||||
* @out is also %IM_BANDFMT_DOUBLE.
|
||||
*
|
||||
* Each output pixel is
|
||||
* calculated as sigma[i]{pixel[i] * mask[i]} / scale + offset, where scale
|
||||
* and offset are part of @mask.
|
||||
*
|
||||
* See also: im_conv(), im_convsep_f(), im_create_dmaskv().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
im_conv_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask )
|
||||
{
|
||||
IMAGE *t1 = im_open_local( out, "im_conv_f intermediate", "p" );
|
||||
|
||||
if( !t1 ||
|
||||
im_embed( in, t1, 1, mask->xsize / 2, mask->ysize / 2,
|
||||
in->Xsize + mask->xsize - 1,
|
||||
in->Ysize + mask->ysize - 1 ) ||
|
||||
im_conv_f_raw( t1, out, mask ) )
|
||||
return( -1 );
|
||||
|
||||
out->Xoffset = 0;
|
||||
out->Yoffset = 0;
|
||||
|
||||
return( 0 );
|
||||
}
|
|
@ -121,8 +121,7 @@ vips_sharpen_generate( VipsRegion *or,
|
|||
|
||||
int x, y;
|
||||
|
||||
if( vips_region_prepare( in[0], r ) ||
|
||||
vips_region_prepare( in[1], r ) )
|
||||
if( vips_reorder_prepare_many( or->im, in, r ) )
|
||||
return( -1 );
|
||||
|
||||
VIPS_GATE_START( "vips_sharpen_generate: work" );
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
noinst_LTLIBRARIES = libcreate.la
|
||||
|
||||
libcreate_la_SOURCES = \
|
||||
perlin.c \
|
||||
worley.c \
|
||||
create.c \
|
||||
pcreate.h \
|
||||
gaussmat.c \
|
||||
|
|
|
@ -277,7 +277,9 @@ vips_buildlut_init( VipsBuildlut *lut )
|
|||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* This operation builds a lookup table from a set of points. Intermediate
|
||||
* values are generated by piecewise linear interpolation.
|
||||
* values are generated by piecewise linear interpolation. The lookup table is
|
||||
* always of type #VIPS_FORMAT_DOUBLE, use vips_cast() to change it to the
|
||||
* type you need.
|
||||
*
|
||||
* For example, consider this 2 x 2 matrix of (x, y) coordinates:
|
||||
*
|
||||
|
@ -330,7 +332,7 @@ vips_buildlut_init( VipsBuildlut *lut )
|
|||
* several Ys, each becomes a band in the output LUT. You don't need to
|
||||
* start at zero, any integer will do, including negatives.
|
||||
*
|
||||
* See also: vips_identity(), vips_invertlut().
|
||||
* See also: vips_identity(), vips_invertlut(), vips_cast(), vips_maplut().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
|
|
|
@ -139,6 +139,8 @@ vips_create_operation_init( void )
|
|||
extern GType vips_mask_ideal_band_get_type( void );
|
||||
extern GType vips_mask_fractal_get_type( void );
|
||||
extern GType vips_fractsurf_get_type( void );
|
||||
extern GType vips_worley_get_type( void );
|
||||
extern GType vips_perlin_get_type( void );
|
||||
|
||||
vips_black_get_type();
|
||||
vips_gaussmat_get_type();
|
||||
|
@ -167,5 +169,7 @@ vips_create_operation_init( void )
|
|||
vips_mask_gaussian_band_get_type();
|
||||
vips_mask_fractal_get_type();
|
||||
vips_fractsurf_get_type();
|
||||
vips_worley_get_type();
|
||||
vips_perlin_get_type();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,388 @@
|
|||
/* Perlin noise generator.
|
||||
*
|
||||
* 24/7/16
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define VIPS_DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#include "pcreate.h"
|
||||
|
||||
typedef struct _VipsPerlin {
|
||||
VipsCreate parent_instance;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
int cell_size;
|
||||
gboolean uchar;
|
||||
|
||||
int cells_across;
|
||||
int cells_down;
|
||||
|
||||
/* Use this to seed this call of our rng.
|
||||
*/
|
||||
guint32 seed;
|
||||
} VipsPerlin;
|
||||
|
||||
typedef struct _VipsPerlinClass {
|
||||
VipsCreateClass parent_class;
|
||||
|
||||
} VipsPerlinClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsPerlin, vips_perlin, VIPS_TYPE_CREATE );
|
||||
|
||||
/* cos and sin from an angle in 0 - 255.
|
||||
*/
|
||||
float vips_perlin_cos[256];
|
||||
float vips_perlin_sin[256];
|
||||
|
||||
typedef struct _Sequence {
|
||||
VipsPerlin *perlin;
|
||||
|
||||
/* The position of the last cell we were in. Use this to avoid
|
||||
* regenerating vectors on every pixel lookup.
|
||||
*/
|
||||
int cell_x;
|
||||
int cell_y;
|
||||
|
||||
/* The 2 x 2 grid of unit vectors, with cell_x/cell_y as the top left.
|
||||
*/
|
||||
float gx[4];
|
||||
float gy[4];
|
||||
|
||||
} Sequence;
|
||||
|
||||
/* A very simple random number generator. See:
|
||||
* http://isthe.com/chongo/tech/comp/fnv/#FNV-source
|
||||
*/
|
||||
static guint32
|
||||
vips_perlin_random( guint32 seed )
|
||||
{
|
||||
return( 1103515245u * seed + 12345 );
|
||||
}
|
||||
|
||||
static guint32
|
||||
vips_perlin_seed_add( guint32 seed, int value )
|
||||
{
|
||||
return( ((2166136261u ^ seed) * 16777619u) ^ value );
|
||||
}
|
||||
|
||||
/* Generate a 3 x 3 grid of cells around a point.
|
||||
*/
|
||||
static void
|
||||
vips_perlin_create_cells( VipsPerlin *perlin,
|
||||
float gx[4], float gy[4], int cell_x, int cell_y )
|
||||
{
|
||||
int x, y;
|
||||
|
||||
for( y = 0; y < 2; y++ )
|
||||
for( x = 0; x < 2; x++ ) {
|
||||
int ci = x + y * 2;
|
||||
|
||||
guint32 seed;
|
||||
int cx;
|
||||
int cy;
|
||||
int angle;
|
||||
|
||||
seed = perlin->seed;
|
||||
|
||||
cx = cell_x + x;
|
||||
cy = cell_y + y;
|
||||
|
||||
/* When we calculate the seed for this cell, we wrap
|
||||
* around so that our output will tesselate.
|
||||
*/
|
||||
|
||||
if( cy >= perlin->cells_down )
|
||||
cy = 0;
|
||||
seed = vips_perlin_seed_add( seed, cy );
|
||||
|
||||
if( cx >= perlin->cells_across )
|
||||
cx = 0;
|
||||
seed = vips_perlin_seed_add( seed, cx );
|
||||
|
||||
seed = vips_perlin_random( seed );
|
||||
angle = (seed ^ (seed >> 8) ^ (seed >> 16)) & 0xff;
|
||||
|
||||
gx[ci] = vips_perlin_cos[angle];
|
||||
gy[ci] = vips_perlin_sin[angle];
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
vips_perlin_stop( void *vseq, void *a, void *b )
|
||||
{
|
||||
Sequence *seq = (Sequence *) vseq;
|
||||
|
||||
VIPS_FREE( seq );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void *
|
||||
vips_perlin_start( VipsImage *out, void *a, void *b )
|
||||
{
|
||||
VipsPerlin *perlin = (VipsPerlin *) b;
|
||||
|
||||
Sequence *seq;
|
||||
|
||||
if( !(seq = VIPS_NEW( NULL, Sequence )) )
|
||||
return( NULL );
|
||||
|
||||
seq->perlin = perlin;
|
||||
seq->cell_x = -1;
|
||||
seq->cell_y = -1;
|
||||
|
||||
return( seq );
|
||||
}
|
||||
|
||||
/* Smooth linear interpolation, 0 <= x <= 1.
|
||||
*
|
||||
* https://en.wikipedia.org/wiki/Smoothstep
|
||||
*/
|
||||
static float
|
||||
smootherstep( float x )
|
||||
{
|
||||
return( x * x * x * (x * (x * 6 - 15) + 10) );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_perlin_gen( VipsRegion *or, void *vseq, void *a, void *b,
|
||||
gboolean *stop )
|
||||
{
|
||||
VipsPerlin *perlin = (VipsPerlin *) a;
|
||||
VipsRect *r = &or->valid;
|
||||
Sequence *seq = (Sequence *) vseq;
|
||||
|
||||
int x, y;
|
||||
|
||||
for( y = 0; y < r->height; y++ ) {
|
||||
float *fq = (float *)
|
||||
VIPS_REGION_ADDR( or, r->left, r->top + y );
|
||||
VipsPel *q = (VipsPel *) fq;
|
||||
|
||||
for( x = 0; x < r->width; x++ ) {
|
||||
int cs = perlin->cell_size;
|
||||
int cell_x = (r->left + x) / cs;
|
||||
int cell_y = (r->top + y) / cs;
|
||||
float dx = (x + r->left - cell_x * cs) / (float) cs;
|
||||
float dy = (y + r->top - cell_y * cs) / (float) cs;
|
||||
float sx = smootherstep( dx );
|
||||
float sy = smootherstep( dy );
|
||||
|
||||
float n0, n1;
|
||||
float ix0, ix1;
|
||||
float p;
|
||||
|
||||
if( cell_x != seq->cell_x ||
|
||||
cell_y != seq->cell_y ) {
|
||||
vips_perlin_create_cells( perlin,
|
||||
seq->gx, seq->gy, cell_x, cell_y );
|
||||
seq->cell_x = cell_x;
|
||||
seq->cell_y = cell_y;
|
||||
}
|
||||
|
||||
n0 = -dx * seq->gx[0] + -dy * seq->gy[0];
|
||||
n1 = (1 - dx) * seq->gx[1] + -dy * seq->gy[1];
|
||||
ix0 = n0 + sx * (n1 - n0);
|
||||
|
||||
n0 = -dx * seq->gx[2] + (1 - dy) * seq->gy[2];
|
||||
n1 = (1 - dx) * seq->gx[3] + (1 - dy) * seq->gy[3];
|
||||
ix1 = n0 + sx * (n1 - n0);
|
||||
|
||||
p = ix0 + sy * (ix1 - ix0);
|
||||
|
||||
if( perlin->uchar )
|
||||
q[x] = 128 * p + 128;
|
||||
else
|
||||
fq[x] = p;
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_perlin_build( VipsObject *object )
|
||||
{
|
||||
VipsCreate *create = VIPS_CREATE( object );
|
||||
VipsPerlin *perlin = (VipsPerlin *) object;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_perlin_parent_class )->build( object ) )
|
||||
return( -1 );
|
||||
|
||||
/* Be careful if width is a multiple of cell_size.
|
||||
*/
|
||||
perlin->cells_across =
|
||||
VIPS_ROUND_UP( perlin->width, perlin->cell_size ) /
|
||||
perlin->cell_size;
|
||||
perlin->cells_down =
|
||||
VIPS_ROUND_UP( perlin->height, perlin->cell_size ) /
|
||||
perlin->cell_size;
|
||||
|
||||
perlin->seed = g_random_double() * 0xffffffffu;
|
||||
|
||||
vips_image_init_fields( create->out,
|
||||
perlin->width, perlin->height, 1,
|
||||
perlin->uchar ? VIPS_FORMAT_UCHAR : VIPS_FORMAT_FLOAT,
|
||||
VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W,
|
||||
1.0, 1.0 );
|
||||
vips_image_pipelinev( create->out,
|
||||
VIPS_DEMAND_STYLE_ANY, NULL );
|
||||
if( vips_image_generate( create->out,
|
||||
vips_perlin_start, vips_perlin_gen, vips_perlin_stop,
|
||||
perlin, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void *
|
||||
vips_perlin_make_tables( void *client )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < 256; i++ ) {
|
||||
double angle = 2 * M_PI * i / 256.0;
|
||||
|
||||
vips_perlin_cos[i] = cos( angle );
|
||||
vips_perlin_sin[i] = sin( angle );
|
||||
}
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_perlin_class_init( VipsPerlinClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
|
||||
|
||||
static GOnce once = G_ONCE_INIT;
|
||||
|
||||
(void) g_once( &once, vips_perlin_make_tables, NULL );
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
vobject_class->nickname = "perlin";
|
||||
vobject_class->description = _( "make a perlin noise image" );
|
||||
vobject_class->build = vips_perlin_build;
|
||||
|
||||
VIPS_ARG_INT( class, "width", 2,
|
||||
_( "Width" ),
|
||||
_( "Image width in pixels" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsPerlin, width ),
|
||||
1, VIPS_MAX_COORD, 1 );
|
||||
|
||||
VIPS_ARG_INT( class, "height", 3,
|
||||
_( "Height" ),
|
||||
_( "Image height in pixels" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsPerlin, height ),
|
||||
1, VIPS_MAX_COORD, 1 );
|
||||
|
||||
VIPS_ARG_INT( class, "cell_size", 3,
|
||||
_( "Cell size" ),
|
||||
_( "Size of Perlin cells" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsPerlin, cell_size ),
|
||||
1, VIPS_MAX_COORD, 256 );
|
||||
|
||||
VIPS_ARG_BOOL( class, "uchar", 4,
|
||||
_( "Uchar" ),
|
||||
_( "Output an unsigned char image" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsPerlin, uchar ),
|
||||
FALSE );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_perlin_init( VipsPerlin *perlin )
|
||||
{
|
||||
perlin->cell_size = 256;
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_perlin:
|
||||
* @out: output image
|
||||
* @width: horizontal size
|
||||
* @height: vertical size
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @cell_size: %gint, size of Perlin cells
|
||||
* * @uchar: output a uchar image
|
||||
*
|
||||
* Create a one-band float image of Perlin noise. See:
|
||||
*
|
||||
* https://en.wikipedia.org/wiki/Perlin_noise
|
||||
*
|
||||
* Use @cell_size to set the size of the cells from which the image is
|
||||
* constructed. The default is 256 x 256.
|
||||
*
|
||||
* If @width and @height are multiples of @cell_size, the image will tessellate.
|
||||
*
|
||||
* Normally, output pixels are #VIPS_FORMAT_FLOAT in the range [-1, +1]. Set
|
||||
* @uchar to output a uchar image with pixels in [0, 255].
|
||||
*
|
||||
* See also: vips_worley(), vips_fractsurf(), vips_gaussnoise().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
vips_perlin( VipsImage **out, int width, int height, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, height );
|
||||
result = vips_call_split( "perlin", ap, out, width, height );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
|
@ -341,11 +341,11 @@ vips_text_init( VipsText *text )
|
|||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @font: font to render with
|
||||
* * @width: render within this many pixels across
|
||||
* * @alignment: left/centre/right alignment
|
||||
* * @dpi: render at this resolution
|
||||
* * @spacing: space lines by this in points
|
||||
* * @font: %gchararray, font to render with
|
||||
* * @width: %gint, image should be no wider than this many pixels
|
||||
* * @align: #VipsAlign, left/centre/right alignment
|
||||
* * @dpi: %gint, render at this resolution
|
||||
* * @spacing: %gint, space lines by this in points
|
||||
*
|
||||
* Draw the string @text to an image. @out is a one-band 8-bit
|
||||
* unsigned char image, with 0 for no text and 255 for text. Values inbetween
|
||||
|
@ -359,7 +359,7 @@ vips_text_init( VipsText *text )
|
|||
*
|
||||
* @width is the maximum number of pixels across to draw within. If the
|
||||
* generated text is wider than this, it will wrap to a new line. In this
|
||||
* case, @alignment can be used to set the alignment style for multi-line
|
||||
* case, @align can be used to set the alignment style for multi-line
|
||||
* text.
|
||||
*
|
||||
* @dpi sets the resolution to render at. "sans 12" at 72 dpi draws characters
|
||||
|
|
|
@ -0,0 +1,390 @@
|
|||
/* Worley noise generator.
|
||||
*
|
||||
* 19/7/16
|
||||
*
|
||||
* 11/8/16
|
||||
* - float output
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define VIPS_DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#include "pcreate.h"
|
||||
|
||||
typedef struct _VipsWorley {
|
||||
VipsCreate parent_instance;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
int cell_size;
|
||||
|
||||
int cells_across;
|
||||
int cells_down;
|
||||
|
||||
/* Use this to seed this call of our rng.
|
||||
*/
|
||||
guint32 seed;
|
||||
} VipsWorley;
|
||||
|
||||
typedef struct _VipsWorleyClass {
|
||||
VipsCreateClass parent_class;
|
||||
|
||||
} VipsWorleyClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsWorley, vips_worley, VIPS_TYPE_CREATE );
|
||||
|
||||
#define MAX_FEATURES (10)
|
||||
|
||||
typedef struct _Cell {
|
||||
/* Cell position, in number of cells. Scale by cell_size to get
|
||||
* absolute image cods.
|
||||
*/
|
||||
int cell_x;
|
||||
int cell_y;
|
||||
|
||||
/* A cell contains 1 to n features.
|
||||
*/
|
||||
int n_features;
|
||||
|
||||
/* Feature coordinates, in absolute image space.
|
||||
*/
|
||||
int feature_x[MAX_FEATURES];
|
||||
int feature_y[MAX_FEATURES];
|
||||
} Cell;
|
||||
|
||||
typedef struct _Sequence {
|
||||
VipsWorley *worley;
|
||||
|
||||
/* The position of the last cell we were in. Use this to avoid
|
||||
* regenerating cells on every pixel lookup.
|
||||
*/
|
||||
int cell_x;
|
||||
int cell_y;
|
||||
|
||||
/* The 3 x 3 grid of cells around the current point.
|
||||
*/
|
||||
Cell cells[9];
|
||||
|
||||
} Sequence;
|
||||
|
||||
/* A very simple random number generator. See:
|
||||
* http://isthe.com/chongo/tech/comp/fnv/#FNV-source
|
||||
*/
|
||||
static guint32
|
||||
vips_worley_random( guint32 seed )
|
||||
{
|
||||
return( 1103515245u * seed + 12345 );
|
||||
}
|
||||
|
||||
static guint32
|
||||
vips_worley_seed_add( guint32 seed, int value )
|
||||
{
|
||||
return( ((2166136261u ^ seed) * 16777619u) ^ value );
|
||||
}
|
||||
|
||||
/* Generate a 3 x 3 grid of cells around a point.
|
||||
*/
|
||||
static void
|
||||
vips_worley_create_cells( VipsWorley *worley,
|
||||
Cell cells[9], int cell_x, int cell_y )
|
||||
{
|
||||
int x, y;
|
||||
|
||||
for( y = 0; y < 3; y++ )
|
||||
for( x = 0; x < 3; x++ ) {
|
||||
Cell *cell = &cells[x + y * 3];
|
||||
|
||||
guint32 seed;
|
||||
int value;
|
||||
int j;
|
||||
|
||||
/* Can go <0 and >width for edges.
|
||||
*/
|
||||
cell->cell_x = cell_x + x - 1;
|
||||
cell->cell_y = cell_y + y - 1;
|
||||
|
||||
seed = worley->seed;
|
||||
|
||||
/* When we calculate the seed for this cell, we wrap
|
||||
* around so that our output will tesselate.
|
||||
*/
|
||||
if( cell->cell_x >= worley->cells_across )
|
||||
value = 0;
|
||||
else if( cell->cell_x < 0 )
|
||||
value = worley->cells_across - 1;
|
||||
else
|
||||
value = cell->cell_x;
|
||||
seed = vips_worley_seed_add( seed, value );
|
||||
|
||||
if( cell->cell_y >= worley->cells_down )
|
||||
value = 0;
|
||||
else if( cell->cell_y < 0 )
|
||||
value = worley->cells_down - 1;
|
||||
else
|
||||
value = cell->cell_y;
|
||||
seed = vips_worley_seed_add( seed, value );
|
||||
|
||||
/* [1, MAX_FEATURES)
|
||||
*/
|
||||
seed = vips_worley_random( seed );
|
||||
cell->n_features = (seed % (MAX_FEATURES - 1)) + 1;
|
||||
|
||||
for( j = 0; j < cell->n_features; j++ ) {
|
||||
seed = vips_worley_random( seed );
|
||||
cell->feature_x[j] =
|
||||
cell->cell_x * worley->cell_size +
|
||||
seed % worley->cell_size;
|
||||
|
||||
seed = vips_worley_random( seed );
|
||||
cell->feature_y[j] =
|
||||
cell->cell_y * worley->cell_size +
|
||||
seed % worley->cell_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
vips_worley_stop( void *vseq, void *a, void *b )
|
||||
{
|
||||
Sequence *seq = (Sequence *) vseq;
|
||||
|
||||
VIPS_FREE( seq );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void *
|
||||
vips_worley_start( VipsImage *out, void *a, void *b )
|
||||
{
|
||||
VipsWorley *worley = (VipsWorley *) b;
|
||||
|
||||
Sequence *seq;
|
||||
|
||||
if( !(seq = VIPS_NEW( NULL, Sequence )) )
|
||||
return( NULL );
|
||||
|
||||
seq->worley = worley;
|
||||
seq->cell_x = -1;
|
||||
seq->cell_y = -1;
|
||||
|
||||
return( seq );
|
||||
}
|
||||
|
||||
static float
|
||||
vips_hypot( int x, int y )
|
||||
{
|
||||
/* Faster than hypot() for int args.
|
||||
*/
|
||||
return( sqrt( x * x + y * y ) );
|
||||
}
|
||||
|
||||
static float
|
||||
vips_worley_distance( VipsWorley *worley, Cell cells[9], int x, int y )
|
||||
{
|
||||
float distance;
|
||||
|
||||
int i, j;
|
||||
|
||||
distance = worley->cell_size * 1.5;
|
||||
|
||||
for( i = 0; i < 9; i++ ) {
|
||||
Cell *cell = &cells[i];
|
||||
|
||||
for( j = 0; j < cell->n_features; j++ ) {
|
||||
float d = vips_hypot(
|
||||
x - cell->feature_x[j],
|
||||
y - cell->feature_y[j] );
|
||||
|
||||
distance = VIPS_MIN( distance, d );
|
||||
}
|
||||
}
|
||||
|
||||
return( distance );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_worley_gen( VipsRegion *or, void *vseq, void *a, void *b,
|
||||
gboolean *stop )
|
||||
{
|
||||
VipsWorley *worley = (VipsWorley *) a;
|
||||
VipsRect *r = &or->valid;
|
||||
Sequence *seq = (Sequence *) vseq;
|
||||
|
||||
int x, y;
|
||||
|
||||
for( y = 0; y < r->height; y++ ) {
|
||||
float *q = (float *) VIPS_REGION_ADDR( or, r->left, r->top + y );
|
||||
|
||||
for( x = 0; x < r->width; x++ ) {
|
||||
int cell_x = (r->left + x) / worley->cell_size;
|
||||
int cell_y = (r->top + y) / worley->cell_size;
|
||||
|
||||
if( cell_x != seq->cell_x ||
|
||||
cell_y != seq->cell_y ) {
|
||||
vips_worley_create_cells( worley,
|
||||
seq->cells, cell_x, cell_y );
|
||||
seq->cell_x = cell_x;
|
||||
seq->cell_y = cell_y;
|
||||
}
|
||||
|
||||
q[x] = vips_worley_distance( worley, seq->cells,
|
||||
r->left + x, r->top + y );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_worley_build( VipsObject *object )
|
||||
{
|
||||
VipsCreate *create = VIPS_CREATE( object );
|
||||
VipsWorley *worley = (VipsWorley *) object;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_worley_parent_class )->build( object ) )
|
||||
return( -1 );
|
||||
|
||||
/* Be careful if width is a multiple of cell_size.
|
||||
*/
|
||||
worley->cells_across =
|
||||
VIPS_ROUND_UP( worley->width, worley->cell_size ) /
|
||||
worley->cell_size;
|
||||
worley->cells_down =
|
||||
VIPS_ROUND_UP( worley->height, worley->cell_size ) /
|
||||
worley->cell_size;
|
||||
|
||||
worley->seed = g_random_double() * 0xffffffffu;
|
||||
|
||||
vips_image_init_fields( create->out,
|
||||
worley->width, worley->height, 1,
|
||||
VIPS_FORMAT_FLOAT, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W,
|
||||
1.0, 1.0 );
|
||||
vips_image_pipelinev( create->out,
|
||||
VIPS_DEMAND_STYLE_ANY, NULL );
|
||||
if( vips_image_generate( create->out,
|
||||
vips_worley_start, vips_worley_gen, vips_worley_stop,
|
||||
worley, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_worley_class_init( VipsWorleyClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
vobject_class->nickname = "worley";
|
||||
vobject_class->description = _( "make a worley noise image" );
|
||||
vobject_class->build = vips_worley_build;
|
||||
|
||||
VIPS_ARG_INT( class, "width", 2,
|
||||
_( "Width" ),
|
||||
_( "Image width in pixels" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsWorley, width ),
|
||||
1, VIPS_MAX_COORD, 1 );
|
||||
|
||||
VIPS_ARG_INT( class, "height", 3,
|
||||
_( "Height" ),
|
||||
_( "Image height in pixels" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsWorley, height ),
|
||||
1, VIPS_MAX_COORD, 1 );
|
||||
|
||||
VIPS_ARG_INT( class, "cell_size", 3,
|
||||
_( "Cell size" ),
|
||||
_( "Size of Worley cells" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsWorley, cell_size ),
|
||||
1, VIPS_MAX_COORD, 256 );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_worley_init( VipsWorley *worley )
|
||||
{
|
||||
worley->cell_size = 256;
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_worley:
|
||||
* @out: output image
|
||||
* @width: horizontal size
|
||||
* @height: vertical size
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @cell_size: %gint, size of Worley cells
|
||||
*
|
||||
* Create a one-band float image of Worley noise. See:
|
||||
*
|
||||
* https://en.wikipedia.org/wiki/Worley_noise
|
||||
*
|
||||
* Use @cell_size to set the size of the cells from which the image is
|
||||
* constructed. The default is 256 x 256.
|
||||
*
|
||||
* If @width and @height are multiples of @cell_size, the image will tessellate.
|
||||
*
|
||||
* See also: vips_perlin(), vips_fractsurf(), vips_gaussnoise().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
vips_worley( VipsImage **out, int width, int height, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, height );
|
||||
result = vips_call_split( "worley", ap, out, width, height );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@
|
|||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#include "../foreign/csv.h"
|
||||
#include "../foreign/pforeign.h"
|
||||
|
||||
int
|
||||
im_csv2vips( const char *filename, IMAGE *out )
|
||||
|
@ -76,7 +76,7 @@ im_csv2vips( const char *filename, IMAGE *out )
|
|||
}
|
||||
|
||||
if( vips__csv_read( name, out,
|
||||
start_skip, lines, whitespace, separator ) )
|
||||
start_skip, lines, whitespace, separator, FALSE ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
#include <vips/thread.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "../foreign/openexr2vips.h"
|
||||
#include "../foreign/pforeign.h"
|
||||
|
||||
int
|
||||
im_exr2vips( const char *filename, IMAGE *out )
|
||||
|
|
|
@ -48,9 +48,7 @@
|
|||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#ifdef HAVE_JPEG
|
||||
#include "../foreign/vipsjpeg.h"
|
||||
#endif /*HAVE_JPEG*/
|
||||
#include "../foreign/pforeign.h"
|
||||
|
||||
static int
|
||||
jpeg2vips( const char *name, IMAGE *out, gboolean header_only )
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#include "../foreign/magick.h"
|
||||
#include "../foreign/pforeign.h"
|
||||
|
||||
int
|
||||
im_magick2vips( const char *filename, IMAGE *out )
|
||||
|
@ -50,7 +50,7 @@ im_magick2vips( const char *filename, IMAGE *out )
|
|||
#ifdef HAVE_MAGICK
|
||||
/* Old behaviour was always to read all frames.
|
||||
*/
|
||||
return( vips__magick_read( filename, out, TRUE, NULL, 0 ) );
|
||||
return( vips__magick_read( filename, out, NULL, 0, -1 ) );
|
||||
#else
|
||||
vips_error( "im_magick2vips",
|
||||
"%s", _( "no libMagick support in your libvips" ) );
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
#include <vips/vips.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "../foreign/vipspng.h"
|
||||
#include "../foreign/pforeign.h"
|
||||
|
||||
static int
|
||||
png2vips( const char *name, IMAGE *out, gboolean header_only )
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
#include <vips/internal.h>
|
||||
#include <vips/thread.h>
|
||||
|
||||
#include "../foreign/tiff.h"
|
||||
#include "../foreign/pforeign.h"
|
||||
|
||||
static int
|
||||
tiff2vips( const char *name, IMAGE *out, gboolean header_only )
|
||||
|
@ -94,11 +94,11 @@ tiff2vips( const char *name, IMAGE *out, gboolean header_only )
|
|||
}
|
||||
|
||||
if( header_only ) {
|
||||
if( vips__tiff_read_header( filename, out, page ) )
|
||||
if( vips__tiff_read_header( filename, out, page, 1, FALSE ) )
|
||||
return( -1 );
|
||||
}
|
||||
else {
|
||||
if( vips__tiff_read( filename, out, page, TRUE ) )
|
||||
if( vips__tiff_read( filename, out, page, 1, FALSE, TRUE ) )
|
||||
return( -1 );
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
#include <vips/vips.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "../foreign/webp.h"
|
||||
#include "../foreign/pforeign.h"
|
||||
|
||||
static int
|
||||
webp2vips( const char *name, IMAGE *out, gboolean header_only )
|
||||
|
|
|
@ -724,3 +724,79 @@ vips_check_bands_3ormore( const char *domain, VipsImage *im )
|
|||
{
|
||||
return( vips_check_bands_atleast( domain, im, 3 ) );
|
||||
}
|
||||
|
||||
/* The old vips_info() stuff, now replaced by g_warning() / g_info().
|
||||
*/
|
||||
|
||||
int vips__info = 0;
|
||||
|
||||
void
|
||||
vips_info_set( gboolean info )
|
||||
{
|
||||
vips__info = info;
|
||||
|
||||
if( info ) {
|
||||
const char *old;
|
||||
char *new;
|
||||
|
||||
old = g_getenv( "G_MESSAGES_DEBUG" );
|
||||
if( !old )
|
||||
old = "";
|
||||
new = g_strdup_printf( "%s VIPS", old );
|
||||
g_setenv( "G_MESSAGES_DEBUG", new, TRUE );
|
||||
g_free( new );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vips_vinfo( const char *domain, const char *fmt, va_list ap )
|
||||
{
|
||||
if( vips__info ) {
|
||||
g_mutex_lock( vips__global_lock );
|
||||
(void) fprintf( stderr, _( "%s: " ), _( "info" ) );
|
||||
if( domain )
|
||||
(void) fprintf( stderr, _( "%s: " ), domain );
|
||||
(void) vfprintf( stderr, fmt, ap );
|
||||
(void) fprintf( stderr, "\n" );
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vips_info( const char *domain, const char *fmt, ... )
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start( ap, fmt );
|
||||
vips_vinfo( domain, fmt, ap );
|
||||
va_end( ap );
|
||||
}
|
||||
|
||||
void
|
||||
vips_vwarn( const char *domain, const char *fmt, va_list ap )
|
||||
{
|
||||
if( !g_getenv( "IM_WARNING" ) &&
|
||||
!g_getenv( "VIPS_WARNING" ) ) {
|
||||
g_mutex_lock( vips__global_lock );
|
||||
(void) fprintf( stderr, _( "%s: " ), _( "vips warning" ) );
|
||||
if( domain )
|
||||
(void) fprintf( stderr, _( "%s: " ), domain );
|
||||
(void) vfprintf( stderr, fmt, ap );
|
||||
(void) fprintf( stderr, "\n" );
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
}
|
||||
|
||||
if( vips__fatal )
|
||||
vips_error_exit( "vips__fatal" );
|
||||
}
|
||||
|
||||
void
|
||||
vips_warn( const char *domain, const char *fmt, ... )
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start( ap, fmt );
|
||||
vips_vwarn( domain, fmt, ap );
|
||||
va_end( ap );
|
||||
}
|
||||
|
||||
|
|
|
@ -649,12 +649,11 @@ process_region( VipsRegion *or, void *seq, void *a, void *b )
|
|||
|
||||
/* Prepare all input regions and make buffer pointers.
|
||||
*/
|
||||
for( i = 0; ir[i]; i++ ) {
|
||||
if( vips_region_prepare( ir[i], &or->valid ) )
|
||||
return( -1 );
|
||||
if( vips_reorder_prepare_many( or->im, ir, &or->valid ) )
|
||||
return( -1 );
|
||||
for( i = 0; ir[i]; i++ )
|
||||
p[i] = (PEL *) VIPS_REGION_ADDR( ir[i],
|
||||
or->valid.left, or->valid.top );
|
||||
}
|
||||
p[i] = NULL;
|
||||
q = (PEL *) VIPS_REGION_ADDR( or, or->valid.left, or->valid.top );
|
||||
|
||||
|
@ -2404,6 +2403,116 @@ im_convsep_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask )
|
|||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_conv( VipsImage *in, VipsImage *out, INTMASK *mask )
|
||||
{
|
||||
VipsImage *t1, *t2;
|
||||
|
||||
if( !(t1 = vips_image_new()) ||
|
||||
im_imask2vips( mask, t1 ) )
|
||||
return( -1 );
|
||||
if( vips_convi( in, &t2, t1,
|
||||
NULL ) ) {
|
||||
g_object_unref( t1 );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t1 );
|
||||
if( vips_image_write( t2, out ) ) {
|
||||
g_object_unref( t2 );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t2 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_conv_raw( VipsImage *in, VipsImage *out, INTMASK *mask )
|
||||
{
|
||||
im_error( "im_conv_raw", "no compat function" );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
int
|
||||
im_conv_f( VipsImage *in, VipsImage *out, DOUBLEMASK *mask )
|
||||
{
|
||||
VipsImage *t1, *t2;
|
||||
|
||||
if( !(t1 = vips_image_new()) ||
|
||||
im_mask2vips( mask, t1 ) )
|
||||
return( -1 );
|
||||
if( vips_convf( in, &t2, t1,
|
||||
NULL ) ) {
|
||||
g_object_unref( t1 );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t1 );
|
||||
if( vips_image_write( t2, out ) ) {
|
||||
g_object_unref( t2 );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t2 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_aconvsep( VipsImage *in, VipsImage *out, DOUBLEMASK *mask, int n_layers )
|
||||
{
|
||||
VipsImage *t1, *t2;
|
||||
|
||||
if( !(t1 = vips_image_new()) ||
|
||||
im_mask2vips( mask, t1 ) )
|
||||
return( -1 );
|
||||
if( vips_convasep( in, &t2, t1,
|
||||
"layers", n_layers,
|
||||
NULL ) ) {
|
||||
g_object_unref( t1 );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t1 );
|
||||
if( vips_image_write( t2, out ) ) {
|
||||
g_object_unref( t2 );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t2 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_aconv( VipsImage *in, VipsImage *out,
|
||||
DOUBLEMASK *mask, int n_layers, int cluster )
|
||||
{
|
||||
VipsImage *t1, *t2;
|
||||
|
||||
if( !(t1 = vips_image_new()) ||
|
||||
im_mask2vips( mask, t1 ) )
|
||||
return( -1 );
|
||||
if( vips_conva( in, &t2, t1,
|
||||
"layers", n_layers,
|
||||
"cluster", cluster,
|
||||
NULL ) ) {
|
||||
g_object_unref( t1 );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t1 );
|
||||
if( vips_image_write( t2, out ) ) {
|
||||
g_object_unref( t2 );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t2 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_conv_f_raw( VipsImage *in, VipsImage *out, DOUBLEMASK *mask )
|
||||
{
|
||||
im_error( "im_conv_f_raw", "no compat function" );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
int
|
||||
im_addgnoise( IMAGE *in, IMAGE *out, double sigma )
|
||||
{
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
noinst_LTLIBRARIES = libforeign.la
|
||||
|
||||
libforeign_la_SOURCES = \
|
||||
pforeign.h \
|
||||
exif.c \
|
||||
gifload.c \
|
||||
cairo.c \
|
||||
pdfload.c \
|
||||
svgload.c \
|
||||
radiance.h \
|
||||
radiance.c \
|
||||
radload.c \
|
||||
radsave.c \
|
||||
ppm.h \
|
||||
ppm.c \
|
||||
ppmload.c \
|
||||
ppmsave.c \
|
||||
csv.h \
|
||||
csv.c \
|
||||
csvload.c \
|
||||
csvsave.c \
|
||||
|
@ -26,34 +26,28 @@ libforeign_la_SOURCES = \
|
|||
dbh.h \
|
||||
analyzeload.c \
|
||||
analyze2vips.c \
|
||||
analyze2vips.h \
|
||||
foreign.c \
|
||||
matlab.h \
|
||||
matlab.c \
|
||||
matload.c \
|
||||
magick.h \
|
||||
magick2vips.c \
|
||||
magickload.c \
|
||||
magick7load.c \
|
||||
pngload.c \
|
||||
pngsave.c \
|
||||
vipspng.h \
|
||||
vipspng.c \
|
||||
openexr2vips.h \
|
||||
openexr2vips.c \
|
||||
openexrload.c \
|
||||
fits.h \
|
||||
fits.c \
|
||||
fitsload.c \
|
||||
fitssave.c \
|
||||
tiff.h \
|
||||
tiff.c \
|
||||
vips2tiff.c \
|
||||
tiff2vips.c \
|
||||
tiffload.c \
|
||||
tiffsave.c \
|
||||
openslide2vips.h \
|
||||
openslide2vips.c \
|
||||
openslideload.c \
|
||||
webp.h \
|
||||
webpload.c \
|
||||
webpsave.c \
|
||||
webp2vips.c \
|
||||
|
@ -61,7 +55,6 @@ libforeign_la_SOURCES = \
|
|||
vips2jpeg.c \
|
||||
jpeg2vips.c \
|
||||
jpeg.h \
|
||||
vipsjpeg.h \
|
||||
jpegload.c \
|
||||
jpegsave.c
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
#include <vips/internal.h>
|
||||
|
||||
#include "dbh.h"
|
||||
#include "analyze2vips.h"
|
||||
#include "pforeign.h"
|
||||
|
||||
/* The things we can have in header fields. Can't use GType, since we want a
|
||||
* static value we can use in a declaration.
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
/* common defs for tiff read/write
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Copyright (C) 1991-2005 The National Gallery
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifndef VIPS_ANALYZE_H
|
||||
#define VIPS_ANALYZE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
int vips__isanalyze( const char *filename );
|
||||
int vips__analyze_read_header( const char *filename, VipsImage *out );
|
||||
int vips__analyze_read( const char *filename, VipsImage *out );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
#endif /*VIPS_ANALYZE_H*/
|
|
@ -50,7 +50,7 @@
|
|||
|
||||
#ifdef HAVE_ANALYZE
|
||||
|
||||
#include "analyze2vips.h"
|
||||
#include "pforeign.h"
|
||||
|
||||
typedef struct _VipsForeignLoadAnalyze {
|
||||
VipsForeignLoad parent_object;
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/* Shared code for cairo based loaders like svgload and pdfload.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
/* Convert from ARGB to RGBA and undo premultiplication.
|
||||
*
|
||||
* See also openslide's argb2rgba().
|
||||
*/
|
||||
void
|
||||
vips__cairo2rgba( guint32 * restrict buf, int n )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < n; i++ ) {
|
||||
guint32 * restrict p = buf + i;
|
||||
guint32 x = *p;
|
||||
guint8 a = x >> 24;
|
||||
VipsPel * restrict out = (VipsPel *) p;
|
||||
|
||||
if( a == 255 )
|
||||
*p = GUINT32_TO_BE( (x << 8) | 255 );
|
||||
else if( a == 0 )
|
||||
*p = GUINT32_TO_BE( x << 8 );
|
||||
else {
|
||||
/* Undo premultiplication.
|
||||
*/
|
||||
out[0] = 255 * ((x >> 16) & 255) / a;
|
||||
out[1] = 255 * ((x >> 8) & 255) / a;
|
||||
out[2] = 255 * (x & 255) / a;
|
||||
out[3] = a;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,6 +30,8 @@
|
|||
* 4/6/15
|
||||
* - try to support DOS files under linux ... we have to look for \r\n
|
||||
* linebreaks
|
||||
* 12/8/16
|
||||
* - allow missing offset and scale in matrix header
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -73,7 +75,7 @@
|
|||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#include "csv.h"
|
||||
#include "pforeign.h"
|
||||
|
||||
/* Skip to the start of the next line (ie. read until we see a '\n'), return
|
||||
* zero if we are at EOF.
|
||||
|
@ -170,7 +172,7 @@ skip_to_sep( FILE *fp, const char sepmap[256] )
|
|||
*/
|
||||
static int
|
||||
read_double( FILE *fp, const char whitemap[256], const char sepmap[256],
|
||||
int lineno, int colno, double *out )
|
||||
int lineno, int colno, double *out, gboolean fail )
|
||||
{
|
||||
int ch;
|
||||
|
||||
|
@ -193,9 +195,10 @@ read_double( FILE *fp, const char whitemap[256], const char sepmap[256],
|
|||
/* Only a warning, since (for example) exported spreadsheets
|
||||
* will often have text or date fields.
|
||||
*/
|
||||
vips_warn( "csv2vips",
|
||||
_( "error parsing number, line %d, column %d" ),
|
||||
g_warning( _( "error parsing number, line %d, column %d" ),
|
||||
lineno, colno );
|
||||
if( fail )
|
||||
return( EOF );
|
||||
|
||||
/* Step over the bad data to the next separator.
|
||||
*/
|
||||
|
@ -220,7 +223,8 @@ read_csv( FILE *fp, VipsImage *out,
|
|||
int skip,
|
||||
int lines,
|
||||
const char *whitespace, const char *separator,
|
||||
gboolean read_image )
|
||||
gboolean read_image,
|
||||
gboolean fail )
|
||||
{
|
||||
int i;
|
||||
char whitemap[256];
|
||||
|
@ -263,7 +267,7 @@ read_csv( FILE *fp, VipsImage *out,
|
|||
}
|
||||
for( columns = 0;
|
||||
(ch = read_double( fp, whitemap, sepmap,
|
||||
skip + 1, columns + 1, &d )) == 0;
|
||||
skip + 1, columns + 1, &d, fail )) == 0;
|
||||
columns++ )
|
||||
;
|
||||
(void) fsetpos( fp, &pos );
|
||||
|
@ -306,7 +310,7 @@ read_csv( FILE *fp, VipsImage *out,
|
|||
int colno = x + 1;
|
||||
|
||||
ch = read_double( fp, whitemap, sepmap,
|
||||
lineno, colno, &d );
|
||||
lineno, colno, &d, fail );
|
||||
if( ch == EOF ) {
|
||||
vips_error( "csv2vips",
|
||||
_( "unexpected EOF, line %d col %d" ),
|
||||
|
@ -340,13 +344,15 @@ read_csv( FILE *fp, VipsImage *out,
|
|||
|
||||
int
|
||||
vips__csv_read( const char *filename, VipsImage *out,
|
||||
int skip, int lines, const char *whitespace, const char *separator )
|
||||
int skip, int lines, const char *whitespace, const char *separator,
|
||||
gboolean fail )
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
if( !(fp = vips__file_open_read( filename, NULL, TRUE )) )
|
||||
return( -1 );
|
||||
if( read_csv( fp, out, skip, lines, whitespace, separator, TRUE ) ) {
|
||||
if( read_csv( fp, out,
|
||||
skip, lines, whitespace, separator, TRUE, fail ) ) {
|
||||
fclose( fp );
|
||||
return( -1 );
|
||||
}
|
||||
|
@ -357,13 +363,15 @@ vips__csv_read( const char *filename, VipsImage *out,
|
|||
|
||||
int
|
||||
vips__csv_read_header( const char *filename, VipsImage *out,
|
||||
int skip, int lines, const char *whitespace, const char *separator )
|
||||
int skip, int lines, const char *whitespace, const char *separator,
|
||||
gboolean fail )
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
if( !(fp = vips__file_open_read( filename, NULL, TRUE )) )
|
||||
return( -1 );
|
||||
if( read_csv( fp, out, skip, lines, whitespace, separator, FALSE ) ) {
|
||||
if( read_csv( fp, out,
|
||||
skip, lines, whitespace, separator, FALSE, fail ) ) {
|
||||
fclose( fp );
|
||||
return( -1 );
|
||||
}
|
||||
|
@ -486,7 +494,7 @@ fetch_nonwhite( FILE *fp, const char whitemap[256], char *buf, int max )
|
|||
|
||||
/* Read a single double in ascii (not locale) encoding.
|
||||
*
|
||||
* Return the char that caused failure on fail (EOF or \n).
|
||||
* Return the char that caused failure on fail (EOF or \n).
|
||||
*/
|
||||
static int
|
||||
read_ascii_double( FILE *fp, const char whitemap[256], double *out )
|
||||
|
@ -522,6 +530,8 @@ read_ascii_double( FILE *fp, const char whitemap[256], double *out )
|
|||
|
||||
/* Read the header. Two numbers for width and height, and two optional
|
||||
* numbers for scale and offset.
|
||||
*
|
||||
* We can have scale and no offset, in which case we assume offset = 0.
|
||||
*/
|
||||
static int
|
||||
vips__matrix_header( char *whitemap, FILE *fp,
|
||||
|
@ -536,11 +546,15 @@ vips__matrix_header( char *whitemap, FILE *fp,
|
|||
(ch = read_ascii_double( fp, whitemap, &header[i] )) == 0;
|
||||
i++ )
|
||||
;
|
||||
|
||||
if( i < 4 )
|
||||
header[3] = 0.0;
|
||||
if( i < 3 )
|
||||
header[2] = 1.0;
|
||||
if( i < 2 ) {
|
||||
vips_error( "mask2vips", "%s", _( "no width / height" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( VIPS_FLOOR( header[0] ) != header[0] ||
|
||||
VIPS_FLOOR( header[1] ) != header[1] ) {
|
||||
vips_error( "mask2vips", "%s", _( "width / height not int" ) );
|
||||
|
@ -556,22 +570,17 @@ vips__matrix_header( char *whitemap, FILE *fp,
|
|||
"%s", _( "width / height out of range" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( i == 3 ) {
|
||||
vips_error( "mask2vips", "%s", _( "bad scale / offset" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( (ch = read_ascii_double( fp, whitemap, &d )) != '\n' ) {
|
||||
vips_error( "mask2vips", "%s", _( "extra chars in header" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( i > 2 &&
|
||||
header[2] == 0.0 ) {
|
||||
if( header[2] == 0.0 ) {
|
||||
vips_error( "mask2vips", "%s", _( "zero scale" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
*scale = i > 2 ? header[2] : 1.0;
|
||||
*offset = i > 2 ? header[3] : 0.0;
|
||||
*scale = header[2];
|
||||
*offset = header[3];
|
||||
|
||||
skip_line( fp );
|
||||
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
/* common defs for csv read/write
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Copyright (C) 1991-2005 The National Gallery
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifndef VIPS_CSV_H
|
||||
#define VIPS_CSV_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
extern const char *vips__foreign_csv_suffs[];
|
||||
|
||||
int vips__csv_read( const char *filename, VipsImage *out,
|
||||
int skip, int lines, const char *whitespace, const char *separator );
|
||||
int vips__csv_read_header( const char *filename, VipsImage *out,
|
||||
int skip, int lines, const char *whitespace, const char *separator );
|
||||
|
||||
int vips__csv_write( VipsImage *in, const char *filename,
|
||||
const char *separator );
|
||||
|
||||
int vips__matrix_read_header( const char *filename,
|
||||
int *width, int *height, double *scale, double *offset );
|
||||
int vips__matrix_ismatrix( const char *filename );
|
||||
VipsImage *vips__matrix_read_file( FILE *fp );
|
||||
VipsImage *vips__matrix_read( const char *filename );
|
||||
int vips__matrix_write( VipsImage *in, const char *filename );
|
||||
int vips__matrix_write_file( VipsImage *in, FILE *fp );
|
||||
|
||||
extern const char *vips__foreign_matrix_suffs[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
#endif /*VIPS_CSV_H*/
|
|
@ -48,7 +48,7 @@
|
|||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "csv.h"
|
||||
#include "pforeign.h"
|
||||
|
||||
typedef struct _VipsForeignLoadCsv {
|
||||
VipsForeignLoad parent_object;
|
||||
|
@ -89,7 +89,8 @@ vips_foreign_load_csv_header( VipsForeignLoad *load )
|
|||
VipsForeignLoadCsv *csv = (VipsForeignLoadCsv *) load;
|
||||
|
||||
if( vips__csv_read_header( csv->filename, load->out,
|
||||
csv->skip, csv->lines, csv->whitespace, csv->separator ) )
|
||||
csv->skip, csv->lines, csv->whitespace, csv->separator,
|
||||
load->fail ) )
|
||||
return( -1 );
|
||||
|
||||
VIPS_SETSTR( load->out->filename, csv->filename );
|
||||
|
@ -103,7 +104,8 @@ vips_foreign_load_csv_load( VipsForeignLoad *load )
|
|||
VipsForeignLoadCsv *csv = (VipsForeignLoadCsv *) load;
|
||||
|
||||
if( vips__csv_read( csv->filename, load->real,
|
||||
csv->skip, csv->lines, csv->whitespace, csv->separator ) )
|
||||
csv->skip, csv->lines, csv->whitespace, csv->separator,
|
||||
load->fail ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
|
@ -191,6 +193,7 @@ vips_foreign_load_csv_init( VipsForeignLoadCsv *csv )
|
|||
* * @lines: read this many lines from file
|
||||
* * @whitespace: set of whitespace characters
|
||||
* * @separator: set of separator characters
|
||||
* * @fail: %gboolean, fail on warnings
|
||||
*
|
||||
* Load a CSV (comma-separated values) file. The output image is always 1
|
||||
* band (monochrome), #VIPS_FORMAT_DOUBLE. Use vips_bandfold() to turn
|
||||
|
@ -218,6 +221,8 @@ vips_foreign_load_csv_init( VipsForeignLoadCsv *csv )
|
|||
* @separator sets the characters that separate fields.
|
||||
* Default ;,<emphasis>tab</emphasis>. Separators are never run together.
|
||||
*
|
||||
* Setting @fail to %TRUE makes the reader fail on any warnings.
|
||||
*
|
||||
* See also: vips_image_new_from_file(), vips_bandfold().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#include "csv.h"
|
||||
#include "pforeign.h"
|
||||
|
||||
typedef struct _VipsForeignSaveCsv {
|
||||
VipsForeignSave parent_object;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +0,0 @@
|
|||
void shrink_region_uncoded( VipsRegion *from, VipsRegion *to,
|
||||
VipsRect *target );
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -23,6 +23,9 @@
|
|||
* - redo as a set of fns ready for wrapping in a new-style class
|
||||
* 23/6/13
|
||||
* - fix ushort save with values >32k, thanks weaverwb
|
||||
* 4/1/17
|
||||
* - load to equivalent data type, not raw image data type ... improves
|
||||
* support for BSCALE / BZERO settings
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -73,7 +76,7 @@
|
|||
|
||||
#include <fitsio.h>
|
||||
|
||||
#include "fits.h"
|
||||
#include "pforeign.h"
|
||||
|
||||
/*
|
||||
|
||||
|
@ -119,7 +122,7 @@ typedef struct {
|
|||
VipsPel *buffer;
|
||||
} VipsFits;
|
||||
|
||||
const char *vips__fits_suffs[] = { ".fits", NULL };
|
||||
const char *vips__fits_suffs[] = { ".fits", ".fit", ".fts", NULL };
|
||||
|
||||
static void
|
||||
vips_fits_error( int status )
|
||||
|
@ -219,10 +222,21 @@ vips_fits_get_header( VipsFits *fits, VipsImage *out )
|
|||
return( -1 );
|
||||
}
|
||||
|
||||
/* cfitsio does automatic conversion from the format stored in
|
||||
* the file to the equivalent type after scale/offset. We need
|
||||
* to allocate a vips image of the equivalent type, not the original
|
||||
* type.
|
||||
*/
|
||||
if( fits_get_img_equivtype( fits->fptr, &bitpix, &status ) ) {
|
||||
vips_fits_error( status );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
#ifdef VIPS_DEBUG
|
||||
VIPS_DEBUG_MSG( "naxis = %d\n", fits->naxis );
|
||||
for( i = 0; i < fits->naxis; i++ )
|
||||
VIPS_DEBUG_MSG( "%d) %lld\n", i, fits->naxes[i] );
|
||||
VIPS_DEBUG_MSG( "fits2vips: bitpix = %d\n", bitpix );
|
||||
#endif /*VIPS_DEBUG*/
|
||||
|
||||
height = 1;
|
||||
|
@ -266,8 +280,8 @@ vips_fits_get_header( VipsFits *fits, VipsImage *out )
|
|||
if( fits->band_select != -1 )
|
||||
bands = 1;
|
||||
|
||||
/* Get image format. We want the 'raw' format of the image, our caller
|
||||
* can convert using the meta info if they want.
|
||||
/* Get image format. This is the equivalent format, or the format
|
||||
* stored in the file.
|
||||
*/
|
||||
for( i = 0; i < VIPS_NUMBER( fits2vips_formats ); i++ )
|
||||
if( fits2vips_formats[i][0] == bitpix )
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/* defs for fits read/write
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Copyright (C) 1991-2005 The National Gallery
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifndef VIPS_FITS_H
|
||||
#define VIPS_FITS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
extern const char *vips__fits_suffs[];
|
||||
|
||||
int vips__fits_isfits( const char *filename );
|
||||
int vips__fits_read_header( const char *filename, VipsImage *out );
|
||||
int vips__fits_read( const char *filename, VipsImage *out );
|
||||
|
||||
int vips__fits_write( VipsImage *in, const char *filename );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
#endif /*VIPS_FITS_H*/
|
|
@ -40,8 +40,6 @@
|
|||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#ifdef HAVE_CFITSIO
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -50,7 +48,9 @@
|
|||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "fits.h"
|
||||
#ifdef HAVE_CFITSIO
|
||||
|
||||
#include "pforeign.h"
|
||||
|
||||
typedef struct _VipsForeignLoadFits {
|
||||
VipsForeignLoad parent_object;
|
||||
|
@ -133,3 +133,36 @@ vips_foreign_load_fits_init( VipsForeignLoadFits *fits )
|
|||
}
|
||||
|
||||
#endif /*HAVE_CFITSIO*/
|
||||
|
||||
/**
|
||||
* vips_fitsload:
|
||||
* @filename: file to load
|
||||
* @out: decompressed image
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Read a FITS image file into a VIPS image.
|
||||
*
|
||||
* This operation can read images with up to three dimensions. Any higher
|
||||
* dimensions must be empty.
|
||||
*
|
||||
* It can read 8, 16 and 32-bit integer images, signed and unsigned, float and
|
||||
* double.
|
||||
*
|
||||
* FITS metadata is attached with the "fits-" prefix.
|
||||
*
|
||||
* See also: vips_image_new_from_file().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_fitsload( const char *filename, VipsImage **out, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, out );
|
||||
result = vips_call_split( "fitsload", ap, filename, out );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
|
|
@ -43,15 +43,15 @@
|
|||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#ifdef HAVE_CFITSIO
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#include "fits.h"
|
||||
#ifdef HAVE_CFITSIO
|
||||
|
||||
#include "pforeign.h"
|
||||
|
||||
typedef struct _VipsForeignSaveFits {
|
||||
VipsForeignSave parent_object;
|
||||
|
@ -148,3 +148,28 @@ vips_foreign_save_fits_init( VipsForeignSaveFits *fits )
|
|||
}
|
||||
|
||||
#endif /*HAVE_CFITSIO*/
|
||||
|
||||
/**
|
||||
* vips_fitssave:
|
||||
* @in: image to save
|
||||
* @filename: file to write to
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Write a VIPS image to a file in FITS format.
|
||||
*
|
||||
* See also: vips_image_write_to_file().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_fitssave( VipsImage *in, const char *filename, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, filename );
|
||||
result = vips_call_split( "fitssave", ap, in, filename );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,6 +4,15 @@
|
|||
* - from svgload.c
|
||||
* 25/4/16
|
||||
* - add giflib5 support
|
||||
* 26/7/16
|
||||
* - transparency was wrong if there was no EXTENSION_RECORD
|
||||
* - write 1, 2, 3, or 4 bands depending on file contents
|
||||
* 17/8/16
|
||||
* - support unicode on win
|
||||
* 19/8/16
|
||||
* - better transparency detection, thanks diegocsandrim
|
||||
* 25/11/16
|
||||
* - support @n, page-height
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -42,8 +51,6 @@
|
|||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#ifdef HAVE_GIFLIB
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -55,10 +62,12 @@
|
|||
#include <vips/internal.h>
|
||||
#include <vips/debug.h>
|
||||
|
||||
#ifdef HAVE_GIFLIB
|
||||
|
||||
#include <gif_lib.h>
|
||||
|
||||
/* giflib 5 is rather different :-( functions have error returns and there's
|
||||
* not LastError function.
|
||||
* no LastError().
|
||||
*
|
||||
* GIFLIB_MAJOR was introduced in 4.1.6. Use it to test for giflib 5.x.
|
||||
*/
|
||||
|
@ -75,8 +84,20 @@ typedef struct _VipsForeignLoadGif {
|
|||
*/
|
||||
int page;
|
||||
|
||||
/* Load this many pages.
|
||||
*/
|
||||
int n;
|
||||
|
||||
GifFileType *file;
|
||||
|
||||
/* The current read position, in pages.
|
||||
*/
|
||||
int current_page;
|
||||
|
||||
/* Set for EOF detected.
|
||||
*/
|
||||
gboolean eof;
|
||||
|
||||
/* As we scan the file, the index of the transparent pixel for this
|
||||
* frame.
|
||||
*/
|
||||
|
@ -86,6 +107,18 @@ typedef struct _VipsForeignLoadGif {
|
|||
*/
|
||||
GifPixelType *line;
|
||||
|
||||
/* We decompress the whole thing to a huge RGBA memory image, and
|
||||
* as we render, watch for bands and transparency. At the end of
|
||||
* loading, we copy 1 or 3 bands, with or without transparency to
|
||||
* output.
|
||||
*/
|
||||
gboolean has_transparency;
|
||||
gboolean has_colour;
|
||||
|
||||
/* The FILE* we read from.
|
||||
*/
|
||||
FILE *fp;
|
||||
|
||||
} VipsForeignLoadGif;
|
||||
|
||||
typedef VipsForeignLoadClass VipsForeignLoadGifClass;
|
||||
|
@ -153,15 +186,33 @@ vips_foreign_load_gif_errstr( int error_code )
|
|||
#endif /*HAVE_GIFLIB_5*/
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_gif_error_vips( VipsForeignLoadGif *gif, int error )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
|
||||
|
||||
const char *message;
|
||||
|
||||
if( (message = vips_foreign_load_gif_errstr( error )) )
|
||||
vips_error( class->nickname, "%s", message );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_gif_error( VipsForeignLoadGif *gif )
|
||||
{
|
||||
int error;
|
||||
|
||||
error = 0;
|
||||
|
||||
#ifdef HAVE_GIFLIB_5
|
||||
if( gif->file )
|
||||
vips_foreign_load_gif_errstr( gif->file->Error );
|
||||
error = gif->file->Error;
|
||||
#else
|
||||
vips_foreign_load_gif_errstr( GifLastError() );
|
||||
error = GifLastError();
|
||||
#endif
|
||||
|
||||
if( error )
|
||||
vips_foreign_load_gif_error_vips( gif, error );
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -171,36 +222,56 @@ vips_foreign_load_gif_close( VipsForeignLoadGif *gif )
|
|||
if( gif->file ) {
|
||||
int error;
|
||||
|
||||
if( DGifCloseFile( gif->file, &error ) )
|
||||
vips_foreign_load_gif_errstr( error );
|
||||
if( DGifCloseFile( gif->file, &error ) == GIF_ERROR )
|
||||
vips_foreign_load_gif_error_vips( gif, error );
|
||||
gif->file = NULL;
|
||||
}
|
||||
#else
|
||||
if( gif->file ) {
|
||||
if( DGifCloseFile( gif->file ) )
|
||||
vips_foreign_load_gif_errstr( GifLastError() );
|
||||
if( DGifCloseFile( gif->file ) == GIF_ERROR )
|
||||
vips_foreign_load_gif_error_vips( gif, GifLastError() );
|
||||
gif->file = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
VIPS_FREEF( fclose, gif->fp );
|
||||
}
|
||||
|
||||
/* Our input function for file open. We can't use DGifOpenFileName(), since
|
||||
* that just calls open() and won't work with unicode on win32. We can't use
|
||||
* DGifOpenFileHandle() since that's an fd from open() and you can't pass those
|
||||
* acoss DLL boundaries on Windows.
|
||||
*/
|
||||
static int
|
||||
vips_giflib_file_read( GifFileType *file, GifByteType *buffer, int n )
|
||||
{
|
||||
FILE *fp = (FILE *) file->UserData;
|
||||
|
||||
return( (int) fread( (void *) buffer, 1, n, fp ) );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_gif_open( VipsForeignLoadGif *gif, const char *filename )
|
||||
{
|
||||
g_assert( !gif->file );
|
||||
g_assert( !gif->fp );
|
||||
|
||||
if( !(gif->fp = vips__file_open_read( filename, NULL, FALSE )) )
|
||||
return( -1 );
|
||||
|
||||
#ifdef HAVE_GIFLIB_5
|
||||
{
|
||||
int error;
|
||||
|
||||
if( !(gif->file = DGifOpenFileName( filename, &error )) ) {
|
||||
vips_foreign_load_gif_errstr( error );
|
||||
if( !(gif->file =
|
||||
DGifOpen( gif->fp, vips_giflib_file_read, &error )) ) {
|
||||
vips_foreign_load_gif_error_vips( gif, error );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
#else
|
||||
if( !(gif->file = DGifOpenFileName( filename )) ) {
|
||||
vips_foreign_load_gif_errstr( GifLastError() );
|
||||
if( !(gif->file = DGifOpen( gif->fp, vips_giflib_file_read )) ) {
|
||||
vips_foreign_load_gif_error_vips( gif, GifLastError() );
|
||||
return( -1 );
|
||||
}
|
||||
#endif
|
||||
|
@ -218,13 +289,13 @@ vips_foreign_load_gif_open_buffer( VipsForeignLoadGif *gif, InputFunc read_fn )
|
|||
int error;
|
||||
|
||||
if( !(gif->file = DGifOpen( gif, read_fn, &error )) ) {
|
||||
vips_foreign_load_gif_errstr( error );
|
||||
vips_foreign_load_gif_error_vips( gif, error );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
#else
|
||||
if( !(gif->file = DGifOpen( gif, read_fn )) ) {
|
||||
vips_foreign_load_gif_errstr( GifLastError() );
|
||||
vips_foreign_load_gif_error_vips( gif, GifLastError() );
|
||||
return( -1 );
|
||||
}
|
||||
#endif
|
||||
|
@ -284,35 +355,6 @@ vips_foreign_load_gif_is_a( const char *filename )
|
|||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_gif_parse( VipsForeignLoadGif *gif,
|
||||
VipsImage *out )
|
||||
{
|
||||
vips_image_init_fields( out,
|
||||
gif->file->SWidth, gif->file->SHeight,
|
||||
4, VIPS_FORMAT_UCHAR,
|
||||
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
|
||||
|
||||
/* We will have the whole GIF frame in memory, so we can render any
|
||||
* area.
|
||||
*/
|
||||
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_ANY, NULL );
|
||||
|
||||
/* We need a line buffer to decompress to.
|
||||
*/
|
||||
gif->line = VIPS_ARRAY( gif, gif->file->SWidth, GifPixelType );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_gif_header( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
|
||||
|
||||
vips_foreign_load_gif_parse( gif, load->out );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_gif_render_line( VipsForeignLoadGif *gif,
|
||||
int width, VipsPel * restrict q, VipsPel * restrict p )
|
||||
|
@ -325,8 +367,12 @@ vips_foreign_load_gif_render_line( VipsForeignLoadGif *gif,
|
|||
for( x = 0; x < width; x++ ) {
|
||||
VipsPel v = p[x];
|
||||
|
||||
if( v != gif->transparency &&
|
||||
v < map->ColorCount ) {
|
||||
if( v >= map->ColorCount ) {
|
||||
g_warning( "%s", _( "pixel value out of range" ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( v != gif->transparency ) {
|
||||
q[0] = map->Colors[v].Red;
|
||||
q[1] = map->Colors[v].Green;
|
||||
q[2] = map->Colors[v].Blue;
|
||||
|
@ -352,6 +398,8 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif, VipsImage *out )
|
|||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
|
||||
GifFileType *file = gif->file;
|
||||
ColorMapObject *map = file->Image.ColorMap ?
|
||||
file->Image.ColorMap : file->SColorMap;
|
||||
|
||||
/* Check that the frame lies within our image.
|
||||
*/
|
||||
|
@ -364,10 +412,34 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif, VipsImage *out )
|
|||
return( -1 );
|
||||
}
|
||||
|
||||
/* Check if we have a non-greyscale colourmap for this frame.
|
||||
*/
|
||||
if( !gif->has_colour ) {
|
||||
int i;
|
||||
|
||||
for( i = 0; i < map->ColorCount; i++ )
|
||||
if( map->Colors[i].Red != map->Colors[i].Green ||
|
||||
map->Colors[i].Green != map->Colors[i].Blue ) {
|
||||
VIPS_DEBUG_MSG( "gifload: not mono\n" );
|
||||
gif->has_colour = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We need a line buffer to decompress to.
|
||||
*/
|
||||
if( !gif->line )
|
||||
if( !(gif->line = VIPS_ARRAY( gif,
|
||||
gif->file->SWidth, GifPixelType )) )
|
||||
return( -1 );
|
||||
|
||||
if( file->Image.Interlace ) {
|
||||
int i;
|
||||
|
||||
VIPS_DEBUG_MSG( "gifload: interlaced frame\n" );
|
||||
VIPS_DEBUG_MSG( "gifload: interlaced frame of "
|
||||
"%d x %d pixels at %d x %d\n",
|
||||
file->Image.Width, file->Image.Height,
|
||||
file->Image.Left, file->Image.Top );
|
||||
|
||||
for( i = 0; i < 4; i++ ) {
|
||||
int y;
|
||||
|
@ -392,6 +464,11 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif, VipsImage *out )
|
|||
else {
|
||||
int y;
|
||||
|
||||
VIPS_DEBUG_MSG( "gifload: non-interlaced frame of "
|
||||
"%d x %d pixels at %d x %d\n",
|
||||
file->Image.Width, file->Image.Height,
|
||||
file->Image.Left, file->Image.Top );
|
||||
|
||||
for( y = 0; y < file->Image.Height; y++ ) {
|
||||
VipsPel *q = VIPS_IMAGE_ADDR( out,
|
||||
file->Image.Left, file->Image.Top + y );
|
||||
|
@ -410,32 +487,22 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif, VipsImage *out )
|
|||
return( 0 );
|
||||
}
|
||||
|
||||
/* Write the next page, if there is one, to @page. Set EOF if we hit the end of
|
||||
* the file. @page must be a memory image of the right size.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_load_gif_load( VipsForeignLoad *load )
|
||||
vips_foreign_load_gif_page( VipsForeignLoadGif *gif, VipsImage *out )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
|
||||
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
|
||||
|
||||
int frame_n;
|
||||
GifRecordType record;
|
||||
int n_pages;
|
||||
|
||||
vips_foreign_load_gif_parse( gif, load->real );
|
||||
n_pages = 0;
|
||||
|
||||
/* Turn out into a memory image which we then render the GIF frames
|
||||
* into.
|
||||
*/
|
||||
if( vips_image_write_prepare( load->real ) )
|
||||
return( -1 );
|
||||
|
||||
/* Scan the GIF until we have enough to have completely rendered the
|
||||
* frame we need.
|
||||
*/
|
||||
frame_n = 0;
|
||||
do {
|
||||
GifByteType *extension;
|
||||
int ext_code;
|
||||
|
||||
if( DGifGetRecordType( gif->file, &record) == GIF_ERROR ) {
|
||||
if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) {
|
||||
vips_foreign_load_gif_error( gif );
|
||||
return( -1 );
|
||||
}
|
||||
|
@ -449,12 +516,13 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
|
|||
return( -1 );
|
||||
}
|
||||
|
||||
if( vips_foreign_load_gif_render( gif, load->real ) )
|
||||
if( vips_foreign_load_gif_render( gif, out ) )
|
||||
return( -1 );
|
||||
|
||||
frame_n += 1;
|
||||
n_pages += 1;
|
||||
|
||||
VIPS_DEBUG_MSG( "gifload: start frame %d:\n", frame_n );
|
||||
VIPS_DEBUG_MSG( "gifload: page %d:\n",
|
||||
gif->current_page + n_pages );
|
||||
|
||||
break;
|
||||
|
||||
|
@ -464,7 +532,7 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
|
|||
gif->transparency = -1;
|
||||
|
||||
if( DGifGetExtension( gif->file,
|
||||
&ext_code, &extension) == GIF_ERROR ) {
|
||||
&ext_code, &extension ) == GIF_ERROR ) {
|
||||
vips_foreign_load_gif_error( gif );
|
||||
return( -1 );
|
||||
}
|
||||
|
@ -472,11 +540,13 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
|
|||
if( ext_code == GRAPHICS_EXT_FUNC_CODE &&
|
||||
extension &&
|
||||
extension[0] == 4 &&
|
||||
extension[1] == 1 ) {
|
||||
(extension[1] & 0x1) ) {
|
||||
/* Bytes are 4, 1, delay low, delay high,
|
||||
* transparency.
|
||||
* transparency. Bit 1 means transparency
|
||||
* is being set.
|
||||
*/
|
||||
gif->transparency = extension[4];
|
||||
gif->has_transparency = TRUE;
|
||||
|
||||
VIPS_DEBUG_MSG( "gifload: "
|
||||
"seen transparency %d\n",
|
||||
|
@ -501,6 +571,7 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
|
|||
|
||||
case TERMINATE_RECORD_TYPE:
|
||||
VIPS_DEBUG_MSG( "gifload: TERMINATE_RECORD_TYPE:\n" );
|
||||
gif->eof = TRUE;
|
||||
break;
|
||||
|
||||
case SCREEN_DESC_RECORD_TYPE:
|
||||
|
@ -514,20 +585,212 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
|
|||
default:
|
||||
break;
|
||||
}
|
||||
} while( frame_n <= gif->page &&
|
||||
record != TERMINATE_RECORD_TYPE );
|
||||
} while( n_pages < 1 &&
|
||||
!gif->eof );
|
||||
|
||||
if( frame_n <= gif->page ) {
|
||||
gif->current_page += n_pages;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static VipsImage *
|
||||
vips_foreign_load_gif_new_page( VipsForeignLoadGif *gif )
|
||||
{
|
||||
VipsImage *out;
|
||||
|
||||
out = vips_image_new_memory();
|
||||
|
||||
vips_image_init_fields( out,
|
||||
gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR,
|
||||
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
|
||||
|
||||
/* We will have the whole GIF frame in memory, so we can render any
|
||||
* area.
|
||||
*/
|
||||
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_ANY, NULL );
|
||||
|
||||
/* Turn out into a memory image which we then render the GIF frames
|
||||
* into.
|
||||
*/
|
||||
if( vips_image_write_prepare( out ) ) {
|
||||
g_object_unref( out );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( out );
|
||||
}
|
||||
|
||||
static void
|
||||
unref_array( GSList *list )
|
||||
{
|
||||
g_slist_free_full( list, (GDestroyNotify) g_object_unref );
|
||||
}
|
||||
|
||||
/* We render each frame to a separate memory image held in a linked
|
||||
* list, then assemble to out. We don't know the number of frames in advance,
|
||||
* so we can't just allocate a large area.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_load_gif_pages( VipsForeignLoadGif *gif, VipsImage **out )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
|
||||
|
||||
GSList *frames;
|
||||
VipsImage *frame;
|
||||
VipsImage *previous;
|
||||
VipsImage **t;
|
||||
int n_frames;
|
||||
int i;
|
||||
|
||||
frames = NULL;
|
||||
previous = NULL;
|
||||
|
||||
/* Accumulate any start stuff up to the first frame we need.
|
||||
*/
|
||||
if( !(frame = vips_foreign_load_gif_new_page( gif )) )
|
||||
return( -1 );
|
||||
do {
|
||||
if( vips_foreign_load_gif_page( gif, frame ) ) {
|
||||
g_object_unref( frame );
|
||||
return( -1 );
|
||||
}
|
||||
} while( !gif->eof &&
|
||||
gif->current_page <= gif->page );
|
||||
|
||||
if( gif->eof ) {
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "too few frames in GIF file" ) );
|
||||
g_object_unref( frame );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
frames = g_slist_append( frames, frame );
|
||||
previous = frame;
|
||||
|
||||
while( gif->n == -1 ||
|
||||
gif->current_page < gif->page + gif->n ) {
|
||||
/* We might need a frame for this read to render to.
|
||||
*/
|
||||
if( !(frame = vips_foreign_load_gif_new_page( gif )) ) {
|
||||
unref_array( frames );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* And init with the previous frame, if any.
|
||||
*/
|
||||
if( previous )
|
||||
memcpy( VIPS_IMAGE_ADDR( frame, 0, 0 ),
|
||||
VIPS_IMAGE_ADDR( previous, 0, 0 ),
|
||||
VIPS_IMAGE_SIZEOF_IMAGE( frame ) );
|
||||
|
||||
if( vips_foreign_load_gif_page( gif, frame ) ) {
|
||||
g_object_unref( frame );
|
||||
unref_array( frames );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( gif->eof ) {
|
||||
/* Nope, didn't need the new frame.
|
||||
*/
|
||||
g_object_unref( frame );
|
||||
break;
|
||||
}
|
||||
else {
|
||||
frames = g_slist_append( frames, frame );
|
||||
previous = frame;
|
||||
}
|
||||
}
|
||||
|
||||
n_frames = g_slist_length( frames );
|
||||
|
||||
if( gif->eof &&
|
||||
gif->n != -1 &&
|
||||
n_frames < gif->n ) {
|
||||
unref_array( frames );
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "too few frames in GIF file" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* We've rendered to a memory image ... we can shut down the GIF
|
||||
/* We've rendered to a set of memory images ... we can shut down the GIF
|
||||
* reader now.
|
||||
*/
|
||||
vips_foreign_load_gif_close( gif );
|
||||
|
||||
if( !(t = VIPS_ARRAY( gif, n_frames, VipsImage * )) ) {
|
||||
unref_array( frames );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
for( i = 0; i < n_frames; i++ )
|
||||
t[i] = (VipsImage *) g_slist_nth_data( frames, i );
|
||||
|
||||
if( vips_arrayjoin( t, out, n_frames,
|
||||
"across", 1,
|
||||
NULL ) ) {
|
||||
unref_array( frames );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
unref_array( frames );
|
||||
|
||||
if( n_frames > 1 )
|
||||
vips_image_set_int( *out, VIPS_META_PAGE_HEIGHT, frame->Ysize );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_gif_load( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
|
||||
VipsImage **t = (VipsImage **)
|
||||
vips_object_local_array( VIPS_OBJECT( load ), 4 );
|
||||
|
||||
VipsImage *im;
|
||||
|
||||
if( vips_foreign_load_gif_pages( gif, &t[0] ) )
|
||||
return( -1 );
|
||||
im = t[0];
|
||||
|
||||
/* Depending on what we found, transform and write to load->real.
|
||||
*/
|
||||
if( gif->has_colour &&
|
||||
gif->has_transparency ) {
|
||||
/* Nothing to do.
|
||||
*/
|
||||
}
|
||||
else if( gif->has_colour ) {
|
||||
/* RGB.
|
||||
*/
|
||||
if( vips_extract_band( im, &t[1], 0,
|
||||
"n", 3,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
im = t[1];
|
||||
}
|
||||
else if( gif->has_transparency ) {
|
||||
/* GA. Take BA so we have neighboring channels.
|
||||
*/
|
||||
if( vips_extract_band( im, &t[1], 2,
|
||||
"n", 2,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
im = t[1];
|
||||
im->Type = VIPS_INTERPRETATION_B_W;
|
||||
}
|
||||
else {
|
||||
/* G.
|
||||
*/
|
||||
if( vips_extract_band( im, &t[1], 0, NULL ) )
|
||||
return( -1 );
|
||||
im = t[1];
|
||||
im->Type = VIPS_INTERPRETATION_B_W;
|
||||
}
|
||||
|
||||
if( vips_image_write( im, load->out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
|
@ -548,7 +811,6 @@ vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class )
|
|||
load_class->get_flags_filename =
|
||||
vips_foreign_load_gif_get_flags_filename;
|
||||
load_class->get_flags = vips_foreign_load_gif_get_flags;
|
||||
load_class->load = vips_foreign_load_gif_load;
|
||||
|
||||
VIPS_ARG_INT( class, "page", 10,
|
||||
_( "Page" ),
|
||||
|
@ -557,11 +819,20 @@ vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class )
|
|||
G_STRUCT_OFFSET( VipsForeignLoadGif, page ),
|
||||
0, 100000, 0 );
|
||||
|
||||
VIPS_ARG_INT( class, "n", 6,
|
||||
_( "n" ),
|
||||
_( "Load this many pages" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadGif, n ),
|
||||
-1, 100000, 1 );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_gif_init( VipsForeignLoadGif *gif )
|
||||
{
|
||||
gif->n = 1;
|
||||
gif->transparency = -1;
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadGifFile {
|
||||
|
@ -587,7 +858,9 @@ vips_foreign_load_gif_file_header( VipsForeignLoad *load )
|
|||
if( vips_foreign_load_gif_open( gif, file->filename ) )
|
||||
return( -1 );
|
||||
|
||||
return( vips_foreign_load_gif_header( load ) );
|
||||
VIPS_SETSTR( load->out->filename, file->filename );
|
||||
|
||||
return( vips_foreign_load_gif_load( load ) );
|
||||
}
|
||||
|
||||
static const char *vips_foreign_gif_suffs[] = {
|
||||
|
@ -653,12 +926,11 @@ G_DEFINE_TYPE( VipsForeignLoadGifBuffer, vips_foreign_load_gif_buffer,
|
|||
* Read up to len bytes into buffer, return number of bytes read, 0 for EOF.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_load_gif_buffer_read( GifFileType *file,
|
||||
GifByteType *buf, int len )
|
||||
vips_giflib_buffer_read( GifFileType *file, GifByteType *buf, int n )
|
||||
{
|
||||
VipsForeignLoadGifBuffer *buffer = (VipsForeignLoadGifBuffer *)
|
||||
file->UserData;
|
||||
size_t will_read = VIPS_MIN( len, buffer->bytes_to_go );
|
||||
VipsForeignLoadGifBuffer *buffer =
|
||||
(VipsForeignLoadGifBuffer *) file->UserData;
|
||||
size_t will_read = VIPS_MIN( n, buffer->bytes_to_go );
|
||||
|
||||
memcpy( buf, buffer->p, will_read );
|
||||
buffer->p += will_read;
|
||||
|
@ -678,11 +950,10 @@ vips_foreign_load_gif_buffer_header( VipsForeignLoad *load )
|
|||
buffer->p = buffer->buf->data;
|
||||
buffer->bytes_to_go = buffer->buf->length;
|
||||
|
||||
if( vips_foreign_load_gif_open_buffer( gif,
|
||||
vips_foreign_load_gif_buffer_read ) )
|
||||
if( vips_foreign_load_gif_open_buffer( gif, vips_giflib_buffer_read ) )
|
||||
return( -1 );
|
||||
|
||||
return( vips_foreign_load_gif_header( load ) );
|
||||
return( vips_foreign_load_gif_load( load ) );
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -716,5 +987,87 @@ vips_foreign_load_gif_buffer_init( VipsForeignLoadGifBuffer *buffer )
|
|||
{
|
||||
}
|
||||
|
||||
#endif /*HAVE_RSVG*/
|
||||
#endif /*HAVE_GIFLIB*/
|
||||
|
||||
/**
|
||||
* vips_gifload:
|
||||
* @filename: file to load
|
||||
* @out: output image
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @page: %gint, page (frame) to read
|
||||
* * @n: %gint, load this many pages
|
||||
*
|
||||
* Read a GIF file into a VIPS image. Rendering uses the giflib library.
|
||||
*
|
||||
* Use @page to select a page to render, numbering from zero.
|
||||
*
|
||||
* Use @n to select the number of pages to render. The default is 1. Pages are
|
||||
* rendered in a vertical column, with each individual page aligned to the
|
||||
* left. Set to -1 to mean "until the end of the document". Use vips_grid()
|
||||
* to change page layout.
|
||||
*
|
||||
* The whole GIF is rendered into memory on header access. The output image
|
||||
* will be 1, 2, 3 or 4 bands depending on what the reader finds in the file.
|
||||
*
|
||||
* See also: vips_image_new_from_file().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_gifload( const char *filename, VipsImage **out, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, out );
|
||||
result = vips_call_split( "gifload", ap, filename, out );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_gifload_buffer:
|
||||
* @buf: memory area to load
|
||||
* @len: size of memory area
|
||||
* @out: image to write
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @page: %gint, page (frame) to read
|
||||
* * @n: %gint, load this many pages
|
||||
*
|
||||
* Read a GIF-formatted memory block into a VIPS image. Exactly as
|
||||
* vips_gifload(), but read from a memory buffer.
|
||||
*
|
||||
* You must not free the buffer while @out is active. The
|
||||
* #VipsObject::postclose signal on @out is a good place to free.
|
||||
*
|
||||
* See also: vips_gifload().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_gifload_buffer( void *buf, size_t len, VipsImage **out, ... )
|
||||
{
|
||||
va_list ap;
|
||||
VipsBlob *blob;
|
||||
int result;
|
||||
|
||||
/* We don't take a copy of the data or free it.
|
||||
*/
|
||||
blob = vips_blob_new( NULL, buf, len );
|
||||
|
||||
va_start( ap, out );
|
||||
result = vips_call_split( "gifload_buffer", ap, blob, out );
|
||||
va_end( ap );
|
||||
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,20 @@
|
|||
* 21/2/16
|
||||
* - _destroy the decompress object as soon as we can, frees loads of
|
||||
* memory for progressive jpg files
|
||||
* 26/5/16
|
||||
* - switch to new orientation tag
|
||||
* 11/7/16
|
||||
* - new --fail handling
|
||||
* 07/09/16
|
||||
* - Don't use the exif resolution if x_resolution / y_resolution /
|
||||
* resolution_unit is missing
|
||||
<<<<<<< HEAD
|
||||
* 7/11/16
|
||||
* - exif handling moved out to exif.c
|
||||
=======
|
||||
* 4/1/17
|
||||
* - Don't warn for missing exif res, since we fall back to jfif now
|
||||
>>>>>>> master
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -118,26 +132,13 @@
|
|||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#ifdef HAVE_EXIF
|
||||
#ifdef UNTAGGED_EXIF
|
||||
#include <exif-data.h>
|
||||
#include <exif-loader.h>
|
||||
#include <exif-ifd.h>
|
||||
#include <exif-utils.h>
|
||||
#else /*!UNTAGGED_EXIF*/
|
||||
#include <libexif/exif-data.h>
|
||||
#include <libexif/exif-loader.h>
|
||||
#include <libexif/exif-ifd.h>
|
||||
#include <libexif/exif-utils.h>
|
||||
#endif /*UNTAGGED_EXIF*/
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "pforeign.h"
|
||||
|
||||
#include "jpeg.h"
|
||||
#include "vipsjpeg.h"
|
||||
|
||||
/* Stuff we track during a read.
|
||||
*/
|
||||
|
@ -168,7 +169,7 @@ typedef struct _ReadJpeg {
|
|||
*/
|
||||
int y_pos;
|
||||
|
||||
/* Use Orientation exif tag to automatically rotate and flip image
|
||||
/* Use orientation tag to automatically rotate and flip image
|
||||
* during load.
|
||||
*/
|
||||
gboolean autorotate;
|
||||
|
@ -184,16 +185,9 @@ readjpeg_free( ReadJpeg *jpeg )
|
|||
result = 0;
|
||||
|
||||
if( jpeg->eman.pub.num_warnings != 0 ) {
|
||||
if( jpeg->fail ) {
|
||||
vips_error( "VipsJpeg", "%s", vips_error_buffer() );
|
||||
result = -1;
|
||||
}
|
||||
else {
|
||||
vips_warn( "VipsJpeg",
|
||||
_( "read gave %ld warnings" ),
|
||||
jpeg->eman.pub.num_warnings );
|
||||
vips_warn( NULL, "%s", vips_error_buffer() );
|
||||
}
|
||||
g_warning( _( "read gave %ld warnings" ),
|
||||
jpeg->eman.pub.num_warnings );
|
||||
g_warning( "%s", vips_error_buffer() );
|
||||
|
||||
/* Make the message only appear once.
|
||||
*/
|
||||
|
@ -275,400 +269,6 @@ readjpeg_file( ReadJpeg *jpeg, const char *filename )
|
|||
return( 0 );
|
||||
}
|
||||
|
||||
#ifdef HAVE_EXIF
|
||||
#ifdef DEBUG_VERBOSE
|
||||
/* Print exif for debugging ... hacked from exif-0.6.9/actions.c
|
||||
*/
|
||||
static void
|
||||
show_tags( ExifData *data )
|
||||
{
|
||||
int i;
|
||||
unsigned int tag;
|
||||
const char *name;
|
||||
|
||||
printf( "show EXIF tags:\n" );
|
||||
|
||||
for( i = 0; i < EXIF_IFD_COUNT; i++ )
|
||||
printf( "%-7.7s", exif_ifd_get_name( i ) );
|
||||
printf( "\n" );
|
||||
|
||||
for( tag = 0; tag < 0xffff; tag++ ) {
|
||||
name = exif_tag_get_title( tag );
|
||||
if( !name )
|
||||
continue;
|
||||
printf( " 0x%04x %-29.29s", tag, name );
|
||||
for( i = 0; i < EXIF_IFD_COUNT; i++ )
|
||||
if( exif_content_get_entry( data->ifd[i], tag ) )
|
||||
printf( " * " );
|
||||
else
|
||||
printf( " - " );
|
||||
printf( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
show_entry( ExifEntry *entry, void *client )
|
||||
{
|
||||
char exif_text[256];
|
||||
|
||||
printf( "%s", exif_tag_get_title( entry->tag ) );
|
||||
printf( "|" );
|
||||
printf( "%s", exif_entry_get_value( entry, exif_text, 256 ) );
|
||||
printf( "|" );
|
||||
printf( "%s", exif_format_get_name( entry->format ) );
|
||||
printf( "|" );
|
||||
printf( "%d bytes", entry->size );
|
||||
printf( "\n" );
|
||||
}
|
||||
|
||||
static void
|
||||
show_ifd( ExifContent *content, void *client )
|
||||
{
|
||||
int *ifd = (int *) client;
|
||||
|
||||
printf( "- ifd %d\n", *ifd );
|
||||
exif_content_foreach_entry( content, show_entry, client );
|
||||
|
||||
*ifd += 1;
|
||||
}
|
||||
|
||||
void
|
||||
show_values( ExifData *data )
|
||||
{
|
||||
ExifByteOrder order;
|
||||
int ifd;
|
||||
|
||||
order = exif_data_get_byte_order( data );
|
||||
printf( "EXIF tags in '%s' byte order\n",
|
||||
exif_byte_order_get_name( order ) );
|
||||
|
||||
printf( "Title|Value|Format|Size\n" );
|
||||
|
||||
ifd = 0;
|
||||
exif_data_foreach_content( data, show_ifd, &ifd );
|
||||
|
||||
if( data->size )
|
||||
printf( "contains thumbnail of %d bytes\n", data->size );
|
||||
}
|
||||
#endif /*DEBUG_VERBOSE*/
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
#ifdef HAVE_EXIF
|
||||
static int
|
||||
vips_exif_get_int( ExifData *ed,
|
||||
ExifEntry *entry, unsigned long component, int *out )
|
||||
{
|
||||
ExifByteOrder bo = exif_data_get_byte_order( ed );
|
||||
size_t sizeof_component = entry->size / entry->components;
|
||||
size_t offset = component * sizeof_component;
|
||||
|
||||
if( entry->format == EXIF_FORMAT_SHORT )
|
||||
*out = exif_get_short( entry->data + offset, bo );
|
||||
else if( entry->format == EXIF_FORMAT_SSHORT )
|
||||
*out = exif_get_sshort( entry->data + offset, bo );
|
||||
else if( entry->format == EXIF_FORMAT_LONG )
|
||||
/* This won't work for huge values, but who cares.
|
||||
*/
|
||||
*out = (int) exif_get_long( entry->data + offset, bo );
|
||||
else if( entry->format == EXIF_FORMAT_SLONG )
|
||||
*out = exif_get_slong( entry->data + offset, bo );
|
||||
else
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_exif_get_rational( ExifData *ed,
|
||||
ExifEntry *entry, unsigned long component, ExifRational *out )
|
||||
{
|
||||
if( entry->format == EXIF_FORMAT_RATIONAL ) {
|
||||
ExifByteOrder bo = exif_data_get_byte_order( ed );
|
||||
size_t sizeof_component = entry->size / entry->components;
|
||||
size_t offset = component * sizeof_component;
|
||||
|
||||
*out = exif_get_rational( entry->data + offset, bo );
|
||||
}
|
||||
else
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_exif_get_srational( ExifData *ed,
|
||||
ExifEntry *entry, unsigned long component, ExifSRational *out )
|
||||
{
|
||||
if( entry->format == EXIF_FORMAT_SRATIONAL ) {
|
||||
ExifByteOrder bo = exif_data_get_byte_order( ed );
|
||||
size_t sizeof_component = entry->size / entry->components;
|
||||
size_t offset = component * sizeof_component;
|
||||
|
||||
*out = exif_get_srational( entry->data + offset, bo );
|
||||
}
|
||||
else
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_exif_get_double( ExifData *ed,
|
||||
ExifEntry *entry, unsigned long component, double *out )
|
||||
{
|
||||
ExifRational rv;
|
||||
ExifSRational srv;
|
||||
|
||||
if( !vips_exif_get_rational( ed, entry, component, &rv ) )
|
||||
*out = (double) rv.numerator / rv.denominator;
|
||||
else if( !vips_exif_get_srational( ed, entry, component, &srv ) )
|
||||
*out = (double) srv.numerator / srv.denominator;
|
||||
else
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Save an exif value to a string in a way that we can restore. We only bother
|
||||
* for the simple formats (that a client might try to change) though.
|
||||
*
|
||||
* Keep in sync with vips_exif_from_s() in vips2jpeg.
|
||||
*/
|
||||
static void
|
||||
vips_exif_to_s( ExifData *ed, ExifEntry *entry, VipsBuf *buf )
|
||||
{
|
||||
unsigned long i;
|
||||
int iv;
|
||||
ExifRational rv;
|
||||
ExifSRational srv;
|
||||
char txt[256];
|
||||
|
||||
if( entry->format == EXIF_FORMAT_ASCII ) {
|
||||
/* libexif does not null-terminate strings. Copy out and add
|
||||
* the \0 ourselves.
|
||||
*/
|
||||
int len = VIPS_MIN( 254, entry->size );
|
||||
|
||||
memcpy( txt, entry->data, len );
|
||||
txt[len] = '\0';
|
||||
vips_buf_appendf( buf, "%s ", txt );
|
||||
}
|
||||
else if( entry->components < 10 &&
|
||||
!vips_exif_get_int( ed, entry, 0, &iv ) ) {
|
||||
for( i = 0; i < entry->components; i++ ) {
|
||||
vips_exif_get_int( ed, entry, i, &iv );
|
||||
vips_buf_appendf( buf, "%d ", iv );
|
||||
}
|
||||
}
|
||||
else if( entry->components < 10 &&
|
||||
!vips_exif_get_rational( ed, entry, 0, &rv ) ) {
|
||||
for( i = 0; i < entry->components; i++ ) {
|
||||
vips_exif_get_rational( ed, entry, i, &rv );
|
||||
vips_buf_appendf( buf, "%u/%u ",
|
||||
rv.numerator, rv.denominator );
|
||||
}
|
||||
}
|
||||
else if( entry->components < 10 &&
|
||||
!vips_exif_get_srational( ed, entry, 0, &srv ) ) {
|
||||
for( i = 0; i < entry->components; i++ ) {
|
||||
vips_exif_get_srational( ed, entry, i, &srv );
|
||||
vips_buf_appendf( buf, "%d/%d ",
|
||||
srv.numerator, srv.denominator );
|
||||
}
|
||||
}
|
||||
else
|
||||
vips_buf_appendf( buf, "%s ",
|
||||
exif_entry_get_value( entry, txt, 256 ) );
|
||||
|
||||
vips_buf_appendf( buf, "(%s, %s, %lu components, %d bytes)",
|
||||
exif_entry_get_value( entry, txt, 256 ),
|
||||
exif_format_get_name( entry->format ),
|
||||
entry->components,
|
||||
entry->size );
|
||||
}
|
||||
|
||||
typedef struct _VipsExif {
|
||||
VipsImage *image;
|
||||
ExifData *ed;
|
||||
} VipsExif;
|
||||
|
||||
static void
|
||||
attach_exif_entry( ExifEntry *entry, VipsExif *ve )
|
||||
{
|
||||
const char *tag_name;
|
||||
char vips_name_txt[256];
|
||||
VipsBuf vips_name = VIPS_BUF_STATIC( vips_name_txt );
|
||||
char value_txt[256];
|
||||
VipsBuf value = VIPS_BUF_STATIC( value_txt );
|
||||
|
||||
if( !(tag_name = exif_tag_get_name( entry->tag )) )
|
||||
return;
|
||||
|
||||
vips_buf_appendf( &vips_name, "exif-ifd%d-%s",
|
||||
exif_entry_get_ifd( entry ), tag_name );
|
||||
vips_exif_to_s( ve->ed, entry, &value );
|
||||
|
||||
/* Can't do anything sensible with the error return.
|
||||
*/
|
||||
(void) vips_image_set_string( ve->image,
|
||||
vips_buf_all( &vips_name ), vips_buf_all( &value ) );
|
||||
}
|
||||
|
||||
static void
|
||||
attach_exif_content( ExifContent *content, VipsExif *ve )
|
||||
{
|
||||
exif_content_foreach_entry( content,
|
||||
(ExifContentForeachEntryFunc) attach_exif_entry, ve );
|
||||
}
|
||||
|
||||
static int
|
||||
get_entry_double( ExifData *ed, int ifd, ExifTag tag, double *out )
|
||||
{
|
||||
ExifEntry *entry;
|
||||
|
||||
if( !(entry = exif_content_get_entry( ed->ifd[ifd], tag )) ||
|
||||
entry->components != 1 )
|
||||
return( -1 );
|
||||
|
||||
return( vips_exif_get_double( ed, entry, 0, out ) );
|
||||
}
|
||||
|
||||
static int
|
||||
get_entry_int( ExifData *ed, int ifd, ExifTag tag, int *out )
|
||||
{
|
||||
ExifEntry *entry;
|
||||
|
||||
if( !(entry = exif_content_get_entry( ed->ifd[ifd], tag )) ||
|
||||
entry->components != 1 )
|
||||
return( -1 );
|
||||
|
||||
return( vips_exif_get_int( ed, entry, 0, out ) );
|
||||
}
|
||||
|
||||
static void
|
||||
res_from_exif( VipsImage *im, ExifData *ed )
|
||||
{
|
||||
double xres, yres;
|
||||
int unit;
|
||||
|
||||
/* The main image xres/yres are in ifd0. ifd1 has xres/yres of the
|
||||
* image thumbnail, if any.
|
||||
*/
|
||||
if( get_entry_double( ed, 0, EXIF_TAG_X_RESOLUTION, &xres ) ||
|
||||
get_entry_double( ed, 0, EXIF_TAG_Y_RESOLUTION, &yres ) ||
|
||||
get_entry_int( ed, 0, EXIF_TAG_RESOLUTION_UNIT, &unit ) ) {
|
||||
vips_warn( "VipsJpeg",
|
||||
"%s", _( "error reading resolution" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "res_from_exif: seen exif tags "
|
||||
"xres = %g, yres = %g, unit = %d\n", xres, yres, unit );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
switch( unit ) {
|
||||
case 1:
|
||||
/* No unit ... just pass the fields straight to vips.
|
||||
*/
|
||||
vips_image_set_string( im,
|
||||
VIPS_META_RESOLUTION_UNIT, "none" );
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* In inches.
|
||||
*/
|
||||
xres /= 25.4;
|
||||
yres /= 25.4;
|
||||
vips_image_set_string( im,
|
||||
VIPS_META_RESOLUTION_UNIT, "in" );
|
||||
break;
|
||||
|
||||
case 3:
|
||||
/* In cm.
|
||||
*/
|
||||
xres /= 10.0;
|
||||
yres /= 10.0;
|
||||
vips_image_set_string( im,
|
||||
VIPS_META_RESOLUTION_UNIT, "cm" );
|
||||
break;
|
||||
|
||||
default:
|
||||
vips_warn( "VipsJpeg",
|
||||
"%s", _( "unknown EXIF resolution unit" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "res_from_exif: seen exif resolution %g, %g p/mm\n",
|
||||
xres, yres );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
im->Xres = xres;
|
||||
im->Yres = yres;
|
||||
}
|
||||
|
||||
static int
|
||||
attach_thumbnail( VipsImage *im, ExifData *ed )
|
||||
{
|
||||
if( ed->size > 0 ) {
|
||||
char *thumb_copy;
|
||||
|
||||
thumb_copy = g_malloc( ed->size );
|
||||
memcpy( thumb_copy, ed->data, ed->size );
|
||||
|
||||
vips_image_set_blob( im, "jpeg-thumbnail-data",
|
||||
(VipsCallbackFn) g_free, thumb_copy, ed->size );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
static int
|
||||
parse_exif( VipsImage *im, void *data, int data_length )
|
||||
{
|
||||
#ifdef HAVE_EXIF
|
||||
{
|
||||
ExifData *ed;
|
||||
VipsExif ve;
|
||||
|
||||
if( !(ed = exif_data_new_from_data( data, data_length )) )
|
||||
return( -1 );
|
||||
|
||||
#ifdef DEBUG_VERBOSE
|
||||
show_tags( ed );
|
||||
show_values( ed );
|
||||
#endif /*DEBUG_VERBOSE*/
|
||||
|
||||
/* Attach informational fields for what we find.
|
||||
|
||||
FIXME ... better to have this in the UI layer?
|
||||
|
||||
Or we could attach non-human-readable tags here (int,
|
||||
double etc) and then move the human stuff to the UI
|
||||
layer?
|
||||
|
||||
*/
|
||||
ve.image = im;
|
||||
ve.ed = ed;
|
||||
exif_data_foreach_content( ed,
|
||||
(ExifDataForeachContentFunc) attach_exif_content, &ve );
|
||||
|
||||
/* Look for resolution fields and use them to set the VIPS
|
||||
* xres/yres fields.
|
||||
*/
|
||||
res_from_exif( im, ed );
|
||||
|
||||
attach_thumbnail( im, ed );
|
||||
|
||||
exif_data_free( ed );
|
||||
}
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
attach_blob( VipsImage *im, const char *field, void *data, int data_length )
|
||||
{
|
||||
|
@ -785,8 +385,7 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out )
|
|||
break;
|
||||
|
||||
default:
|
||||
vips_warn( "VipsJpeg",
|
||||
"%s", _( "unknown JFIF resolution unit" ) );
|
||||
g_warning( "%s", _( "unknown JFIF resolution unit" ) );
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -833,13 +432,10 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out )
|
|||
/* Possible EXIF or XMP data.
|
||||
*/
|
||||
if( p->data_length > 4 &&
|
||||
vips_isprefix( "Exif", (char *) p->data ) ) {
|
||||
if( parse_exif( out,
|
||||
p->data, p->data_length ) ||
|
||||
attach_blob( out, VIPS_META_EXIF_NAME,
|
||||
p->data, p->data_length ) )
|
||||
vips_isprefix( "Exif", (char *) p->data ) &&
|
||||
attach_blob( out, VIPS_META_EXIF_NAME,
|
||||
p->data, p->data_length ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( p->data_length > 4 &&
|
||||
vips_isprefix( "http", (char *) p->data ) &&
|
||||
|
@ -916,6 +512,9 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out )
|
|||
(VipsCallbackFn) vips_free, data, data_length );
|
||||
}
|
||||
|
||||
if( vips__exif_parse( out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
|
@ -957,15 +556,36 @@ read_jpeg_generate( VipsRegion *or,
|
|||
* a vips_sequential().
|
||||
*/
|
||||
if( r->top != jpeg->y_pos ) {
|
||||
VIPS_GATE_STOP( "read_jpeg_generate: work" );
|
||||
vips_error( "VipsJpeg",
|
||||
_( "out of order read at line %d" ), jpeg->y_pos );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Here for longjmp() from vips__new_error_exit().
|
||||
*/
|
||||
if( setjmp( jpeg->eman.jmp ) )
|
||||
if( setjmp( jpeg->eman.jmp ) ) {
|
||||
VIPS_GATE_STOP( "read_jpeg_generate: work" );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* If --fail is set, we make read fail on any warnings. This will stop
|
||||
* on any errors from the previous jpeg_read_scanlines().
|
||||
*/
|
||||
if( jpeg->eman.pub.num_warnings > 0 &&
|
||||
jpeg->fail ) {
|
||||
VIPS_GATE_STOP( "read_jpeg_generate: work" );
|
||||
|
||||
/* Only fail once.
|
||||
*/
|
||||
jpeg->eman.pub.num_warnings = 0;
|
||||
|
||||
*stop = TRUE;
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
for( y = 0; y < r->height; y++ ) {
|
||||
JSAMPROW row_pointer[1];
|
||||
|
@ -996,8 +616,6 @@ read_jpeg_generate( VipsRegion *or,
|
|||
return( 0 );
|
||||
}
|
||||
|
||||
#define ORIENTATION ("exif-ifd0-Orientation")
|
||||
|
||||
/* Auto-rotate, if rotate_image is set.
|
||||
*/
|
||||
static VipsImage *
|
||||
|
@ -1021,7 +639,8 @@ read_jpeg_rotate( VipsObject *process, VipsImage *im )
|
|||
vips_rot( t[0], &t[1], angle, NULL ) )
|
||||
return( NULL );
|
||||
im = t[1];
|
||||
(void) vips_image_remove( im, ORIENTATION );
|
||||
|
||||
vips_autorot_remove_angle( im );
|
||||
}
|
||||
|
||||
return( im );
|
||||
|
@ -1090,7 +709,7 @@ vips__jpeg_read( ReadJpeg *jpeg, VipsImage *out, gboolean header_only )
|
|||
{
|
||||
int i;
|
||||
|
||||
/* Handy for debubgging ... spot any extra markers.
|
||||
/* Handy for debugging ... spot any extra markers.
|
||||
*/
|
||||
for( i = 0; i < 16; i++ )
|
||||
jpeg_save_markers( &jpeg->cinfo, JPEG_APP0 + i, 0xffff );
|
||||
|
@ -1114,7 +733,7 @@ vips__jpeg_read( ReadJpeg *jpeg, VipsImage *out, gboolean header_only )
|
|||
|
||||
/* We won't be returning an orientation tag.
|
||||
*/
|
||||
(void) vips_image_remove( out, ORIENTATION );
|
||||
vips_autorot_remove_angle( out );
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -1151,6 +770,8 @@ vips__jpeg_read_file( const char *filename, VipsImage *out,
|
|||
if( vips__jpeg_read( jpeg, out, header_only ) )
|
||||
return( -1 );
|
||||
|
||||
VIPS_SETSTR( out->filename, filename );
|
||||
|
||||
/* We can kill off the decompress early if this is just a header read.
|
||||
* This saves an fd during read.
|
||||
*/
|
||||
|
|
|
@ -43,13 +43,19 @@
|
|||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#ifdef HAVE_JPEG
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "pforeign.h"
|
||||
|
||||
#ifdef HAVE_JPEG
|
||||
|
||||
#ifdef HAVE_EXIF
|
||||
#ifdef UNTAGGED_EXIF
|
||||
#include <exif-data.h>
|
||||
|
@ -64,12 +70,6 @@
|
|||
#endif /*UNTAGGED_EXIF*/
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "vipsjpeg.h"
|
||||
|
||||
typedef struct _VipsForeignLoadJpeg {
|
||||
VipsForeignLoad parent_object;
|
||||
|
||||
|
@ -77,10 +77,6 @@ typedef struct _VipsForeignLoadJpeg {
|
|||
*/
|
||||
int shrink;
|
||||
|
||||
/* Fail on first warning.
|
||||
*/
|
||||
gboolean fail;
|
||||
|
||||
/* Autorotate using exif orientation tag.
|
||||
*/
|
||||
gboolean autorotate;
|
||||
|
@ -144,19 +140,13 @@ vips_foreign_load_jpeg_class_init( VipsForeignLoadJpegClass *class )
|
|||
G_STRUCT_OFFSET( VipsForeignLoadJpeg, shrink ),
|
||||
1, 16, 1 );
|
||||
|
||||
VIPS_ARG_BOOL( class, "fail", 11,
|
||||
_( "Fail" ),
|
||||
_( "Fail on first warning" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadJpeg, fail ),
|
||||
FALSE );
|
||||
|
||||
VIPS_ARG_BOOL( class, "autorotate", 12,
|
||||
_( "Autorotate" ),
|
||||
_( "Rotate image using exif orientation" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadJpeg, autorotate ),
|
||||
FALSE );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -200,11 +190,9 @@ vips_foreign_load_jpeg_file_header( VipsForeignLoad *load )
|
|||
VipsForeignLoadJpegFile *file = (VipsForeignLoadJpegFile *) load;
|
||||
|
||||
if( vips__jpeg_read_file( file->filename, load->out,
|
||||
TRUE, jpeg->shrink, jpeg->fail, FALSE, jpeg->autorotate ) )
|
||||
TRUE, jpeg->shrink, load->fail, FALSE, jpeg->autorotate ) )
|
||||
return( -1 );
|
||||
|
||||
VIPS_SETSTR( load->out->filename, file->filename );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
|
@ -215,7 +203,7 @@ vips_foreign_load_jpeg_file_load( VipsForeignLoad *load )
|
|||
VipsForeignLoadJpegFile *file = (VipsForeignLoadJpegFile *) load;
|
||||
|
||||
if( vips__jpeg_read_file( file->filename, load->real,
|
||||
FALSE, jpeg->shrink, jpeg->fail,
|
||||
FALSE, jpeg->shrink, load->fail,
|
||||
load->access == VIPS_ACCESS_SEQUENTIAL, jpeg->autorotate ) )
|
||||
return( -1 );
|
||||
|
||||
|
@ -284,7 +272,7 @@ vips_foreign_load_jpeg_buffer_header( VipsForeignLoad *load )
|
|||
VipsForeignLoadJpegBuffer *buffer = (VipsForeignLoadJpegBuffer *) load;
|
||||
|
||||
if( vips__jpeg_read_buffer( buffer->buf->data, buffer->buf->length,
|
||||
load->out, TRUE, jpeg->shrink, jpeg->fail, FALSE,
|
||||
load->out, TRUE, jpeg->shrink, load->fail, FALSE,
|
||||
jpeg->autorotate ) )
|
||||
return( -1 );
|
||||
|
||||
|
@ -298,7 +286,7 @@ vips_foreign_load_jpeg_buffer_load( VipsForeignLoad *load )
|
|||
VipsForeignLoadJpegBuffer *buffer = (VipsForeignLoadJpegBuffer *) load;
|
||||
|
||||
if( vips__jpeg_read_buffer( buffer->buf->data, buffer->buf->length,
|
||||
load->real, FALSE, jpeg->shrink, jpeg->fail,
|
||||
load->real, FALSE, jpeg->shrink, load->fail,
|
||||
load->access == VIPS_ACCESS_SEQUENTIAL, jpeg->autorotate ) )
|
||||
return( -1 );
|
||||
|
||||
|
@ -343,3 +331,124 @@ vips_foreign_load_jpeg_buffer_init( VipsForeignLoadJpegBuffer *buffer )
|
|||
}
|
||||
|
||||
#endif /*HAVE_JPEG*/
|
||||
|
||||
/**
|
||||
* vips_jpegload:
|
||||
* @filename: file to load
|
||||
* @out: decompressed image
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @shrink: %gint, shrink by this much on load
|
||||
* * @fail: %gboolean, fail on warnings
|
||||
* * @autorotate: %gboolean, use exif Orientation tag to rotate the image
|
||||
* during load
|
||||
*
|
||||
* Read a JPEG file into a VIPS image. It can read most 8-bit JPEG images,
|
||||
* including CMYK and YCbCr.
|
||||
*
|
||||
* @shrink means shrink by this integer factor during load. Possible values
|
||||
* are 1, 2, 4 and 8. Shrinking during read is very much faster than
|
||||
* decompressing the whole image and then shrinking later.
|
||||
*
|
||||
* Setting @fail to %TRUE makes the JPEG reader fail on any warnings.
|
||||
* This can be useful for detecting truncated files, for example. Normally
|
||||
* reading these produces a warning, but no fatal error.
|
||||
*
|
||||
* Setting @autorotate to %TRUE will make the loader interpret the
|
||||
* orientation tag and automatically rotate the image appropriately during
|
||||
* load.
|
||||
*
|
||||
* If @autorotate is %FALSE, the metadata field #VIPS_META_ORIENTATION is set
|
||||
* to the value of the orientation tag. Applications may read and interpret
|
||||
* this field
|
||||
* as they wish later in processing. See vips_autorot(). Save
|
||||
* operations will use #VIPS_META_ORIENTATION, if present, to set the
|
||||
* orientation of output images.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* |[
|
||||
* vips_jpegload( "fred.jpg", &out,
|
||||
* "shrink", 8,
|
||||
* "fail", TRUE,
|
||||
* NULL );
|
||||
* ]|
|
||||
*
|
||||
* Any embedded ICC profiles are ignored: you always just get the RGB from
|
||||
* the file. Instead, the embedded profile will be attached to the image as
|
||||
* #VIPS_META_ICC_NAME. You need to use something like
|
||||
* vips_icc_import() to get CIE values from the file.
|
||||
*
|
||||
* EXIF metadata is attached as #VIPS_META_EXIF_NAME, IPCT as
|
||||
* #VIPS_META_IPCT_NAME, and XMP as #VIPS_META_XMP_NAME.
|
||||
*
|
||||
* The int metadata item "jpeg-multiscan" is set to the result of
|
||||
* jpeg_has_multiple_scans(). Interlaced jpeg images need a large amount of
|
||||
* memory to load, so this field gives callers a chance to handle these
|
||||
* images differently.
|
||||
*
|
||||
* The EXIF thumbnail, if present, is attached to the image as
|
||||
* "jpeg-thumbnail-data". See vips_image_get_blob().
|
||||
*
|
||||
* See also: vips_jpegload_buffer(), vips_image_new_from_file(), vips_autorot().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_jpegload( const char *filename, VipsImage **out, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, out );
|
||||
result = vips_call_split( "jpegload", ap, filename, out );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_jpegload_buffer:
|
||||
* @buf: memory area to load
|
||||
* @len: size of memory area
|
||||
* @out: image to write
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @shrink: %gint, shrink by this much on load
|
||||
* * @fail: %gboolean, fail on warnings
|
||||
* * @autorotate: %gboolean, use exif Orientation tag to rotate the image
|
||||
* during load
|
||||
*
|
||||
* Read a JPEG-formatted memory block into a VIPS image. Exactly as
|
||||
* vips_jpegload(), but read from a memory buffer.
|
||||
*
|
||||
* You must not free the buffer while @out is active. The
|
||||
* #VipsObject::postclose signal on @out is a good place to free.
|
||||
*
|
||||
* See also: vips_jpegload().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_jpegload_buffer( void *buf, size_t len, VipsImage **out, ... )
|
||||
{
|
||||
va_list ap;
|
||||
VipsBlob *blob;
|
||||
int result;
|
||||
|
||||
/* We don't take a copy of the data or free it.
|
||||
*/
|
||||
blob = vips_blob_new( NULL, buf, len );
|
||||
|
||||
va_start( ap, out );
|
||||
result = vips_call_split( "jpegload_buffer", ap, blob, out );
|
||||
va_end( ap );
|
||||
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
|
|
@ -41,13 +41,19 @@
|
|||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#ifdef HAVE_JPEG
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "pforeign.h"
|
||||
|
||||
#ifdef HAVE_JPEG
|
||||
|
||||
#ifdef HAVE_EXIF
|
||||
#ifdef UNTAGGED_EXIF
|
||||
#include <exif-data.h>
|
||||
|
@ -62,12 +68,6 @@
|
|||
#endif /*UNTAGGED_EXIF*/
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "vipsjpeg.h"
|
||||
|
||||
typedef struct _VipsForeignSaveJpeg {
|
||||
VipsForeignSave parent_object;
|
||||
|
||||
|
@ -95,7 +95,8 @@ typedef struct _VipsForeignSaveJpeg {
|
|||
*/
|
||||
gboolean trellis_quant;
|
||||
|
||||
/* Apply overshooting to samples with extreme values e.g. 0 & 255 for 8-bit.
|
||||
/* Apply overshooting to samples with extreme values e.g. 0 & 255
|
||||
* for 8-bit.
|
||||
*/
|
||||
gboolean overshoot_deringing;
|
||||
|
||||
|
@ -103,6 +104,10 @@ typedef struct _VipsForeignSaveJpeg {
|
|||
*/
|
||||
gboolean optimize_scans;
|
||||
|
||||
/* Use predefined quantization table with given index.
|
||||
*/
|
||||
int quant_table;
|
||||
|
||||
} VipsForeignSaveJpeg;
|
||||
|
||||
typedef VipsForeignSaveClass VipsForeignSaveJpegClass;
|
||||
|
@ -135,6 +140,8 @@ vips_foreign_save_jpeg_class_init( VipsForeignSaveJpegClass *class )
|
|||
|
||||
foreign_class->suffs = vips__jpeg_suffs;
|
||||
|
||||
/* See also vips_foreign_save_tiff_build() when saving JPEG in TIFF.
|
||||
*/
|
||||
save_class->saveable = VIPS_SAVEABLE_RGB_CMYK;
|
||||
save_class->format_table = bandfmt_jpeg;
|
||||
|
||||
|
@ -194,6 +201,13 @@ vips_foreign_save_jpeg_class_init( VipsForeignSaveJpegClass *class )
|
|||
G_STRUCT_OFFSET( VipsForeignSaveJpeg, optimize_scans ),
|
||||
FALSE );
|
||||
|
||||
VIPS_ARG_INT( class, "quant_table", 18,
|
||||
_( "Quantization table" ),
|
||||
_( "Use predefined quantization table with given index" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveJpeg, quant_table ),
|
||||
0, 8, 0 );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -230,7 +244,8 @@ vips_foreign_save_jpeg_file_build( VipsObject *object )
|
|||
if( vips__jpeg_write_file( save->ready, file->filename,
|
||||
jpeg->Q, jpeg->profile, jpeg->optimize_coding,
|
||||
jpeg->interlace, save->strip, jpeg->no_subsample,
|
||||
jpeg->trellis_quant, jpeg->overshoot_deringing, jpeg->optimize_scans) )
|
||||
jpeg->trellis_quant, jpeg->overshoot_deringing,
|
||||
jpeg->optimize_scans, jpeg->quant_table ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
|
@ -294,10 +309,13 @@ vips_foreign_save_jpeg_buffer_build( VipsObject *object )
|
|||
if( vips__jpeg_write_buffer( save->ready,
|
||||
&obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding,
|
||||
jpeg->interlace, save->strip, jpeg->no_subsample,
|
||||
jpeg->trellis_quant, jpeg->overshoot_deringing, jpeg->optimize_scans) )
|
||||
jpeg->trellis_quant, jpeg->overshoot_deringing,
|
||||
jpeg->optimize_scans, jpeg->quant_table ) )
|
||||
return( -1 );
|
||||
|
||||
blob = vips_blob_new( (VipsCallbackFn) vips_free, obuf, olen );
|
||||
/* obuf is a g_free() buffer, not vips_free().
|
||||
*/
|
||||
blob = vips_blob_new( (VipsCallbackFn) g_free, obuf, olen );
|
||||
g_object_set( file, "buffer", blob, NULL );
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
|
||||
|
@ -357,7 +375,8 @@ vips_foreign_save_jpeg_mime_build( VipsObject *object )
|
|||
if( vips__jpeg_write_buffer( save->ready,
|
||||
&obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding,
|
||||
jpeg->interlace, save->strip, jpeg->no_subsample,
|
||||
jpeg->trellis_quant, jpeg->overshoot_deringing, jpeg->optimize_scans) )
|
||||
jpeg->trellis_quant, jpeg->overshoot_deringing,
|
||||
jpeg->optimize_scans, jpeg->quant_table ) )
|
||||
return( -1 );
|
||||
|
||||
printf( "Content-length: %zu\r\n", olen );
|
||||
|
@ -391,3 +410,212 @@ vips_foreign_save_jpeg_mime_init( VipsForeignSaveJpegMime *mime )
|
|||
}
|
||||
|
||||
#endif /*HAVE_JPEG*/
|
||||
|
||||
/**
|
||||
* vips_jpegsave:
|
||||
* @in: image to save
|
||||
* @filename: file to write to
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @Q: %gint, quality factor
|
||||
* * @profile: filename of ICC profile to attach
|
||||
* * @optimize_coding: %gboolean, compute optimal Huffman coding tables
|
||||
* * @interlace: %gboolean, write an interlaced (progressive) jpeg
|
||||
* * @strip: %gboolean, remove all metadata from image
|
||||
* * @no_subsample: %gboolean, disable chroma subsampling
|
||||
* * @trellis_quant: %gboolean, apply trellis quantisation to each 8x8 block
|
||||
* * @overshoot_deringing: %gboolean, overshoot samples with extreme values
|
||||
* * @optimize_scans: %gboolean, split DCT coefficients into separate scans
|
||||
* * @quant_table: %gint, quantization table index
|
||||
*
|
||||
* Write a VIPS image to a file as JPEG.
|
||||
*
|
||||
* Use @Q to set the JPEG compression factor. Default 75.
|
||||
*
|
||||
* Use @profile to give the filename of a profile to be embedded in the JPEG.
|
||||
* This does not affect the pixels which are written, just the way
|
||||
* they are tagged. You can use the special string "none" to mean
|
||||
* "don't attach a profile".
|
||||
*
|
||||
* If no profile is specified and the VIPS header
|
||||
* contains an ICC profile named #VIPS_META_ICC_NAME, the
|
||||
* profile from the VIPS header will be attached.
|
||||
*
|
||||
* If @optimize_coding is set, the Huffman tables are optimised. This is
|
||||
* sllightly slower and produces slightly smaller files.
|
||||
*
|
||||
* If @interlace is set, the jpeg files will be interlaced (progressive jpeg,
|
||||
* in jpg parlance). These files may be better for display over a slow network
|
||||
* conection, but need much more memory to encode and decode.
|
||||
*
|
||||
* If @strip is set, no EXIF data, IPCT data, ICC profile or XMP metadata is
|
||||
* written into the output file.
|
||||
*
|
||||
* If @no_subsample is set, chrominance subsampling is disabled. This will
|
||||
* improve quality at the cost of larger file size. Useful for high Q factors.
|
||||
*
|
||||
* If @trellis_quant is set and the version of libjpeg supports it
|
||||
* (e.g. mozjpeg >= 3.0), apply trellis quantisation to each 8x8 block.
|
||||
* Reduces file size but increases compression time.
|
||||
*
|
||||
* If @overshoot_deringing is set and the version of libjpeg supports it
|
||||
* (e.g. mozjpeg >= 3.0), apply overshooting to samples with extreme values
|
||||
* for example 0 and 255 for 8-bit. Overshooting may reduce ringing artifacts
|
||||
* from compression, in particular in areas where black text appears on a
|
||||
* white background.
|
||||
*
|
||||
* If @optimize_scans is set and the version of libjpeg supports it
|
||||
* (e.g. mozjpeg >= 3.0), split the spectrum of DCT coefficients into
|
||||
* separate scans. Reduces file size but increases compression time.
|
||||
*
|
||||
* If @quant_table is set and the version of libjpeg supports it
|
||||
* (e.g. mozjpeg >= 3.0) it selects the quantization table to use:
|
||||
*
|
||||
* * 0 — Tables from JPEG Annex K (vips and libjpeg default)
|
||||
* * 1 — Flat table
|
||||
* * 2 — Table tuned for MSSIM on Kodak image set
|
||||
* * 3 — Table from ImageMagick by N. Robidoux (current mozjpeg default)
|
||||
* * 4 — Table tuned for PSNR-HVS-M on Kodak image set
|
||||
* * 5 — Table from Relevance of Human Vision to JPEG-DCT Compression (1992)
|
||||
* * 6 — Table from DCTune Perceptual Optimization of Compressed Dental
|
||||
* X-Rays (1997)
|
||||
* * 7 — Table from A Visual Detection Model for DCT Coefficient
|
||||
* Quantization (1993)
|
||||
* * 8 — Table from An Improved Detection Model for DCT Coefficient
|
||||
* Quantization (1993)
|
||||
*
|
||||
* Quantization table 0 is the default in vips and libjpeg(-turbo), but it
|
||||
* tends to favor detail over color accuracy, producting colored patches and
|
||||
* stripes as well as heavy banding in flat areas at high compression ratios.
|
||||
* Quantization table 2 is a good candidate to try if the default quantization
|
||||
* table produces banding or color shifts and is well suited for hires images.
|
||||
* Quantization table 3 is the default in mozjpeg and has been tuned to produce
|
||||
* good results at the default quality setting; banding at high compression.
|
||||
* Quantization table 4 is the most accurate at the cost of compression ratio.
|
||||
* Tables 5-7 are based on older research papers, but generally achieve worse
|
||||
* compression ratios and/or quality than 2 or 4.
|
||||
*
|
||||
* The image is automatically converted to RGB, Monochrome or CMYK before
|
||||
* saving.
|
||||
*
|
||||
* EXIF data is constructed from #VIPS_META_EXIF_NAME, then
|
||||
* modified with any other related tags on the image before being written to
|
||||
* the file. #VIPS_META_RESOLUTION_UNIT is used to set the EXIF resolution
|
||||
* unit. #VIPS_META_ORIENTATION is used to set the EXIF orientation tag.
|
||||
*
|
||||
* IPCT as #VIPS_META_IPCT_NAME and XMP as #VIPS_META_XMP_NAME
|
||||
* are coded and attached.
|
||||
*
|
||||
* See also: vips_jpegsave_buffer(), vips_image_write_to_file().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_jpegsave( VipsImage *in, const char *filename, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, filename );
|
||||
result = vips_call_split( "jpegsave", ap, in, filename );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_jpegsave_buffer:
|
||||
* @in: image to save
|
||||
* @buf: return output buffer here
|
||||
* @len: return output length here
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @Q: %gint, quality factor
|
||||
* * @profile: filename of ICC profile to attach
|
||||
* * @optimize_coding: %gboolean, compute optimal Huffman coding tables
|
||||
* * @interlace: %gboolean, write an interlaced (progressive) jpeg
|
||||
* * @strip: %gboolean, remove all metadata from image
|
||||
* * @no_subsample: %gboolean, disable chroma subsampling
|
||||
* * @trellis_quant: %gboolean, apply trellis quantisation to each 8x8 block
|
||||
* * @overshoot_deringing: %gboolean, overshoot samples with extreme values
|
||||
* * @optimize_scans: %gboolean, split DCT coefficients into separate scans
|
||||
* * @quant_table: %gint, quantization table index
|
||||
*
|
||||
* As vips_jpegsave(), but save to a memory buffer.
|
||||
*
|
||||
* The address of the buffer is returned in @obuf, the length of the buffer in
|
||||
* @olen. You are responsible for freeing the buffer with g_free() when you
|
||||
* are done with it.
|
||||
*
|
||||
* See also: vips_jpegsave(), vips_image_write_to_file().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_jpegsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
{
|
||||
va_list ap;
|
||||
VipsArea *area;
|
||||
int result;
|
||||
|
||||
area = NULL;
|
||||
|
||||
va_start( ap, len );
|
||||
result = vips_call_split( "jpegsave_buffer", ap, in, &area );
|
||||
va_end( ap );
|
||||
|
||||
if( !result &&
|
||||
area ) {
|
||||
if( buf ) {
|
||||
*buf = area->data;
|
||||
area->free_fn = NULL;
|
||||
}
|
||||
if( len )
|
||||
*len = area->length;
|
||||
|
||||
vips_area_unref( area );
|
||||
}
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_jpegsave_mime:
|
||||
* @in: image to save
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @Q: %gint, quality factor
|
||||
* * @profile: filename of ICC profile to attach
|
||||
* * @optimize_coding: %gboolean, compute optimal Huffman coding tables
|
||||
* * @interlace: %gboolean, write an interlaced (progressive) jpeg
|
||||
* * @strip: %gboolean, remove all metadata from image
|
||||
* * @no_subsample: %gboolean, disable chroma subsampling
|
||||
* * @trellis_quant: %gboolean, apply trellis quantisation to each 8x8 block
|
||||
* * @overshoot_deringing: %gboolean, overshoot samples with extreme values
|
||||
* * @optimize_scans: %gboolean, split DCT coefficients into separate scans
|
||||
* * @quant_table: %gint, quantization table index
|
||||
*
|
||||
* As vips_jpegsave(), but save as a mime jpeg on stdout.
|
||||
*
|
||||
* See also: vips_jpegsave(), vips_image_write_to_file().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_jpegsave_mime( VipsImage *in, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, in );
|
||||
result = vips_call_split( "jpegsave_mime", ap, in );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
/* common defs for libMagick read/write
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Copyright (C) 1991-2005 The National Gallery
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifndef VIPS_MAGICK_H
|
||||
#define VIPS_MAGICK_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
int vips__magick_read( const char *filename,
|
||||
VipsImage *out, gboolean all_frames, const char *density, int page );
|
||||
int vips__magick_read_header( const char *filename,
|
||||
VipsImage *out, gboolean all_frames, const char *density, int page );
|
||||
|
||||
int vips__magick_read_buffer( const void *buf, const size_t len,
|
||||
VipsImage *out, gboolean all_frames, const char *density, int page );
|
||||
int vips__magick_read_buffer_header( const void *buf, const size_t len,
|
||||
VipsImage *out, gboolean all_frames, const char *density, int page );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
#endif /*VIPS_MAGICK_H*/
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue