Merge branch 'master' into invalidate-on-load-error

This commit is contained in:
John Cupitt 2017-01-17 13:47:14 +00:00
commit c464503510
291 changed files with 25199 additions and 15780 deletions

5
.gitattributes vendored Normal file
View File

@ -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

15
.gitignore vendored
View File

@ -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

View File

@ -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
View File

@ -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

View File

@ -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) \

View File

@ -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
View File

@ -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?

View File

@ -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

View File

@ -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 $*

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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")

View File

@ -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 );
};

View File

@ -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")

View File

@ -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 );

View File

@ -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;

View File

@ -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>

View File

@ -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">

View File

@ -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)

View File

@ -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 );
}

View File

@ -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,

View File

@ -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;

View File

@ -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,

View File

@ -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 */

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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" );

View File

@ -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,

View File

@ -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.
*

View File

@ -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

View File

@ -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
*/

View File

@ -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 );

View File

@ -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 */

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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.

View File

@ -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.
*

View File

@ -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 ) )

View File

@ -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.

View File

@ -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.
*

View File

@ -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
*/

View File

@ -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
*/

View File

@ -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 );

View File

@ -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

View File

@ -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.
*

View File

@ -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;

View File

@ -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@

View File

@ -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

1360
libvips/convolution/conva.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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 );
}

412
libvips/convolution/convf.c Normal file
View File

@ -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 );
}

1064
libvips/convolution/convi.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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();

View File

@ -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 );

View File

@ -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 );
}

View File

@ -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

View File

@ -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

View File

@ -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 );
}

View File

@ -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" );

View File

@ -1,6 +1,8 @@
noinst_LTLIBRARIES = libcreate.la
libcreate_la_SOURCES = \
perlin.c \
worley.c \
create.c \
pcreate.h \
gaussmat.c \

View File

@ -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
*/

View File

@ -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();
}

388
libvips/create/perlin.c Normal file
View File

@ -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 );
}

View File

@ -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

390
libvips/create/worley.c Normal file
View File

@ -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 );
}

View File

@ -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 );

View File

@ -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 )

View File

@ -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 )

View File

@ -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" ) );

View File

@ -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 )

View File

@ -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

View File

@ -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 )

View File

@ -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 );
}

View File

@ -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 )
{

View File

@ -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

View File

@ -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.

View File

@ -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*/

View File

@ -50,7 +50,7 @@
#ifdef HAVE_ANALYZE
#include "analyze2vips.h"
#include "pforeign.h"
typedef struct _VipsForeignLoadAnalyze {
VipsForeignLoad parent_object;

66
libvips/foreign/cairo.c Normal file
View File

@ -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;
}
}
}

View File

@ -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 );

View File

@ -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*/

View File

@ -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.

View File

@ -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

View File

@ -1,3 +0,0 @@
void shrink_region_uncoded( VipsRegion *from, VipsRegion *to,
VipsRect *target );

1129
libvips/foreign/exif.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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 )

View File

@ -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*/

View File

@ -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 );
}

View File

@ -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

View File

@ -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 );
}

View File

@ -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.
*/

View File

@ -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", &amp;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 );
}

View File

@ -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 );
}

View File

@ -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