Merge branch 'master' into improve-detection-of-bad-profiles
This commit is contained in:
commit
8cddba44ab
12
.github/ISSUE_TEMPLATE/bug_report.md
vendored
12
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,12 +1,20 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create A Bug Report
|
||||
title: 'A concise title of what the bug is.'
|
||||
labels: 'bug'
|
||||
title: A concise title of what the bug is.
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Questions, enhancements, tips, etc.
|
||||
|
||||
Please use libvips discussions for other topics.
|
||||
|
||||
https://github.com/libvips/libvips/discussions
|
||||
|
||||
## Bug report
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
|
6
.github/ISSUE_TEMPLATE/config.yaml
vendored
Normal file
6
.github/ISSUE_TEMPLATE/config.yaml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
blank_issues_enabled: false
|
||||
|
||||
contact_links:
|
||||
- name: libvips discussions
|
||||
url: https://github.com/libvips/libvips/discussions
|
||||
about: Please ask questions and suggest improvements here.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Enhancement or Feature Request
|
||||
about: Suggest an addition to libvips
|
||||
title: ''
|
||||
labels: 'enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
11
.github/ISSUE_TEMPLATE/question.md
vendored
11
.github/ISSUE_TEMPLATE/question.md
vendored
@ -1,11 +0,0 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask a Question
|
||||
title: ''
|
||||
labels: 'question'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Ask a question...
|
||||
|
144
.github/workflows/ci.yml
vendored
Normal file
144
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
name: CI
|
||||
|
||||
on: [ push, pull_request, workflow_dispatch ]
|
||||
|
||||
jobs:
|
||||
CI:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: ${{ contains(matrix.os, 'macos') }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
name: [ "Linux x64 (Ubuntu 20.04) - GCC 10" ]
|
||||
os: [ ubuntu-20.04 ]
|
||||
sanitize: [ false ]
|
||||
build:
|
||||
- { cc: gcc, cxx: g++, linker: ld }
|
||||
include:
|
||||
- name: "Linux x64 (Ubuntu 20.04) - Clang 10 with ASan and UBSan"
|
||||
os: ubuntu-20.04
|
||||
sanitize: true
|
||||
build: { cc: clang-10, cxx: clang++-10, linker: ld.lld-10 }
|
||||
- name: "macOS (10.15) - Xcode 12.3"
|
||||
os: macos-10.15
|
||||
sanitize: false
|
||||
build: { cc: clang, cxx: clang++, linker: ld.lld }
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Update apt
|
||||
if: contains(matrix.os, 'ubuntu')
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
run: sudo -E apt-get update -qq -o Acquire::Retries=3
|
||||
|
||||
- name: Add libheif PPA
|
||||
if: contains(matrix.os, 'ubuntu')
|
||||
run: |
|
||||
sudo add-apt-repository ppa:strukturag/libde265
|
||||
sudo add-apt-repository ppa:strukturag/libheif
|
||||
|
||||
- name: Install Ubuntu dependencies
|
||||
if: contains(matrix.os, 'ubuntu')
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
run:
|
||||
sudo -E apt-get install --fix-missing -qq -o Acquire::Retries=3
|
||||
gtk-doc-tools gobject-introspection
|
||||
python3-pip python3-setuptools python3-wheel
|
||||
libfftw3-dev libexif-dev libjpeg-turbo8-dev
|
||||
libpng-dev libwebp-dev libtiff5-dev
|
||||
libheif-dev libexpat1-dev libcfitsio-dev
|
||||
libmatio-dev libnifti-dev liborc-0.4-dev
|
||||
liblcms2-dev libpoppler-glib-dev librsvg2-dev
|
||||
libgif-dev libopenexr-dev libpango1.0-dev
|
||||
libgsf-1-dev libopenslide-dev libffi-dev
|
||||
|
||||
- name: Install macOS dependencies
|
||||
if: contains(matrix.os, 'macos')
|
||||
run:
|
||||
brew install
|
||||
autoconf automake libtool
|
||||
gtk-doc gobject-introspection
|
||||
cfitsio fftw giflib
|
||||
glib libexif libgsf
|
||||
libheif libjpeg-turbo libmatio
|
||||
librsvg libspng libtiff
|
||||
little-cms2 openexr openslide
|
||||
orc pango poppler webp
|
||||
|
||||
- name: Install Clang 10
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
if: contains(matrix.os, 'ubuntu') && matrix.build.cc == 'clang-10'
|
||||
run:
|
||||
sudo -E apt-get install --fix-missing -qq -o Acquire::Retries=3
|
||||
clang-10 libomp-10-dev lld-10 llvm-10
|
||||
|
||||
- name: Prepare build environment
|
||||
run: |
|
||||
echo "CC=${{ matrix.build.cc }}" >> $GITHUB_ENV
|
||||
echo "CXX=${{ matrix.build.cxx }}" >> $GITHUB_ENV
|
||||
echo "LD=${{ matrix.build.linker }}" >> $GITHUB_ENV
|
||||
echo "CPPFLAGS=-Wall" >> $GITHUB_ENV
|
||||
|
||||
- name: Prepare Ubuntu environment
|
||||
if: contains(matrix.os, 'ubuntu')
|
||||
run: echo "JOBS=$(nproc)" >> $GITHUB_ENV
|
||||
|
||||
- name: Prepare macOS environment
|
||||
if: contains(matrix.os, 'macos')
|
||||
run: |
|
||||
echo "JOBS=$(sysctl -n hw.logicalcpu)" >> $GITHUB_ENV
|
||||
echo "PKG_CONFIG_PATH=/usr/local/opt/jpeg-turbo/lib/pkgconfig:/usr/local/opt/libxml2/lib/pkgconfig" >> $GITHUB_ENV
|
||||
|
||||
- name: Prepare sanitizers
|
||||
if: matrix.sanitize
|
||||
env:
|
||||
LLVM_PREFIX: /usr/lib/llvm-10
|
||||
run: |
|
||||
ASAN_DSO=`$CC -print-file-name=libclang_rt.asan-x86_64.so`
|
||||
echo "LDSHARED=$CC -shared" >> $GITHUB_ENV
|
||||
echo "CPPFLAGS=-g -fsanitize=address,undefined -fno-omit-frame-pointer -fopenmp -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION" >> $GITHUB_ENV
|
||||
echo "LDFLAGS=-g -fsanitize=address,undefined -shared-libasan -fopenmp=libomp" >> $GITHUB_ENV
|
||||
echo "ASAN_DSO=$ASAN_DSO" >> $GITHUB_ENV
|
||||
echo "ASAN_OPTIONS=suppressions=${{ github.workspace }}/suppressions/asan.supp" >> $GITHUB_ENV
|
||||
echo "LSAN_OPTIONS=suppressions=${{ github.workspace }}/suppressions/lsan.supp" >> $GITHUB_ENV
|
||||
echo "UBSAN_OPTIONS=suppressions=${{ github.workspace }}/suppressions/ubsan.supp:print_stacktrace=1" >> $GITHUB_ENV
|
||||
echo "LD_LIBRARY_PATH=$LLVM_PREFIX/lib:`dirname $ASAN_DSO`" >> $GITHUB_ENV
|
||||
echo "$LLVM_PREFIX/bin" >> $GITHUB_PATH
|
||||
# workaround for https://github.com/google/sanitizers/issues/89
|
||||
# otherwise libIlmImf-2_3.so ends up as <unknown module>
|
||||
echo "DLCLOSE_PRELOAD=${{ github.workspace }}/dlclose.so" >> $GITHUB_ENV
|
||||
echo -e '#include <stdio.h>\nint dlclose(void*handle){return 0;}' | $CC -shared -xc -odlclose.so -
|
||||
|
||||
- name: Configure libvips
|
||||
run:
|
||||
./autogen.sh
|
||||
--disable-dependency-tracking
|
||||
--disable-deprecated || (cat config.log && exit 1)
|
||||
|
||||
- name: Build libvips
|
||||
run: make V=0 -j$JOBS
|
||||
|
||||
- name: Check libvips
|
||||
run: make V=0 check
|
||||
|
||||
- name: Install libvips
|
||||
run: sudo make V=0 install
|
||||
|
||||
- name: Rebuild the shared library cache
|
||||
if: contains(matrix.os, 'ubuntu')
|
||||
run: sudo ldconfig
|
||||
|
||||
- name: Install pyvips
|
||||
run: pip3 install pyvips[test]
|
||||
|
||||
- name: Run test suite
|
||||
env:
|
||||
VIPS_LEAK: 1
|
||||
LD_PRELOAD: ${{ env.ASAN_DSO }} ${{ env.DLCLOSE_PRELOAD }}
|
||||
run: python3 -m pytest -sv --log-cli-level=WARNING test/test-suite
|
87
.github/workflows/test.yml
vendored
87
.github/workflows/test.yml
vendored
@ -1,87 +0,0 @@
|
||||
name: Test
|
||||
|
||||
# to-do:
|
||||
# - add a macos test with brew etc.
|
||||
# - build with clang/asan/etc. and run the fuzz tests
|
||||
# - libvips work with 1.6.1 in ubuntu-20.04, but 1.10 has a bug in heic write
|
||||
# paste this back when it's working
|
||||
# - name: Add libheif PPA
|
||||
# run: |
|
||||
# sudo add-apt-repository ppa:strukturag/libde265
|
||||
# sudo add-apt-repository ppa:strukturag/libheif
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Update apt
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
run:
|
||||
sudo apt-get update -qq -o Acquire::Retries=3
|
||||
|
||||
- name: Install platform dependencies
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
run:
|
||||
sudo apt-get install --fix-missing -qq -o Acquire::Retries=3
|
||||
gtk-doc-tools
|
||||
gobject-introspection
|
||||
python3-pip
|
||||
python3-setuptools
|
||||
python3-wheel
|
||||
libfftw3-dev
|
||||
libexif-dev
|
||||
libjpeg-turbo8-dev
|
||||
libpng-dev
|
||||
libwebp-dev
|
||||
libtiff5-dev
|
||||
libheif-dev
|
||||
libexpat1-dev
|
||||
libcfitsio-dev
|
||||
libgsl-dev
|
||||
libmatio-dev
|
||||
libnifti-dev
|
||||
liborc-0.4-dev
|
||||
liblcms2-dev
|
||||
libpoppler-glib-dev
|
||||
librsvg2-dev
|
||||
libgif-dev
|
||||
libopenexr-dev
|
||||
libpango1.0-dev
|
||||
libgsf-1-dev
|
||||
libopenslide-dev
|
||||
libffi-dev
|
||||
|
||||
- name: Configure libvips
|
||||
run: CFLAGS=-Wall CXXFLAGS=-Wall ./autogen.sh
|
||||
--disable-dependency-tracking
|
||||
--disable-deprecated
|
||||
|
||||
- name: Build libvips
|
||||
run: make V=0
|
||||
|
||||
- name: Check libvips
|
||||
run: make V=0 check
|
||||
|
||||
- name: Install libvips
|
||||
run: |
|
||||
sudo make V=0 install
|
||||
sudo ldconfig
|
||||
|
||||
- name: Install pyvips
|
||||
run: pip3 install pyvips pytest
|
||||
|
||||
- name: Run test suite
|
||||
run: python3 -m pytest
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -43,7 +43,12 @@ Vips-8.0.typelib
|
||||
.deps
|
||||
.libs
|
||||
aclocal.m4
|
||||
config.*
|
||||
config.log
|
||||
config.h
|
||||
config.h.*
|
||||
config.guess
|
||||
config.status
|
||||
config.sub
|
||||
configure
|
||||
depcomp
|
||||
autom4te.cache/
|
||||
|
159
.travis.yml
159
.travis.yml
@ -1,159 +0,0 @@
|
||||
language: cpp
|
||||
|
||||
env:
|
||||
global:
|
||||
- PYTHON=python3
|
||||
- PYVIPS_VERSION=master
|
||||
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
sources: &common_sources
|
||||
# add support for HEIF files
|
||||
- sourceline: 'ppa:strukturag/libheif'
|
||||
- sourceline: 'ppa:strukturag/libde265'
|
||||
packages: &common_packages
|
||||
- gtk-doc-tools
|
||||
- gobject-introspection
|
||||
- python3-pip
|
||||
- python3-setuptools
|
||||
- python3-wheel
|
||||
- libfftw3-dev
|
||||
- libexif-dev
|
||||
- libjpeg-turbo8-dev
|
||||
- libpng-dev
|
||||
- libwebp-dev
|
||||
- libtiff5-dev
|
||||
- libheif-dev
|
||||
- libexpat1-dev
|
||||
- libcfitsio-dev
|
||||
- libgsl-dev
|
||||
- libmatio-dev
|
||||
- libnifti-dev
|
||||
- liborc-0.4-dev
|
||||
- liblcms2-dev
|
||||
- libpoppler-glib-dev
|
||||
- librsvg2-dev
|
||||
- libgif-dev
|
||||
- libopenexr-dev
|
||||
- libpango1.0-dev
|
||||
- libgsf-1-dev
|
||||
- libopenslide-dev
|
||||
- libffi-dev
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
- gtk-doc
|
||||
- gobject-introspection
|
||||
- fftw
|
||||
- libexif
|
||||
- libjpeg-turbo
|
||||
- webp
|
||||
- imagemagick
|
||||
- cfitsio
|
||||
- gsl
|
||||
- libmatio
|
||||
- orc
|
||||
- little-cms2
|
||||
- poppler
|
||||
- librsvg
|
||||
- openexr
|
||||
- pango
|
||||
- libgsf
|
||||
- openslide
|
||||
|
||||
jobs:
|
||||
allow_failures:
|
||||
- os: osx
|
||||
fast_finish: true
|
||||
include:
|
||||
- os: linux
|
||||
dist: bionic
|
||||
compiler: gcc
|
||||
name: "Ubuntu 18.04 / GCC 10"
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- *common_sources
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- *common_packages
|
||||
- libmagick++-dev
|
||||
- g++-10
|
||||
env:
|
||||
- JPEG=/usr
|
||||
- JOBS=`nproc`
|
||||
- WITH_MAGICK=yes
|
||||
- CC="gcc-10"
|
||||
- CXX="g++-10"
|
||||
- LDSHARED="$CC -shared"
|
||||
- CFLAGS="-Wcast-function-type"
|
||||
cache: ccache
|
||||
- os: linux
|
||||
dist: bionic
|
||||
compiler: clang
|
||||
name: "Ubuntu 18.04 / Clang 10 with ASan and UBSan"
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- *common_sources
|
||||
- sourceline: deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main
|
||||
key_url: https://apt.llvm.org/llvm-snapshot.gpg.key
|
||||
packages:
|
||||
- *common_packages
|
||||
- clang-10
|
||||
- libomp-10-dev
|
||||
env:
|
||||
- JPEG=/usr
|
||||
- JOBS=`nproc`
|
||||
- WITH_MAGICK=no
|
||||
- CC="clang-10"
|
||||
- CXX="clang++-10"
|
||||
- LDSHARED="$CC -shared"
|
||||
- CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -fopenmp -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION"
|
||||
- CXXFLAGS="$CFLAGS"
|
||||
- LDFLAGS="-fsanitize=address,undefined -shared-libasan -fopenmp=libomp"
|
||||
- ASAN_DSO=`$CC -print-file-name=libclang_rt.asan-x86_64.so`
|
||||
- ASAN_SYMBOLIZER_PATH=`which llvm-symbolizer-10`
|
||||
- ASAN_OPTIONS="suppressions=$TRAVIS_BUILD_DIR/suppressions/asan.supp"
|
||||
- LSAN_OPTIONS="suppressions=$TRAVIS_BUILD_DIR/suppressions/lsan.supp"
|
||||
- UBSAN_OPTIONS="suppressions=$TRAVIS_BUILD_DIR/suppressions/ubsan.supp:print_stacktrace=1"
|
||||
- LD_LIBRARY_PATH="/usr/lib/llvm-10/lib:`dirname $ASAN_DSO`"
|
||||
- DLCLOSE_PRELOAD="$TRAVIS_BUILD_DIR/dlclose.so"
|
||||
before_script:
|
||||
# workaround for https://github.com/google/sanitizers/issues/89
|
||||
# otherwise libIlmImf-2_2.so ends up as <unknown module>
|
||||
- echo -e '#include <stdio.h>\nint dlclose(void*handle){return 0;}' | $CC -shared -xc -odlclose.so -
|
||||
cache: ccache
|
||||
- os: osx
|
||||
osx_image: xcode11
|
||||
name: "macOS 10.14.6 / GCC 9"
|
||||
env:
|
||||
- JPEG=/usr/local/opt/jpeg-turbo
|
||||
- JOBS="`sysctl -n hw.ncpu`"
|
||||
- WITH_MAGICK=yes
|
||||
- PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
- PKG_CONFIG_PATH="/usr/local/opt/jpeg-turbo/lib/pkgconfig:/usr/local/opt/libxml2/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
- HOMEBREW_NO_AUTO_UPDATE=1
|
||||
- CC="gcc-9"
|
||||
- CXX="g++-9"
|
||||
cache: ccache
|
||||
|
||||
install:
|
||||
- $PYTHON -m pip download --no-deps https://github.com/libvips/pyvips/archive/$PYVIPS_VERSION.tar.gz
|
||||
- tar xf $PYVIPS_VERSION.tar.gz
|
||||
- $PYTHON -m pip install --user --upgrade pyvips-$PYVIPS_VERSION/[test]
|
||||
- ./autogen.sh
|
||||
--disable-dependency-tracking
|
||||
--disable-deprecated
|
||||
--with-jpeg-includes=$JPEG/include
|
||||
--with-jpeg-libraries=$JPEG/lib
|
||||
--with-magick=$WITH_MAGICK
|
||||
- make -j$JOBS -s
|
||||
|
||||
script:
|
||||
- make -j$JOBS -s -k V=0 VERBOSE=1 check
|
||||
- LD_LIBRARY_PATH="$PWD/libvips/.libs:$LD_LIBRARY_PATH"
|
||||
DYLD_LIBRARY_PATH=$PWD/libvips/.libs
|
||||
LD_PRELOAD="$ASAN_DSO $DLCLOSE_PRELOAD"
|
||||
$PYTHON -m pytest -sv --log-cli-level=WARNING test/test-suite
|
4
AUTHORS
4
AUTHORS
@ -2,7 +2,7 @@ Authors of VIPS
|
||||
|
||||
See also the files THANKS and ChangeLog
|
||||
|
||||
Nicos Dessipris and Kirk Martinez started VIPS in 1990.
|
||||
Kirk Martinez and Nicos Dessipris started VIPS in 1990.
|
||||
|
||||
John Cupitt started ip in late 1990, and took over maintenance of the VIPS
|
||||
library in 1995.
|
||||
@ -22,3 +22,5 @@ contributed the tmake VIPS.DLL build system and the MSVC project files.
|
||||
Nicolas Robidoux contributed optimized bilinear and bicubic code to
|
||||
the VipsInterpolate class and, with Chantal Racette and Adam Turcotte,
|
||||
contributed the novel LBB, Nohalo and VSQBS interpolators.
|
||||
|
||||
Many other active contributors, see https://github.com/libvips/libvips/graphs/contributors
|
||||
|
15
ChangeLog
15
ChangeLog
@ -11,6 +11,15 @@
|
||||
- add "seed" param to perlin, worley and gaussnoise
|
||||
- add vips_source_g_input_stream_new() to load images from a GInputStream
|
||||
- add openslideload_source(), vipsload_source(), vipssave_target()
|
||||
- add hist path to rank for large windows on uchar images
|
||||
- better 8/16-bit choice for pngsave
|
||||
- avoid NaN in mapim [afontenot]
|
||||
- hist_find outputs a double histogram for large images [erdmann]
|
||||
- fix ref leaks in mosaicing package
|
||||
- run libvips leak test in CI
|
||||
- add vips_fitsload_source(), vips_niftiload_source()
|
||||
- png and gif load note background colour as metadata [781545872]
|
||||
- add vips_image_[set|get]_array_double()
|
||||
|
||||
22/12/20 start 8.10.6
|
||||
- don't seek on bad file descriptors [kleisauke]
|
||||
@ -18,6 +27,12 @@
|
||||
- revise ppmload, fixing a couple of small bugs
|
||||
- signal error on EOF in jpegload more reliably [bozaro]
|
||||
- better error detection in spngload [randy408]
|
||||
- fix includes of glib headers in C++ [lovell]
|
||||
- fix build with more modern librsvg [lovell]
|
||||
- fix a possible segv with very wide images [f1ac]
|
||||
- revise premultiply, again [jjonesrs]
|
||||
- revise profile handling in vipsthumbnail
|
||||
- fix tiff deflate predictor setting [Adios]
|
||||
|
||||
18/12/20 started 8.10.5
|
||||
- fix potential /0 in animated webp load [lovell]
|
||||
|
41
README.md
41
README.md
@ -1,6 +1,6 @@
|
||||
# libvips : an image processing library
|
||||
|
||||
[](https://github.com/libvips/libvips/actions?query=workflow%3ATest)
|
||||
[](https://github.com/libvips/libvips/actions)
|
||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=2&q=proj:libvips)
|
||||
[](https://scan.coverity.com/projects/jcupitt-libvips)
|
||||
[](https://gitter.im/libvips/devchat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
@ -16,22 +16,22 @@ libvips is licensed under the [LGPL
|
||||
2.1+](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html).
|
||||
|
||||
It has around [300
|
||||
operations](http://libvips.github.io/libvips/API/current/func-list.html)
|
||||
operations](https://libvips.github.io/libvips/API/current/func-list.html)
|
||||
covering arithmetic, histograms, convolution, morphological
|
||||
operations, frequency filtering, colour, resampling,
|
||||
statistics and others. It supports a large range of [numeric
|
||||
types](http://libvips.github.io/libvips/API/current/VipsImage.html#VipsBandFormat),
|
||||
types](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsBandFormat),
|
||||
from 8-bit int to 128-bit complex. Images can have any number of bands.
|
||||
It supports a good range of image formats, including JPEG, TIFF, PNG,
|
||||
WebP, HEIC, FITS, Matlab, OpenEXR, PDF, SVG, HDR, PPM / PGM / PFM, CSV,
|
||||
GIF, Analyze, NIfTI, DeepZoom, and OpenSlide. It can also load images via
|
||||
ImageMagick or GraphicsMagick, letting it work with formats like DICOM.
|
||||
WebP, HEIC, AVIF, FITS, Matlab, OpenEXR, PDF, SVG, HDR, PPM / PGM / PFM,
|
||||
CSV, GIF, Analyze, NIfTI, DeepZoom, and OpenSlide. It can also load images
|
||||
via ImageMagick or GraphicsMagick, letting it work with formats like DICOM.
|
||||
|
||||
It comes with bindings for
|
||||
[C](http://libvips.github.io/libvips/API/current/using-from-c.html),
|
||||
[C++](http://libvips.github.io/libvips/API/current/using-from-cpp.html),
|
||||
[C](https://libvips.github.io/libvips/API/current/using-from-c.html),
|
||||
[C++](https://libvips.github.io/libvips/API/current/using-from-cpp.html),
|
||||
and the
|
||||
[command-line](http://libvips.github.io/libvips/API/current/using-cli.html).
|
||||
[command-line](https://libvips.github.io/libvips/API/current/using-cli.html).
|
||||
Full bindings are available for [Ruby](https://rubygems.org/gems/ruby-vips),
|
||||
[Python](https://pypi.python.org/pypi/pyvips),
|
||||
[PHP](https://github.com/libvips/php-vips),
|
||||
@ -42,9 +42,9 @@ is used as an image processing engine by [sharp
|
||||
(on node.js)](https://www.npmjs.org/package/sharp),
|
||||
[bimg](https://github.com/h2non/bimg), [sharp
|
||||
for Go](https://github.com/DAddYE/vips), [Ruby on
|
||||
Rails](http://edgeguides.rubyonrails.org/active_storage_overview.html),
|
||||
Rails](https://edgeguides.rubyonrails.org/active_storage_overview.html),
|
||||
[carrierwave-vips](https://github.com/eltiare/carrierwave-vips),
|
||||
[mediawiki](http://www.mediawiki.org/wiki/Extension:VipsScaler),
|
||||
[mediawiki](https://www.mediawiki.org/wiki/Extension:VipsScaler),
|
||||
[PhotoFlow](https://github.com/aferrero2707/PhotoFlow) and others.
|
||||
The official libvips GUI is [nip2](https://github.com/libvips/nip2),
|
||||
a strange combination of a spreadsheet and an photo editor.
|
||||
@ -212,7 +212,7 @@ If libvips finds this library, it uses it for fourier transforms.
|
||||
### lcms2
|
||||
|
||||
If present, `vips_icc_import()`, `vips_icc_export()` and `vips_icc_transform()`
|
||||
are available for transforming images with ICC profiles.
|
||||
can be used to manipulate images with ICC profiles.
|
||||
|
||||
### libspng
|
||||
|
||||
@ -279,7 +279,22 @@ files: Aperio, Hamamatsu, Leica, MIRAX, Sakura, Trestle, and Ventana.
|
||||
|
||||
### libheif
|
||||
|
||||
If available, libvips can load and save HEIC images.
|
||||
If available, libvips can load and save HEIC and AVIF images. Your libheif (in
|
||||
turn) needs to be built with the correct decoders and encoders. You can check
|
||||
with eg.:
|
||||
|
||||
```
|
||||
$ pkg-config libheif --print-variables
|
||||
builtin_avif_decoder
|
||||
builtin_avif_encoder
|
||||
builtin_h265_decoder
|
||||
builtin_h265_encoder
|
||||
exec_prefix
|
||||
includedir
|
||||
libdir
|
||||
pcfiledir
|
||||
prefix
|
||||
```
|
||||
|
||||
# Contributors
|
||||
|
||||
|
60
configure.ac
60
configure.ac
@ -429,19 +429,9 @@ AC_CHECK_LIB(m,hypot,[AC_DEFINE(HAVE_HYPOT,1,[have hypot() in libm.])])
|
||||
AC_CHECK_LIB(m,atan2,[AC_DEFINE(HAVE_ATAN2,1,[have atan2() in libm.])])
|
||||
|
||||
# have to have these parts of glib ... we need glib 2.15 for gio
|
||||
PKG_CHECK_MODULES(REQUIRED, glib-2.0 >= 2.15 gmodule-2.0 gobject-2.0 gio-2.0)
|
||||
PKG_CHECK_MODULES(REQUIRED, glib-2.0 >= 2.40 gmodule-2.0 gobject-2.0 gio-2.0)
|
||||
PACKAGES_USED="$PACKAGES_USED glib-2.0 gmodule-2.0 gobject-2.0 gio-2.0"
|
||||
|
||||
# from 2.28 we have a monotonic timer
|
||||
PKG_CHECK_MODULES(MONOTONIC_TIME, glib-2.0 >= 2.28,
|
||||
[AC_DEFINE(HAVE_MONOTONIC_TIME,1,
|
||||
[define if your glib has g_get_monotonic_time().]
|
||||
)
|
||||
],
|
||||
[:
|
||||
]
|
||||
)
|
||||
|
||||
# from 2.62 we have datetime
|
||||
PKG_CHECK_MODULES(DATE_TIME_FORMAT_ISO8601, glib-2.0 >= 2.62,
|
||||
[AC_DEFINE(HAVE_DATE_TIME_FORMAT_ISO8601,1,
|
||||
@ -452,22 +442,6 @@ PKG_CHECK_MODULES(DATE_TIME_FORMAT_ISO8601, glib-2.0 >= 2.62,
|
||||
]
|
||||
)
|
||||
|
||||
# from 2.32 there are a new set of thread functions, it is no longer
|
||||
# necessary to use g_thread_init() or to link against libgthread
|
||||
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().]
|
||||
)
|
||||
],
|
||||
[PKG_CHECK_MODULES(GTHREAD, gthread-2.0)
|
||||
PACKAGES_USED="$PACKAGES_USED gthread-2.0"
|
||||
]
|
||||
)
|
||||
|
||||
# if available, we use pthread_setattr_default_np() to raise the per-thread
|
||||
# stack size ... musl (libc on Alpine), for example, has a very small stack per
|
||||
# thread by default
|
||||
@ -482,32 +456,10 @@ AC_CHECK_FUNC(pthread_setattr_default_np,
|
||||
LIBS="$save_pthread_LIBS"
|
||||
CFLAGS="$save_pthread_CFLAGS"
|
||||
|
||||
# from 2.36 the type system inits itself
|
||||
PKG_CHECK_MODULES(TYPE_INIT, glib-2.0 < 2.36,
|
||||
[AC_DEFINE(HAVE_TYPE_INIT,1,[define if your glib needs g_type_init().])
|
||||
],
|
||||
[:
|
||||
]
|
||||
)
|
||||
|
||||
# from 2.40, on win32 we have g_win32_get_command_line()
|
||||
PKG_CHECK_MODULES(WIN32_GET_COMMAND_LINE, 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
|
||||
],
|
||||
[:
|
||||
]
|
||||
)
|
||||
|
||||
# from 2.40, have g_str_to_ascii()
|
||||
PKG_CHECK_MODULES(STR_TO_ASCII, glib-2.0 >= 2.40,
|
||||
[AC_DEFINE(HAVE_G_STR_TO_ASCII,1,[define if your glib has g_str_to_ascii().])
|
||||
],
|
||||
[:
|
||||
]
|
||||
)
|
||||
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
|
||||
|
||||
# from 2.48 we have g_uint_checked_mul() etc.
|
||||
PKG_CHECK_MODULES(HAVE_CHECKED_MUL, glib-2.0 >= 2.48,
|
||||
@ -1108,7 +1060,7 @@ if test x"$with_tiff" != x"no"; then
|
||||
)
|
||||
fi
|
||||
|
||||
# WEBP in TIFF added in libtiff 4.0.10
|
||||
# ZSTD and WEBP in TIFF added in libtiff 4.0.10
|
||||
if test x"$with_tiff" != x"no"; then
|
||||
save_INCLUDES="$INCLUDES"
|
||||
INCLUDES="$INCLUDES $TIFF_INCLUDES"
|
||||
|
@ -2977,6 +2977,8 @@ static VImage heifload_source( VSource source, VOption *options = 0 );
|
||||
* - **Q** -- Q factor, int.
|
||||
* - **lossless** -- Enable lossless compression, bool.
|
||||
* - **compression** -- Compression format, VipsForeignHeifCompression.
|
||||
* - **speed**: -- CPU effort, 0 slowest - 8 fastest, AV1 compression only, int.
|
||||
* - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample.
|
||||
* - **strip** -- Strip all metadata from image, bool.
|
||||
* - **background** -- Background value, std::vector<double>.
|
||||
* - **page_height** -- Set page height for multipage save, int.
|
||||
@ -2993,6 +2995,8 @@ void heifsave( const char *filename, VOption *options = 0 ) const;
|
||||
* - **Q** -- Q factor, int.
|
||||
* - **lossless** -- Enable lossless compression, bool.
|
||||
* - **compression** -- Compression format, VipsForeignHeifCompression.
|
||||
* - **speed**: -- CPU effort, 0 slowest - 8 fastest, AV1 compression only, int.
|
||||
* - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample.
|
||||
* - **strip** -- Strip all metadata from image, bool.
|
||||
* - **background** -- Background value, std::vector<double>.
|
||||
* - **page_height** -- Set page height for multipage save, int.
|
||||
@ -3009,6 +3013,8 @@ VipsBlob *heifsave_buffer( VOption *options = 0 ) const;
|
||||
* - **Q** -- Q factor, int.
|
||||
* - **lossless** -- Enable lossless compression, bool.
|
||||
* - **compression** -- Compression format, VipsForeignHeifCompression.
|
||||
* - **speed**: -- CPU effort, 0 slowest - 8 fastest, AV1 compression only, int.
|
||||
* - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample.
|
||||
* - **strip** -- Strip all metadata from image, bool.
|
||||
* - **background** -- Background value, std::vector<double>.
|
||||
* - **page_height** -- Set page height for multipage save, int.
|
||||
@ -3341,7 +3347,7 @@ static VImage jpegload_source( VSource source, VOption *options = 0 );
|
||||
* - **overshoot_deringing** -- Apply overshooting to samples with extreme values, bool.
|
||||
* - **optimize_scans** -- Split spectrum of DCT coefficients into separate scans, bool.
|
||||
* - **quant_table** -- Use predefined quantization table with given index, int.
|
||||
* - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignJpegSubsample.
|
||||
* - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample.
|
||||
* - **strip** -- Strip all metadata from image, bool.
|
||||
* - **background** -- Background value, std::vector<double>.
|
||||
* - **page_height** -- Set page height for multipage save, int.
|
||||
@ -3364,7 +3370,7 @@ void jpegsave( const char *filename, VOption *options = 0 ) const;
|
||||
* - **overshoot_deringing** -- Apply overshooting to samples with extreme values, bool.
|
||||
* - **optimize_scans** -- Split spectrum of DCT coefficients into separate scans, bool.
|
||||
* - **quant_table** -- Use predefined quantization table with given index, int.
|
||||
* - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignJpegSubsample.
|
||||
* - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample.
|
||||
* - **strip** -- Strip all metadata from image, bool.
|
||||
* - **background** -- Background value, std::vector<double>.
|
||||
* - **page_height** -- Set page height for multipage save, int.
|
||||
@ -3387,7 +3393,7 @@ VipsBlob *jpegsave_buffer( VOption *options = 0 ) const;
|
||||
* - **overshoot_deringing** -- Apply overshooting to samples with extreme values, bool.
|
||||
* - **optimize_scans** -- Split spectrum of DCT coefficients into separate scans, bool.
|
||||
* - **quant_table** -- Use predefined quantization table with given index, int.
|
||||
* - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignJpegSubsample.
|
||||
* - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample.
|
||||
* - **strip** -- Strip all metadata from image, bool.
|
||||
* - **background** -- Background value, std::vector<double>.
|
||||
* - **page_height** -- Set page height for multipage save, int.
|
||||
@ -3409,7 +3415,7 @@ void jpegsave_mime( VOption *options = 0 ) const;
|
||||
* - **overshoot_deringing** -- Apply overshooting to samples with extreme values, bool.
|
||||
* - **optimize_scans** -- Split spectrum of DCT coefficients into separate scans, bool.
|
||||
* - **quant_table** -- Use predefined quantization table with given index, int.
|
||||
* - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignJpegSubsample.
|
||||
* - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample.
|
||||
* - **strip** -- Strip all metadata from image, bool.
|
||||
* - **background** -- Background value, std::vector<double>.
|
||||
* - **page_height** -- Set page height for multipage save, int.
|
||||
|
@ -18,12 +18,28 @@ to what you need.
|
||||
|
||||
# Don't bind the top-level C API
|
||||
|
||||
The libvips C API (vips_add() and so on) is very inconvenient and dangerous
|
||||
to use from other languages due to its heavy use of varargs.
|
||||
The libvips C API (vips_add() and so on) was designed to be easy for humans
|
||||
to write. It is inconvenient and dangerous to use from other languages due
|
||||
to its heavy use of varargs.
|
||||
|
||||
It's much better to use the layer below. This lower layer is structured as
|
||||
create operator, set parameters, execute, extract results. For example, you can
|
||||
execute vips_invert() like this:
|
||||
It's much better to use the layer below. This lower layer is structured as:
|
||||
|
||||
- Create operator. You can use vips_operation_new() to make a new
|
||||
`VipsOperation` object from an operator nickname, like `"add"`.
|
||||
|
||||
- Set parameters. You can loop over the operation with vips_argument_map() to
|
||||
get the name and type of each input argument. For each argument, you
|
||||
need to get the value from your language, convert to a `GValue`, then
|
||||
use g_object_set_property() to set that value on the operator.
|
||||
|
||||
- Execute with vips_cache_operation_build().
|
||||
|
||||
- Extract results. Again, you loop over the operator arguments with
|
||||
vips_argument_map(), but instead of inputs, this time you look for output
|
||||
arguments. You extract their value with g_object_get_property(), and pass
|
||||
the value back to your language.
|
||||
|
||||
For example, you can execute vips_invert() like this:
|
||||
|
||||
```C
|
||||
/* compile with
|
||||
@ -113,18 +129,11 @@ main( int argc, char **argv )
|
||||
}
|
||||
```
|
||||
|
||||
libvips has a couple of extra things to let you examine the arguments and
|
||||
types of an operator at runtime. Use vips_argument_map() to loop
|
||||
over all the arguments of an operator, and vips_object_get_argument()
|
||||
to fetch the type and flags of a specific argument.
|
||||
|
||||
Use vips_operation_get_flags() to get general information about an operator.
|
||||
|
||||
# Compiled language which can call C
|
||||
|
||||
The C++ binding uses this lower layer to define a function called
|
||||
`VImage::call()` which can call any libvips operator with a not-varargs set of
|
||||
variable arguments.
|
||||
`VImage::call()` which can call any libvips operator with a set of variable
|
||||
arguments.
|
||||
|
||||
A small Python program walks the set of all libvips operators and generates a
|
||||
set of static bindings. For example:
|
||||
@ -142,9 +151,9 @@ VImage VImage::invert( VOption *options )
|
||||
}
|
||||
```
|
||||
|
||||
So from C++ you can call any libvips operator (though without type-safety) with
|
||||
`VImage::call()`, or use the member functions on `VImage` to get type-safe
|
||||
calls for at least the required operator arguments.
|
||||
So from C++ you can call any libvips operator (though without static
|
||||
typechecking) with `VImage::call()`, or use the member functions on `VImage`
|
||||
to get type-checked calls for at least the required operator arguments.
|
||||
|
||||
The `VImage` class also adds automatic reference counting, constant expansion,
|
||||
operator overloads, and various other useful features.
|
||||
@ -157,15 +166,14 @@ but use FFI to call into libvips and run operations.
|
||||
|
||||
Since these languages are dynamic, they can add another trick: they intercept
|
||||
the method-missing hook and attempt to run any method calls not implemented by
|
||||
the `Image` class as libvips operators. This makes these bindings self-writing:
|
||||
they only contain a small amount of code and just expose everything they find in
|
||||
the libvips class hierarchy.
|
||||
the `Image` class as libvips operators. In effect, the binding is generated at
|
||||
runtime.
|
||||
|
||||
# Dynamic langauge without FFI
|
||||
|
||||
PHP does not have FFI, unfortunately, so for this language a small native
|
||||
module implements the general `vips_call()` function for PHP language types,
|
||||
and a larger pure PHP layer makes it convenient to use.
|
||||
PHP does not have a useful FFI, unfortunately, so for this language a small
|
||||
C module implements the general `vips_call()` function for PHP language
|
||||
types, and a larger pure PHP layer makes it convenient to use.
|
||||
|
||||
# gobject-introspection
|
||||
|
||||
|
@ -26,6 +26,8 @@
|
||||
* - redo as a class
|
||||
* 28/2/16 lovell
|
||||
* - unroll common cases
|
||||
* 1/2/21 erdmann
|
||||
* - use double for very large histograms
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -69,11 +71,11 @@
|
||||
/* Accumulate a histogram in one of these.
|
||||
*/
|
||||
typedef struct {
|
||||
int bands; /* Number of bands in output */
|
||||
int which; /* If one band in out, which band of input */
|
||||
int n_bands; /* Number of bands in output */
|
||||
int band; /* If one band in out, which band of input */
|
||||
int size; /* Number of bins for each band */
|
||||
int mx; /* Maximum value we have seen */
|
||||
unsigned int **bins; /* All the bins! */
|
||||
VipsPel **bins; /* double or uint bins */
|
||||
} Histogram;
|
||||
|
||||
typedef struct _VipsHistFind {
|
||||
@ -81,7 +83,7 @@ typedef struct _VipsHistFind {
|
||||
|
||||
/* -1 for all bands, or the band we scan.
|
||||
*/
|
||||
int which;
|
||||
int band;
|
||||
|
||||
/* Main image histogram. Subhists accumulate to this.
|
||||
*/
|
||||
@ -91,6 +93,11 @@ typedef struct _VipsHistFind {
|
||||
*/
|
||||
VipsImage *out;
|
||||
|
||||
/* TRUE for "large" histograms ... causes double output rather than
|
||||
* uint.
|
||||
*/
|
||||
gboolean large;
|
||||
|
||||
} VipsHistFind;
|
||||
|
||||
typedef VipsStatisticClass VipsHistFindClass;
|
||||
@ -100,24 +107,28 @@ G_DEFINE_TYPE( VipsHistFind, vips_hist_find, VIPS_TYPE_STATISTIC );
|
||||
/* Build a Histogram.
|
||||
*/
|
||||
static Histogram *
|
||||
histogram_new( VipsHistFind *hist_find, int bands, int which, int size )
|
||||
histogram_new( VipsHistFind *hist_find, int n_bands, int band, int size )
|
||||
{
|
||||
/* We won't use all of this for uint accumulators.
|
||||
*/
|
||||
int n_bytes = size * sizeof( double );
|
||||
|
||||
Histogram *hist;
|
||||
int i;
|
||||
|
||||
if( !(hist = VIPS_NEW( hist_find, Histogram )) ||
|
||||
!(hist->bins = VIPS_ARRAY( hist_find, bands, unsigned int * )) )
|
||||
!(hist->bins = VIPS_ARRAY( hist_find, n_bands, VipsPel * )) )
|
||||
return( NULL );
|
||||
|
||||
for( i = 0; i < bands; i++ ) {
|
||||
if( !(hist->bins[i] =
|
||||
VIPS_ARRAY( hist_find, size, unsigned int )) )
|
||||
for( i = 0; i < n_bands; i++ ) {
|
||||
if( !(hist->bins[i] = VIPS_ARRAY( hist_find,
|
||||
n_bytes, VipsPel )) )
|
||||
return( NULL );
|
||||
memset( hist->bins[i], 0, size * sizeof( unsigned int ) );
|
||||
memset( hist->bins[i], 0, n_bytes );
|
||||
}
|
||||
|
||||
hist->bands = bands;
|
||||
hist->which = which;
|
||||
hist->n_bands = n_bands;
|
||||
hist->band = band;
|
||||
hist->size = size;
|
||||
hist->mx = 0;
|
||||
|
||||
@ -130,21 +141,27 @@ vips_hist_find_build( VipsObject *object )
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsStatistic *statistic = VIPS_STATISTIC( object );
|
||||
VipsHistFind *hist_find = (VipsHistFind *) object;
|
||||
VipsImage *in = statistic->in;
|
||||
|
||||
unsigned int *obuffer;
|
||||
unsigned int *q;
|
||||
int i, j;
|
||||
VipsPel *obuffer;
|
||||
|
||||
g_object_set( object,
|
||||
"out", vips_image_new(),
|
||||
NULL );
|
||||
|
||||
if( statistic->in &&
|
||||
vips_check_bandno( class->nickname,
|
||||
statistic->in, hist_find->which ) )
|
||||
if( in &&
|
||||
vips_check_bandno( class->nickname, in, hist_find->band ) )
|
||||
return( -1 );
|
||||
|
||||
/* main hist made on first thread start.
|
||||
/* Is this a large histogram? We want to avoid overflow of the uint
|
||||
* accumulators.
|
||||
*/
|
||||
if( in &&
|
||||
(guint64) in->Xsize * (guint64) in->Ysize >=
|
||||
((guint64) 1 << 32) )
|
||||
hist_find->large = TRUE;
|
||||
|
||||
/* main hist made on in vips_hist_find_start().
|
||||
*/
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_hist_find_parent_class )->build( object ) )
|
||||
@ -156,20 +173,33 @@ vips_hist_find_build( VipsObject *object )
|
||||
VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL ) )
|
||||
return( -1 );
|
||||
vips_image_init_fields( hist_find->out,
|
||||
hist_find->hist->mx + 1, 1, hist_find->hist->bands,
|
||||
VIPS_FORMAT_UINT,
|
||||
hist_find->hist->mx + 1, 1, hist_find->hist->n_bands,
|
||||
hist_find->large ? VIPS_FORMAT_DOUBLE : VIPS_FORMAT_UINT,
|
||||
VIPS_CODING_NONE, VIPS_INTERPRETATION_HISTOGRAM, 1.0, 1.0 );
|
||||
|
||||
/* Interleave for output.
|
||||
*/
|
||||
if( !(obuffer = VIPS_ARRAY( object,
|
||||
VIPS_IMAGE_N_ELEMENTS( hist_find->out ), unsigned int )) )
|
||||
VIPS_IMAGE_SIZEOF_LINE( hist_find->out ), VipsPel )) )
|
||||
return( -1 );
|
||||
for( q = obuffer, j = 0; j < hist_find->out->Xsize; j++ )
|
||||
for( i = 0; i < hist_find->out->Bands; i++ )
|
||||
*q++ = hist_find->hist->bins[i][j];
|
||||
|
||||
if( vips_image_write_line( hist_find->out, 0, (VipsPel *) obuffer ) )
|
||||
#define INTERLEAVE( TYPE ) G_STMT_START { \
|
||||
TYPE **bins = (TYPE **) hist_find->hist->bins; \
|
||||
\
|
||||
TYPE *q; \
|
||||
int i, j; \
|
||||
\
|
||||
for( q = (TYPE *) obuffer, j = 0; j < hist_find->out->Xsize; j++ ) \
|
||||
for( i = 0; i < hist_find->out->Bands; i++ ) \
|
||||
*q++ = bins[i][j]; \
|
||||
} G_STMT_END
|
||||
|
||||
if( hist_find->large )
|
||||
INTERLEAVE( double );
|
||||
else
|
||||
INTERLEAVE( unsigned int );
|
||||
|
||||
if( vips_image_write_line( hist_find->out, 0, obuffer ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
@ -186,15 +216,15 @@ vips_hist_find_start( VipsStatistic *statistic )
|
||||
*/
|
||||
if( !hist_find->hist )
|
||||
hist_find->hist = histogram_new( hist_find,
|
||||
hist_find->which == -1 ?
|
||||
hist_find->band == -1 ?
|
||||
statistic->ready->Bands : 1,
|
||||
hist_find->which,
|
||||
hist_find->band,
|
||||
statistic->ready->BandFmt == VIPS_FORMAT_UCHAR ?
|
||||
256 : 65536 );
|
||||
|
||||
return( (void *) histogram_new( hist_find,
|
||||
hist_find->hist->bands,
|
||||
hist_find->hist->which,
|
||||
hist_find->hist->n_bands,
|
||||
hist_find->hist->band,
|
||||
hist_find->hist->size ) );
|
||||
}
|
||||
|
||||
@ -209,210 +239,143 @@ vips_hist_find_stop( VipsStatistic *statistic, void *seq )
|
||||
|
||||
int i, j;
|
||||
|
||||
g_assert( sub_hist->bands == hist->bands &&
|
||||
g_assert( sub_hist->n_bands == hist->n_bands &&
|
||||
sub_hist->size == hist->size );
|
||||
|
||||
/* Add on sub-data.
|
||||
*/
|
||||
hist->mx = VIPS_MAX( hist->mx, sub_hist->mx );
|
||||
for( i = 0; i < hist->bands; i++ )
|
||||
for( j = 0; j < hist->size; j++ )
|
||||
hist->bins[i][j] += sub_hist->bins[i][j];
|
||||
|
||||
#define SUM( TYPE ) G_STMT_START { \
|
||||
TYPE **main_bins = (TYPE **) hist->bins; \
|
||||
TYPE **sub_bins = (TYPE **) sub_hist->bins; \
|
||||
\
|
||||
for( i = 0; i < hist->n_bands; i++ ) \
|
||||
for( j = 0; j < hist->size; j++ ) \
|
||||
main_bins[i][j] += sub_bins[i][j]; \
|
||||
} G_STMT_END
|
||||
|
||||
if( hist_find->large )
|
||||
SUM( double );
|
||||
else
|
||||
SUM( unsigned int );
|
||||
|
||||
/* Blank out sub-hist to make sure we can't add it again.
|
||||
*/
|
||||
sub_hist->mx = 0;
|
||||
for( i = 0; i < sub_hist->bands; i++ )
|
||||
for( i = 0; i < sub_hist->n_bands; i++ )
|
||||
sub_hist->bins[i] = NULL;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Hist of all bands of uchar.
|
||||
#define SCANOP G_STMT_START { \
|
||||
for( z = 0; z < nb; z++ ) { \
|
||||
int v = p[z]; \
|
||||
\
|
||||
if( v > mx ) \
|
||||
mx = v; \
|
||||
\
|
||||
bins[z][v] += 1; \
|
||||
} \
|
||||
\
|
||||
p += nb; \
|
||||
} G_STMT_END
|
||||
|
||||
/* No need to track max for uchar images (it's always 255).
|
||||
*/
|
||||
static int
|
||||
vips_hist_find_uchar_scan( VipsStatistic *statistic,
|
||||
void *seq, int x, int y, void *in, int n )
|
||||
{
|
||||
Histogram *hist = (Histogram *) seq;
|
||||
unsigned int *bins = hist->bins[0];
|
||||
int nb = statistic->ready->Bands;
|
||||
VipsPel *p = (VipsPel *) in;
|
||||
#define UCSCANOP G_STMT_START { \
|
||||
for( z = 0; z < nb; z++ ) { \
|
||||
int v = p[z]; \
|
||||
\
|
||||
bins[z][v] += 1; \
|
||||
} \
|
||||
\
|
||||
p += nb; \
|
||||
} G_STMT_END
|
||||
|
||||
int j;
|
||||
|
||||
/* The inner loop cannot be auto-vectorized by the compiler.
|
||||
* Unroll for common cases.
|
||||
*/
|
||||
switch( nb ) {
|
||||
case 1:
|
||||
for( j = 0; j < n; j++ )
|
||||
bins[p[j]] += 1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
for( j = 0; j < n; j++ ) {
|
||||
hist->bins[0][p[0]] += 1;
|
||||
hist->bins[1][p[1]] += 1;
|
||||
|
||||
p += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
for( j = 0; j < n; j++ ) {
|
||||
hist->bins[0][p[0]] += 1;
|
||||
hist->bins[1][p[1]] += 1;
|
||||
hist->bins[2][p[2]] += 1;
|
||||
|
||||
p += 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
for( j = 0; j < n; j++ ) {
|
||||
hist->bins[0][p[0]] += 1;
|
||||
hist->bins[1][p[1]] += 1;
|
||||
hist->bins[2][p[2]] += 1;
|
||||
hist->bins[3][p[3]] += 1;
|
||||
|
||||
p += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Loop when >4 bands
|
||||
*/
|
||||
for( j = 0; j < n; j++ ) {
|
||||
int z;
|
||||
|
||||
for( z = 0; z < nb; z++ )
|
||||
hist->bins[z][p[z]] += 1;
|
||||
|
||||
p += nb;
|
||||
}
|
||||
}
|
||||
|
||||
/* Note the maximum.
|
||||
*/
|
||||
hist->mx = 255;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Histogram of a selected band of a uchar image.
|
||||
/* Hist of all bands. This one is just about worth unrolling.
|
||||
*/
|
||||
static int
|
||||
vips_hist_find_uchar_extract_scan( VipsStatistic *statistic,
|
||||
void *seq, int x, int y, void *in, int n )
|
||||
{
|
||||
Histogram *hist = (Histogram *) seq;
|
||||
int nb = statistic->ready->Bands;
|
||||
int max = n * nb;
|
||||
unsigned int *bins = hist->bins[0];
|
||||
VipsPel *p = (VipsPel *) in;
|
||||
#define SCAN( IMAGE_TYPE, HIST_TYPE, OP ) G_STMT_START { \
|
||||
HIST_TYPE **bins = (HIST_TYPE **) hist->bins; \
|
||||
IMAGE_TYPE *p = (IMAGE_TYPE *) in; \
|
||||
\
|
||||
int z; \
|
||||
\
|
||||
VIPS_UNROLL( n, OP ); \
|
||||
} G_STMT_END
|
||||
|
||||
int i;
|
||||
|
||||
for( i = hist->which; i < max; i += nb )
|
||||
bins[p[i]] += 1;
|
||||
|
||||
/* Note the maximum.
|
||||
*/
|
||||
hist->mx = 255;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Histogram of all bands of a ushort image.
|
||||
/* Hist of selected band.
|
||||
*/
|
||||
static int
|
||||
vips_hist_find_ushort_scan( VipsStatistic *statistic,
|
||||
void *seq, int x, int y, void *in, int n )
|
||||
{
|
||||
Histogram *hist = (Histogram *) seq;
|
||||
int mx = hist->mx;
|
||||
int nb = statistic->ready->Bands;
|
||||
unsigned short *p = (unsigned short *) in;
|
||||
|
||||
int j, z;
|
||||
|
||||
for( j = 0; j < n; j++ ) {
|
||||
for( z = 0; z < nb; z++ ) {
|
||||
int v = p[z];
|
||||
|
||||
/* Adjust maximum.
|
||||
*/
|
||||
if( v > mx )
|
||||
mx = v;
|
||||
|
||||
hist->bins[z][v] += 1;
|
||||
}
|
||||
|
||||
p += nb;
|
||||
}
|
||||
|
||||
/* Note the maximum.
|
||||
*/
|
||||
hist->mx = mx;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Histogram of one band of a ushort image.
|
||||
*/
|
||||
static int
|
||||
vips_hist_find_ushort_extract_scan( VipsStatistic *statistic,
|
||||
void *seq, int x, int y, void *in, int n )
|
||||
{
|
||||
Histogram *hist = (Histogram *) seq;
|
||||
int mx = hist->mx;
|
||||
unsigned int *bins = hist->bins[0];
|
||||
unsigned short *p = (unsigned short *) in;
|
||||
int nb = statistic->ready->Bands;
|
||||
int max = nb * n;
|
||||
|
||||
int i;
|
||||
|
||||
for( i = hist->which; i < max; i += nb ) {
|
||||
int v = p[i];
|
||||
|
||||
/* Adjust maximum.
|
||||
*/
|
||||
if( v > mx )
|
||||
mx = v;
|
||||
|
||||
bins[v] += 1;
|
||||
}
|
||||
|
||||
/* Note the maximum.
|
||||
*/
|
||||
hist->mx = mx;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
#define SCAN1( IMAGE_TYPE, HIST_TYPE ) G_STMT_START { \
|
||||
HIST_TYPE *bins = (HIST_TYPE *) hist->bins[0]; \
|
||||
IMAGE_TYPE *p = (IMAGE_TYPE *) in; \
|
||||
int max = nb * n; \
|
||||
\
|
||||
for( i = hist->band; i < max; i += nb ) { \
|
||||
int v = p[i]; \
|
||||
\
|
||||
if( v > mx ) \
|
||||
mx = v; \
|
||||
\
|
||||
bins[v] += 1; \
|
||||
} \
|
||||
} G_STMT_END
|
||||
|
||||
static int
|
||||
vips_hist_find_scan( VipsStatistic *statistic, void *seq,
|
||||
int x, int y, void *in, int n )
|
||||
{
|
||||
VipsHistFind *hist_find = (VipsHistFind *) statistic;
|
||||
VipsStatisticScanFn scan;
|
||||
Histogram *hist = (Histogram *) seq;
|
||||
int nb = statistic->ready->Bands;
|
||||
int mx = hist->mx;
|
||||
|
||||
if( hist_find->which < 0 ) {
|
||||
if( statistic->ready->BandFmt == VIPS_FORMAT_UCHAR )
|
||||
scan = vips_hist_find_uchar_scan;
|
||||
else
|
||||
scan = vips_hist_find_ushort_scan;
|
||||
}
|
||||
else {
|
||||
if( statistic->ready->BandFmt == VIPS_FORMAT_UCHAR )
|
||||
scan = vips_hist_find_uchar_extract_scan;
|
||||
else
|
||||
scan = vips_hist_find_ushort_extract_scan;
|
||||
}
|
||||
int i;
|
||||
|
||||
return( scan( statistic, seq, x, y, in, n ) );
|
||||
if( hist_find->band < 0 )
|
||||
switch( statistic->ready->BandFmt ) {
|
||||
case VIPS_FORMAT_UCHAR:
|
||||
if( hist_find->large )
|
||||
SCAN( unsigned char, double, UCSCANOP );
|
||||
else
|
||||
SCAN( unsigned char, unsigned int, UCSCANOP );
|
||||
mx = 255;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_USHORT:
|
||||
if( hist_find->large )
|
||||
SCAN( unsigned short, double, SCANOP );
|
||||
else
|
||||
SCAN( unsigned short, unsigned int, SCANOP );
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
else
|
||||
switch( statistic->ready->BandFmt ) {
|
||||
case VIPS_FORMAT_UCHAR:
|
||||
if( hist_find->large )
|
||||
SCAN1( unsigned char, double );
|
||||
else
|
||||
SCAN1( unsigned char, unsigned int );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_USHORT:
|
||||
if( hist_find->large )
|
||||
SCAN1( unsigned short, double );
|
||||
else
|
||||
SCAN1( unsigned short, unsigned int );
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
hist->mx = mx;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Save a bit of typing.
|
||||
@ -456,7 +419,7 @@ vips_hist_find_class_init( VipsHistFindClass *class )
|
||||
_( "Band" ),
|
||||
_( "Find histogram of band" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsHistFind, which ),
|
||||
G_STRUCT_OFFSET( VipsHistFind, band ),
|
||||
-1, 100000, -1 );
|
||||
|
||||
}
|
||||
@ -464,7 +427,7 @@ vips_hist_find_class_init( VipsHistFindClass *class )
|
||||
static void
|
||||
vips_hist_find_init( VipsHistFind *hist_find )
|
||||
{
|
||||
hist_find->which = -1;
|
||||
hist_find->band = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -66,10 +66,30 @@ vips_profile_fallback_get( const char *name, size_t *length )
|
||||
|
||||
for( i = 0; (fallback = vips__profile_fallback_table[i]); i++ )
|
||||
if( g_ascii_strcasecmp( fallback->name, name ) == 0 ) {
|
||||
if( length )
|
||||
*length = fallback->length;
|
||||
void *data;
|
||||
GConverter *converter;
|
||||
GConverterResult res;
|
||||
gsize bytes_read;
|
||||
gsize bytes_written;
|
||||
|
||||
return( fallback->data );
|
||||
data = g_malloc0( fallback->length );
|
||||
converter = G_CONVERTER( g_zlib_decompressor_new(
|
||||
G_ZLIB_COMPRESSOR_FORMAT_ZLIB ) );
|
||||
|
||||
res = g_converter_convert( converter,
|
||||
fallback->data, fallback->length,
|
||||
data, fallback->length,
|
||||
G_CONVERTER_INPUT_AT_END,
|
||||
&bytes_read, &bytes_written, NULL );
|
||||
g_object_unref( converter );
|
||||
|
||||
if( res == G_CONVERTER_FINISHED ) {
|
||||
*length = fallback->length;
|
||||
return( data );
|
||||
} else {
|
||||
g_free( data );
|
||||
g_warning( "fallback profile decompression failed" );
|
||||
}
|
||||
}
|
||||
|
||||
return( NULL );
|
||||
@ -92,7 +112,8 @@ vips_profile_load_build( VipsObject *object )
|
||||
if( g_ascii_strcasecmp( load->name, "none" ) == 0 )
|
||||
profile = NULL;
|
||||
else if( (data = vips_profile_fallback_get( load->name, &length )) )
|
||||
profile = vips_blob_new( NULL, data, length );
|
||||
profile = vips_blob_new(
|
||||
(VipsCallbackFn) vips_area_free_cb, data, length );
|
||||
else if( (data = vips__file_read_name( load->name,
|
||||
vips__icc_dir(), &length )) )
|
||||
profile = vips_blob_new(
|
||||
|
113044
libvips/colour/profiles.c
113044
libvips/colour/profiles.c
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,7 @@ echo "#include \"profiles.h\"" >> $out
|
||||
echo "" >> $out
|
||||
|
||||
profile_names=
|
||||
for file in $in/*; do
|
||||
for file in $in/*.icm; do
|
||||
root=${file%.icm}
|
||||
base=${root##*/}
|
||||
profile_name=vips__profile_fallback_$base
|
||||
@ -24,7 +24,7 @@ for file in $in/*; do
|
||||
echo " \"$base\"," >> $out
|
||||
echo " $(stat --format=%s $file)," >> $out
|
||||
echo " {" >> $out
|
||||
hexdump -v -e '" 0x" 1/1 "%02X,"' $file | fmt >> $out
|
||||
pigz -c -z -11 $file | hexdump -v -e '" 0x" 1/1 "%02X,"' | fmt >> $out
|
||||
echo " }" >> $out
|
||||
echo "};" >> $out
|
||||
echo >> $out
|
||||
|
@ -67,7 +67,7 @@ typedef struct _VipsFlatten {
|
||||
*/
|
||||
VipsArrayDouble *background;
|
||||
|
||||
/* The [double] converted to the input image format.
|
||||
/* The [double] background converted to the input image format.
|
||||
*/
|
||||
VipsPel *ink;
|
||||
|
||||
|
@ -7,6 +7,8 @@
|
||||
* - max_alpha defaults to 65535 for RGB16/GREY16
|
||||
* 24/11/17 lovell
|
||||
* - match normalised alpha to output type
|
||||
* 27/2/21 jjonesrs
|
||||
* - revise range clipping and 1/x, again
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -70,7 +72,8 @@ typedef VipsConversionClass VipsUnpremultiplyClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION );
|
||||
|
||||
/* Unpremultiply an N-band image.
|
||||
/* Unpremultiply an N-band image. Don't use clip_alpha to calculate factor: we
|
||||
* want over and undershoots on alpha and RGB to cancel.
|
||||
*/
|
||||
#define UNPRE_MANY( IN, OUT ) { \
|
||||
IN * restrict p = (IN *) in; \
|
||||
@ -78,18 +81,11 @@ G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION );
|
||||
\
|
||||
for( x = 0; x < width; x++ ) { \
|
||||
IN alpha = p[alpha_band]; \
|
||||
OUT factor = alpha == 0 ? 0 : max_alpha / alpha; \
|
||||
\
|
||||
if( alpha != 0 ) { \
|
||||
OUT factor = max_alpha / alpha; \
|
||||
\
|
||||
for( i = 0; i < alpha_band; i++ ) \
|
||||
q[i] = factor * p[i]; \
|
||||
q[alpha_band] = alpha; \
|
||||
} \
|
||||
else \
|
||||
for( i = 0; i < alpha_band + 1; i++ ) \
|
||||
q[i] = 0; \
|
||||
\
|
||||
for( i = 0; i < alpha_band; i++ ) \
|
||||
q[i] = factor * p[i]; \
|
||||
q[alpha_band] = VIPS_CLIP( 0, alpha, max_alpha ); \
|
||||
for( i = alpha_band + 1; i < bands; i++ ) \
|
||||
q[i] = p[i]; \
|
||||
\
|
||||
@ -106,21 +102,12 @@ G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION );
|
||||
\
|
||||
for( x = 0; x < width; x++ ) { \
|
||||
IN alpha = p[3]; \
|
||||
OUT factor = alpha == 0 ? 0 : max_alpha / alpha; \
|
||||
\
|
||||
if( alpha != 0 ) { \
|
||||
OUT factor = max_alpha / alpha; \
|
||||
\
|
||||
q[0] = factor * p[0]; \
|
||||
q[1] = factor * p[1]; \
|
||||
q[2] = factor * p[2]; \
|
||||
q[3] = alpha; \
|
||||
} \
|
||||
else { \
|
||||
q[0] = 0; \
|
||||
q[1] = 0; \
|
||||
q[2] = 0; \
|
||||
q[3] = 0; \
|
||||
} \
|
||||
q[0] = factor * p[0]; \
|
||||
q[1] = factor * p[1]; \
|
||||
q[2] = factor * p[2]; \
|
||||
q[3] = VIPS_CLIP( 0, alpha, max_alpha ); \
|
||||
\
|
||||
p += 4; \
|
||||
q += 4; \
|
||||
|
@ -616,11 +616,22 @@ vips_foreign_load_csv_source_build( VipsObject *object )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static gboolean
|
||||
vips_foreign_load_csv_source_is_a_source( VipsSource *source )
|
||||
{
|
||||
/* Detecting CSV files automatically is tricky. Define this method to
|
||||
* prevent a warning, but users will need to run the csv loader
|
||||
* explicitly.
|
||||
*/
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_csv_source_class_init( VipsForeignLoadCsvFileClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
@ -628,6 +639,8 @@ vips_foreign_load_csv_source_class_init( VipsForeignLoadCsvFileClass *class )
|
||||
object_class->nickname = "csvload_source";
|
||||
object_class->build = vips_foreign_load_csv_source_build;
|
||||
|
||||
load_class->is_a_source = vips_foreign_load_csv_source_is_a_source;
|
||||
|
||||
VIPS_ARG_OBJECT( class, "source", 1,
|
||||
_( "Source" ),
|
||||
_( "Source to load from" ),
|
||||
|
@ -845,21 +845,16 @@ vips_exif_set_string_encoding( ExifData *ed,
|
||||
ExifEntry *entry, unsigned long component, const char *data )
|
||||
{
|
||||
char *str;
|
||||
char *ascii;
|
||||
int len;
|
||||
|
||||
str = drop_tail( data );
|
||||
|
||||
/* libexif can only really save ASCII to things like UserComment.
|
||||
*/
|
||||
#ifdef HAVE_G_STR_TO_ASCII
|
||||
{
|
||||
char *ascii;
|
||||
|
||||
ascii = g_str_to_ascii( str, NULL );
|
||||
g_free( str );
|
||||
str = ascii;
|
||||
}
|
||||
#endif /*HAVE_G_STR_TO_ASCII*/
|
||||
|
||||
/* libexif comment strings are not NULL-terminated, and have an
|
||||
* encoding tag (always ASCII) in the first 8 bytes.
|
||||
@ -880,21 +875,16 @@ vips_exif_set_string_ascii( ExifData *ed,
|
||||
ExifEntry *entry, unsigned long component, const char *data )
|
||||
{
|
||||
char *str;
|
||||
char *ascii;
|
||||
int len;
|
||||
|
||||
str = drop_tail( data );
|
||||
|
||||
/* libexif can only really save ASCII to things like UserComment.
|
||||
*/
|
||||
#ifdef HAVE_G_STR_TO_ASCII
|
||||
{
|
||||
char *ascii;
|
||||
|
||||
ascii = g_str_to_ascii( str, NULL );
|
||||
g_free( str );
|
||||
str = ascii;
|
||||
}
|
||||
#endif /*HAVE_G_STR_TO_ASCII*/
|
||||
|
||||
/* ASCII strings are NULL-terminated.
|
||||
*/
|
||||
|
@ -217,7 +217,9 @@ vips_fits_get_header( VipsFits *fits, VipsImage *out )
|
||||
int status;
|
||||
int bitpix;
|
||||
|
||||
int width, height, bands, format, type;
|
||||
int width, height, bands;
|
||||
VipsBandFormat format;
|
||||
VipsInterpretation interpretation;
|
||||
int keysexist;
|
||||
int i;
|
||||
|
||||
@ -318,24 +320,24 @@ vips_fits_get_header( VipsFits *fits, VipsImage *out )
|
||||
|
||||
if( bands == 1 ) {
|
||||
if( format == VIPS_FORMAT_USHORT )
|
||||
type = VIPS_INTERPRETATION_GREY16;
|
||||
interpretation = VIPS_INTERPRETATION_GREY16;
|
||||
else
|
||||
type = VIPS_INTERPRETATION_B_W;
|
||||
interpretation = VIPS_INTERPRETATION_B_W;
|
||||
}
|
||||
else if( bands == 3 ) {
|
||||
if( format == VIPS_FORMAT_USHORT )
|
||||
type = VIPS_INTERPRETATION_RGB16;
|
||||
interpretation = VIPS_INTERPRETATION_RGB16;
|
||||
else
|
||||
type = VIPS_INTERPRETATION_sRGB;
|
||||
interpretation = VIPS_INTERPRETATION_sRGB;
|
||||
}
|
||||
else
|
||||
type = VIPS_INTERPRETATION_MULTIBAND;
|
||||
interpretation = VIPS_INTERPRETATION_MULTIBAND;
|
||||
|
||||
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL );
|
||||
vips_image_init_fields( out,
|
||||
width, height, bands,
|
||||
format,
|
||||
VIPS_CODING_NONE, type, 1.0, 1.0 );
|
||||
VIPS_CODING_NONE, interpretation, 1.0, 1.0 );
|
||||
|
||||
/* Read all keys into meta.
|
||||
*/
|
||||
@ -517,7 +519,8 @@ int
|
||||
vips__fits_read( const char *filename, VipsImage *out )
|
||||
{
|
||||
VipsImage *t;
|
||||
int n_bands;
|
||||
int bands;
|
||||
VipsInterpretation interpretation;
|
||||
|
||||
VIPS_DEBUG_MSG( "fits2vips: reading \"%s\"\n", filename );
|
||||
|
||||
@ -531,22 +534,26 @@ vips__fits_read( const char *filename, VipsImage *out )
|
||||
g_object_unref( t );
|
||||
return( -1 );
|
||||
}
|
||||
n_bands = t->Bands;
|
||||
bands = t->Bands;
|
||||
interpretation = t->Type;
|
||||
g_object_unref( t );
|
||||
|
||||
if( n_bands == 1 ) {
|
||||
if( bands == 1 ) {
|
||||
if( fits2vips( filename, out, 0 ) )
|
||||
return( -1 );
|
||||
}
|
||||
else {
|
||||
VipsImage **x;
|
||||
VipsImage **y;
|
||||
int i;
|
||||
|
||||
t = vips_image_new();
|
||||
x = (VipsImage **) vips_object_local_array( VIPS_OBJECT( t ),
|
||||
n_bands + 1 );
|
||||
bands );
|
||||
y = (VipsImage **) vips_object_local_array( VIPS_OBJECT( t ),
|
||||
3 );
|
||||
|
||||
for( i = 0; i < n_bands; i++ ) {
|
||||
for( i = 0; i < bands; i++ ) {
|
||||
x[i] = vips_image_new();
|
||||
if( fits2vips( filename, x[i], i ) ) {
|
||||
g_object_unref( t );
|
||||
@ -554,8 +561,11 @@ vips__fits_read( const char *filename, VipsImage *out )
|
||||
}
|
||||
}
|
||||
|
||||
if( vips_bandjoin( x, &x[n_bands], n_bands, NULL ) ||
|
||||
vips_image_write( x[n_bands], out ) ) {
|
||||
if( vips_bandjoin( x, &y[0], bands, NULL ) ||
|
||||
vips_copy( y[0], &y[1],
|
||||
"interpretation", interpretation,
|
||||
NULL ) ||
|
||||
vips_image_write( y[1], out ) ) {
|
||||
g_object_unref( t );
|
||||
return( -1 );
|
||||
}
|
||||
|
@ -55,17 +55,85 @@
|
||||
typedef struct _VipsForeignLoadFits {
|
||||
VipsForeignLoad parent_object;
|
||||
|
||||
/* Filename for load.
|
||||
/* Set by subclasses.
|
||||
*/
|
||||
char *filename;
|
||||
VipsSource *source;
|
||||
|
||||
/* Filename from source.
|
||||
*/
|
||||
const char *filename;
|
||||
|
||||
} VipsForeignLoadFits;
|
||||
|
||||
typedef VipsForeignLoadClass VipsForeignLoadFitsClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadFits, vips_foreign_load_fits,
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadFits, vips_foreign_load_fits,
|
||||
VIPS_TYPE_FOREIGN_LOAD );
|
||||
|
||||
static void
|
||||
vips_foreign_load_fits_dispose( GObject *gobject )
|
||||
{
|
||||
VipsForeignLoadFits *fits = (VipsForeignLoadFits *) gobject;
|
||||
|
||||
VIPS_UNREF( fits->source );
|
||||
|
||||
G_OBJECT_CLASS( vips_foreign_load_fits_parent_class )->
|
||||
dispose( gobject );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_fits_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignLoadFits *fits =
|
||||
(VipsForeignLoadFits *) object;
|
||||
|
||||
/* We can only open source which have an associated filename, since
|
||||
* the fits library works in terms of filenames.
|
||||
*/
|
||||
if( fits->source ) {
|
||||
fits->filename = vips_connection_filename( VIPS_CONNECTION(
|
||||
fits->source ) );
|
||||
if( !fits->filename ) {
|
||||
vips_error( class->nickname, "%s",
|
||||
_( "no filename available" ) );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_load_fits_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static VipsForeignFlags
|
||||
vips_foreign_load_fits_get_flags_source( VipsSource *source )
|
||||
{
|
||||
return( VIPS_FOREIGN_PARTIAL );
|
||||
}
|
||||
|
||||
static VipsForeignFlags
|
||||
vips_foreign_load_fits_get_flags( VipsForeignLoad *load )
|
||||
{
|
||||
return( VIPS_FOREIGN_PARTIAL );
|
||||
}
|
||||
|
||||
static VipsForeignFlags
|
||||
vips_foreign_load_fits_get_flags_filename( const char *filename )
|
||||
{
|
||||
VipsSource *source;
|
||||
VipsForeignFlags flags;
|
||||
|
||||
if( !(source = vips_source_new_from_file( filename )) )
|
||||
return( 0 );
|
||||
flags = vips_foreign_load_fits_get_flags_source( source );
|
||||
VIPS_UNREF( source );
|
||||
|
||||
return( flags );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_fits_header( VipsForeignLoad *load )
|
||||
{
|
||||
@ -103,32 +171,167 @@ vips_foreign_load_fits_class_init( VipsForeignLoadFitsClass *class )
|
||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->dispose = vips_foreign_load_fits_dispose;
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "fitsload";
|
||||
object_class->description = _( "load a FITS image" );
|
||||
object_class->nickname = "fitsload_base";
|
||||
object_class->description = _( "FITS loader base class" );
|
||||
object_class->build = vips_foreign_load_fits_build;
|
||||
|
||||
/* is_a() is not that quick ... lower the priority.
|
||||
*/
|
||||
foreign_class->priority = -50;
|
||||
|
||||
foreign_class->suffs = vips__fits_suffs;
|
||||
|
||||
load_class->get_flags_filename =
|
||||
vips_foreign_load_fits_get_flags_filename;
|
||||
load_class->get_flags = vips_foreign_load_fits_get_flags;
|
||||
load_class->is_a = vips__fits_isfits;
|
||||
load_class->header = vips_foreign_load_fits_header;
|
||||
load_class->load = vips_foreign_load_fits_load;
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_fits_init( VipsForeignLoadFits *fits )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadFitsFile {
|
||||
VipsForeignLoadFits parent_object;
|
||||
|
||||
/* Filename for load.
|
||||
*/
|
||||
char *filename;
|
||||
|
||||
} VipsForeignLoadFitsFile;
|
||||
|
||||
typedef VipsForeignLoadFitsClass VipsForeignLoadFitsFileClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadFitsFile, vips_foreign_load_fits_file,
|
||||
vips_foreign_load_fits_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_load_fits_file_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignLoadFits *fits = (VipsForeignLoadFits *) object;
|
||||
VipsForeignLoadFitsFile *file = (VipsForeignLoadFitsFile *) object;
|
||||
|
||||
if( file->filename &&
|
||||
!(fits->source = vips_source_new_from_file( file->filename )) )
|
||||
return( -1 );
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_load_fits_file_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_fits_file_class_init( VipsForeignLoadFitsFileClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "fitsload";
|
||||
object_class->description = _( "load a FITS image" );
|
||||
object_class->build = vips_foreign_load_fits_file_build;
|
||||
|
||||
foreign_class->suffs = vips__fits_suffs;
|
||||
|
||||
load_class->is_a = vips__fits_isfits;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "Filename to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadFits, filename ),
|
||||
G_STRUCT_OFFSET( VipsForeignLoadFitsFile, filename ),
|
||||
NULL );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_fits_init( VipsForeignLoadFits *fits )
|
||||
vips_foreign_load_fits_file_init( VipsForeignLoadFitsFile *file )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadFitsSource {
|
||||
VipsForeignLoadFits parent_object;
|
||||
|
||||
/* Load from a source.
|
||||
*/
|
||||
VipsSource *source;
|
||||
|
||||
} VipsForeignLoadFitsSource;
|
||||
|
||||
typedef VipsForeignLoadFitsClass VipsForeignLoadFitsSourceClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadFitsSource, vips_foreign_load_fits_source,
|
||||
vips_foreign_load_fits_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_load_fits_source_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignLoadFits *fits = (VipsForeignLoadFits *) object;
|
||||
VipsForeignLoadFitsSource *source =
|
||||
(VipsForeignLoadFitsSource *) object;
|
||||
|
||||
if( source->source ) {
|
||||
fits->source = source->source;
|
||||
g_object_ref( fits->source );
|
||||
}
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_load_fits_source_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static gboolean
|
||||
vips_foreign_load_fits_source_is_a_source( VipsSource *source )
|
||||
{
|
||||
const char *filename;
|
||||
|
||||
return( (filename =
|
||||
vips_connection_filename( VIPS_CONNECTION( source ) )) &&
|
||||
vips__fits_isfits( filename ) );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_fits_source_class_init(
|
||||
VipsForeignLoadFitsSourceClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "fitsload_source";
|
||||
object_class->description = _( "load FITS from a source" );
|
||||
object_class->build = vips_foreign_load_fits_source_build;
|
||||
|
||||
load_class->is_a_source =
|
||||
vips_foreign_load_fits_source_is_a_source;
|
||||
|
||||
VIPS_ARG_OBJECT( class, "source", 1,
|
||||
_( "Source" ),
|
||||
_( "Source to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadFitsSource, source ),
|
||||
VIPS_TYPE_SOURCE );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_fits_source_init( VipsForeignLoadFitsSource *fits )
|
||||
{
|
||||
}
|
||||
|
||||
@ -166,3 +369,26 @@ vips_fitsload( const char *filename, VipsImage **out, ... )
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_fitsload_source:
|
||||
* @source: source to load from
|
||||
* @out: (out): decompressed image
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Exactly as vips_fitsload(), but read from a source.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_fitsload_source( VipsSource *source, VipsImage **out, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, out );
|
||||
result = vips_call_split( "fitsload_source", ap, source, out );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
@ -1395,12 +1395,15 @@ vips__foreign_convert_saveable( VipsImage *in, VipsImage **ready,
|
||||
in = out;
|
||||
}
|
||||
|
||||
/* If this is something other than CMYK or RAD, eg. maybe a LAB image,
|
||||
* we need to transform to RGB.
|
||||
/* If this is something other than CMYK or RAD, and it's not already
|
||||
* an RGB image, eg. maybe a LAB or scRGB image, we need to transform
|
||||
* to RGB.
|
||||
*/
|
||||
if( !coding[VIPS_CODING_RAD] &&
|
||||
in->Bands >= 3 &&
|
||||
in->Type != VIPS_INTERPRETATION_CMYK &&
|
||||
in->Type != VIPS_INTERPRETATION_sRGB &&
|
||||
in->Type != VIPS_INTERPRETATION_RGB16 &&
|
||||
vips_colourspace_issupported( in ) &&
|
||||
(saveable == VIPS_SAVEABLE_RGB ||
|
||||
saveable == VIPS_SAVEABLE_RGBA ||
|
||||
@ -1426,7 +1429,7 @@ vips__foreign_convert_saveable( VipsImage *in, VipsImage **ready,
|
||||
in = out;
|
||||
}
|
||||
|
||||
/* VIPS_SAVEABLE_RGBA_ONLY does not support 1 or 2 bands ... convert
|
||||
/* VIPS_SAVEABLE_RGBA_ONLY does not support mono types ... convert
|
||||
* to sRGB.
|
||||
*/
|
||||
if( !coding[VIPS_CODING_RAD] &&
|
||||
@ -2114,7 +2117,8 @@ vips_foreign_operation_init( void )
|
||||
extern GType vips_foreign_save_matrix_target_get_type( void );
|
||||
extern GType vips_foreign_print_matrix_get_type( void );
|
||||
|
||||
extern GType vips_foreign_load_fits_get_type( void );
|
||||
extern GType vips_foreign_load_fits_file_get_type( void );
|
||||
extern GType vips_foreign_load_fits_source_get_type( void );
|
||||
extern GType vips_foreign_save_fits_get_type( void );
|
||||
|
||||
extern GType vips_foreign_load_analyze_get_type( void );
|
||||
@ -2179,7 +2183,8 @@ vips_foreign_operation_init( void )
|
||||
extern GType vips_foreign_save_heif_buffer_get_type( void );
|
||||
extern GType vips_foreign_save_heif_target_get_type( void );
|
||||
|
||||
extern GType vips_foreign_load_nifti_get_type( void );
|
||||
extern GType vips_foreign_load_nifti_file_get_type( void );
|
||||
extern GType vips_foreign_load_nifti_source_get_type( void );
|
||||
extern GType vips_foreign_save_nifti_get_type( void );
|
||||
|
||||
extern GType vips_foreign_load_gif_file_get_type( void );
|
||||
@ -2321,7 +2326,8 @@ vips_foreign_operation_init( void )
|
||||
#endif /*ENABLE_MAGICKSAVE*/
|
||||
|
||||
#ifdef HAVE_CFITSIO
|
||||
vips_foreign_load_fits_get_type();
|
||||
vips_foreign_load_fits_file_get_type();
|
||||
vips_foreign_load_fits_source_get_type();
|
||||
vips_foreign_save_fits_get_type();
|
||||
#endif /*HAVE_CFITSIO*/
|
||||
|
||||
@ -2330,7 +2336,8 @@ vips_foreign_operation_init( void )
|
||||
#endif /*HAVE_OPENEXR*/
|
||||
|
||||
#ifdef HAVE_NIFTI
|
||||
vips_foreign_load_nifti_get_type();
|
||||
vips_foreign_load_nifti_file_get_type();
|
||||
vips_foreign_load_nifti_source_get_type();
|
||||
vips_foreign_save_nifti_get_type();
|
||||
#endif /*HAVE_NIFTI*/
|
||||
|
||||
|
@ -42,6 +42,8 @@
|
||||
* 2/7/20
|
||||
* - clip out of bounds images against canvas
|
||||
* - fix PREVIOUS handling, again
|
||||
* 19/2/21 781545872
|
||||
* - read out background, if we can
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -701,6 +703,7 @@ static int
|
||||
vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image )
|
||||
{
|
||||
const gint64 total_height = (gint64) gif->file->SHeight * gif->n;
|
||||
ColorMapObject *map = gif->file->SColorMap;
|
||||
|
||||
if( total_height <= 0 ||
|
||||
total_height > VIPS_MAX_COORD ) {
|
||||
@ -745,6 +748,18 @@ vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image )
|
||||
if( gif->comment )
|
||||
vips_image_set_string( image, "gif-comment", gif->comment );
|
||||
|
||||
if( map &&
|
||||
gif->file->SBackGroundColor >= 0 &&
|
||||
gif->file->SBackGroundColor < map->ColorCount ) {
|
||||
double array[3];
|
||||
|
||||
array[0] = map->Colors[gif->file->SBackGroundColor].Red;
|
||||
array[1] = map->Colors[gif->file->SBackGroundColor].Green;
|
||||
array[2] = map->Colors[gif->file->SBackGroundColor].Blue;
|
||||
|
||||
vips_image_set_array_double( image, "background", array, 3 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,10 @@ typedef struct _VipsForeignSaveHeif {
|
||||
*/
|
||||
int speed;
|
||||
|
||||
/* Chroma subsampling.
|
||||
*/
|
||||
VipsForeignSubsample subsample_mode;
|
||||
|
||||
/* The image we save. This is a copy of save->ready since we need to
|
||||
* be able to update the metadata.
|
||||
*/
|
||||
@ -319,6 +323,7 @@ vips_foreign_save_heif_build( VipsObject *object )
|
||||
|
||||
struct heif_error error;
|
||||
struct heif_writer writer;
|
||||
char *chroma;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_parent_class )->
|
||||
build( object ) )
|
||||
@ -363,6 +368,17 @@ vips_foreign_save_heif_build( VipsObject *object )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
chroma = heif->subsample_mode == VIPS_FOREIGN_SUBSAMPLE_OFF ||
|
||||
( heif->subsample_mode == VIPS_FOREIGN_SUBSAMPLE_AUTO &&
|
||||
heif->Q > 90 ) ? "444" : "420";
|
||||
error = heif_encoder_set_parameter_string( heif->encoder,
|
||||
"chroma", chroma );
|
||||
if( error.code &&
|
||||
error.subcode != heif_suberror_Unsupported_parameter ) {
|
||||
vips__heif_error( &error );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* TODO .. support extra per-encoder params with
|
||||
* heif_encoder_list_parameters().
|
||||
*/
|
||||
@ -487,6 +503,13 @@ vips_foreign_save_heif_class_init( VipsForeignSaveHeifClass *class )
|
||||
G_STRUCT_OFFSET( VipsForeignSaveHeif, speed ),
|
||||
0, 8, 5 );
|
||||
|
||||
VIPS_ARG_ENUM( class, "subsample_mode", 16,
|
||||
_( "Subsample mode" ),
|
||||
_( "Select chroma subsample operation mode" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveHeif, subsample_mode ),
|
||||
VIPS_TYPE_FOREIGN_SUBSAMPLE,
|
||||
VIPS_FOREIGN_SUBSAMPLE_AUTO );
|
||||
}
|
||||
|
||||
static void
|
||||
@ -496,6 +519,7 @@ vips_foreign_save_heif_init( VipsForeignSaveHeif *heif )
|
||||
heif->Q = 50;
|
||||
heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_HEVC;
|
||||
heif->speed = 5;
|
||||
heif->subsample_mode = VIPS_FOREIGN_SUBSAMPLE_AUTO;
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveHeifFile {
|
||||
@ -689,6 +713,7 @@ vips_foreign_save_heif_target_init( VipsForeignSaveHeifTarget *target )
|
||||
* * @lossless: %gboolean, enable lossless encoding
|
||||
* * @compression: #VipsForeignHeifCompression, write with this compression
|
||||
* * @speed: %gint, CPU effort, 0 slowest - 8 fastest, AV1 compression only
|
||||
* * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode
|
||||
*
|
||||
* Write a VIPS image to a file in HEIF format.
|
||||
*
|
||||
@ -702,6 +727,9 @@ vips_foreign_save_heif_target_init( VipsForeignSaveHeifTarget *target )
|
||||
* Use @speed to control the CPU effort spent improving compression.
|
||||
* This is currently only applicable to AV1 encoders, defaults to 5.
|
||||
*
|
||||
* Chroma subsampling is normally automatically disabled for Q > 90. You can
|
||||
* force the subsampling mode with @subsample_mode.
|
||||
*
|
||||
* See also: vips_image_write_to_file(), vips_heifload().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
@ -732,6 +760,7 @@ vips_heifsave( VipsImage *in, const char *filename, ... )
|
||||
* * @lossless: %gboolean, enable lossless encoding
|
||||
* * @compression: #VipsForeignHeifCompression, write with this compression
|
||||
* * @speed: %gint, CPU effort, 0 slowest - 8 fastest, AV1 compression only
|
||||
* * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode
|
||||
*
|
||||
* As vips_heifsave(), but save to a memory buffer.
|
||||
*
|
||||
@ -783,6 +812,7 @@ vips_heifsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
* * @lossless: %gboolean, enable lossless encoding
|
||||
* * @compression: #VipsForeignHeifCompression, write with this compression
|
||||
* * @speed: %gint, CPU effort, 0 slowest - 8 fastest, AV1 compression only
|
||||
* * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode
|
||||
*
|
||||
* As vips_heifsave(), but save to a target.
|
||||
*
|
||||
|
@ -84,7 +84,7 @@ typedef struct _VipsForeignSaveJpeg {
|
||||
* on will always enable subsampling
|
||||
* off will always disable subsampling
|
||||
*/
|
||||
VipsForeignJpegSubsample subsample_mode;
|
||||
VipsForeignSubsample subsample_mode;
|
||||
|
||||
/* Apply trellis quantisation to each 8x8 block.
|
||||
*/
|
||||
@ -133,8 +133,8 @@ vips_foreign_save_jpeg_build( VipsObject *object )
|
||||
*/
|
||||
if( vips_object_argument_isset( object, "no_subsample" ) )
|
||||
jpeg->subsample_mode = jpeg->no_subsample ?
|
||||
VIPS_FOREIGN_JPEG_SUBSAMPLE_OFF :
|
||||
VIPS_FOREIGN_JPEG_SUBSAMPLE_AUTO;
|
||||
VIPS_FOREIGN_SUBSAMPLE_OFF :
|
||||
VIPS_FOREIGN_SUBSAMPLE_AUTO;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -229,15 +229,15 @@ vips_foreign_save_jpeg_class_init( VipsForeignSaveJpegClass *class )
|
||||
_( "Select chroma subsample operation mode" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveJpeg, subsample_mode ),
|
||||
VIPS_TYPE_FOREIGN_JPEG_SUBSAMPLE,
|
||||
VIPS_FOREIGN_JPEG_SUBSAMPLE_AUTO );
|
||||
VIPS_TYPE_FOREIGN_SUBSAMPLE,
|
||||
VIPS_FOREIGN_SUBSAMPLE_AUTO );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_jpeg_init( VipsForeignSaveJpeg *jpeg )
|
||||
{
|
||||
jpeg->Q = 75;
|
||||
jpeg->subsample_mode = VIPS_FOREIGN_JPEG_SUBSAMPLE_AUTO;
|
||||
jpeg->subsample_mode = VIPS_FOREIGN_SUBSAMPLE_AUTO;
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveJpegTarget {
|
||||
@ -529,7 +529,7 @@ vips_foreign_save_jpeg_mime_init( VipsForeignSaveJpegMime *mime )
|
||||
* * @optimize_coding: %gboolean, compute optimal Huffman coding tables
|
||||
* * @interlace: %gboolean, write an interlaced (progressive) jpeg
|
||||
* * @strip: %gboolean, remove all metadata from image
|
||||
* * @subsample_mode: #VipsForeignJpegSubsample, chroma subsampling mode
|
||||
* * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode
|
||||
* * @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
|
||||
@ -558,7 +558,7 @@ vips_foreign_save_jpeg_mime_init( VipsForeignSaveJpegMime *mime )
|
||||
* written into the output file.
|
||||
*
|
||||
* Chroma subsampling is normally automatically disabled for Q > 90. You can
|
||||
* force the subsampling mode with @@subsample_mode.
|
||||
* force the subsampling mode with @subsample_mode.
|
||||
*
|
||||
* If @trellis_quant is set and the version of libjpeg supports it
|
||||
* (e.g. mozjpeg >= 3.0), apply trellis quantisation to each 8x8 block.
|
||||
@ -645,7 +645,7 @@ vips_jpegsave( VipsImage *in, const char *filename, ... )
|
||||
* * @optimize_coding: %gboolean, compute optimal Huffman coding tables
|
||||
* * @interlace: %gboolean, write an interlaced (progressive) jpeg
|
||||
* * @strip: %gboolean, remove all metadata from image
|
||||
* * @subsample_mode: #VipsForeignJpegSubsample, chroma subsampling mode
|
||||
* * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode
|
||||
* * @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
|
||||
@ -684,7 +684,7 @@ vips_jpegsave_target( VipsImage *in, VipsTarget *target, ... )
|
||||
* * @optimize_coding: %gboolean, compute optimal Huffman coding tables
|
||||
* * @interlace: %gboolean, write an interlaced (progressive) jpeg
|
||||
* * @strip: %gboolean, remove all metadata from image
|
||||
* * @subsample_mode: #VipsForeignJpegSubsample, chroma subsampling mode
|
||||
* * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode
|
||||
* * @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
|
||||
@ -740,7 +740,7 @@ vips_jpegsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
* * @optimize_coding: %gboolean, compute optimal Huffman coding tables
|
||||
* * @interlace: %gboolean, write an interlaced (progressive) jpeg
|
||||
* * @strip: %gboolean, remove all metadata from image
|
||||
* * @subsample_mode: #VipsForeignJpegSubsample, chroma subsampling mode
|
||||
* * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode
|
||||
* * @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
|
||||
|
@ -74,9 +74,13 @@
|
||||
typedef struct _VipsForeignLoadNifti {
|
||||
VipsForeignLoad parent_object;
|
||||
|
||||
/* Filename for load.
|
||||
/* Source to load from (set by subclasses).
|
||||
*/
|
||||
char *filename;
|
||||
VipsSource *source;
|
||||
|
||||
/* Filename from source.
|
||||
*/
|
||||
const char *filename;
|
||||
|
||||
/* The NIFTI image loaded to memory.
|
||||
*/
|
||||
@ -99,6 +103,7 @@ vips_foreign_load_nifti_dispose( GObject *gobject )
|
||||
{
|
||||
VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) gobject;
|
||||
|
||||
VIPS_UNREF( nifti->source );
|
||||
VIPS_UNREF( nifti->memory );
|
||||
VIPS_FREEF( nifti_image_free, nifti->nim );
|
||||
|
||||
@ -107,38 +112,27 @@ vips_foreign_load_nifti_dispose( GObject *gobject )
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_nifti_is_a( const char *filename )
|
||||
vips_foreign_load_nifti_build( VipsObject *object )
|
||||
{
|
||||
char *hfile;
|
||||
znzFile fp;
|
||||
nifti_1_header nhdr;
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) object;
|
||||
|
||||
/* Unfortunately is_nifti_file() is very slow and produces lots of
|
||||
* output. We have to make our own.
|
||||
/* We can only open source which have an associated filename, since
|
||||
* the nifti library works in terms of filenames.
|
||||
*/
|
||||
|
||||
if( !(hfile = nifti_findhdrname( filename )) )
|
||||
return( 0 );
|
||||
|
||||
fp = znzopen( hfile, "rb", nifti_is_gzfile( hfile ));
|
||||
if( znz_isnull( fp ) ) {
|
||||
free( hfile );
|
||||
return( 0 );
|
||||
if( nifti->source ) {
|
||||
nifti->filename = vips_connection_filename( VIPS_CONNECTION(
|
||||
nifti->source ) );
|
||||
if( !nifti->filename ) {
|
||||
vips_error( class->nickname, "%s",
|
||||
_( "no filename available" ) );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
free( hfile );
|
||||
|
||||
(void) znzread( &nhdr, 1, sizeof( nhdr ), fp );
|
||||
|
||||
znzclose( fp );
|
||||
|
||||
/* Test for sanity both ways around. There's a thing to test for byte
|
||||
* order in niftilib, but it's static :(
|
||||
*/
|
||||
if( nifti_hdr_looks_good( &nhdr ) )
|
||||
return( 1 );
|
||||
swap_nifti_header( &nhdr, FALSE );
|
||||
if( nifti_hdr_looks_good( &nhdr ) )
|
||||
return( 1 );
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_load_nifti_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -576,14 +570,6 @@ vips_foreign_load_nifti_load( VipsForeignLoad *load )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
const char *vips__nifti_suffs[] = {
|
||||
".nii", ".nii.gz",
|
||||
".hdr", ".hdr.gz",
|
||||
".img", ".img.gz",
|
||||
".nia", ".nia.gz",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void
|
||||
vips_foreign_load_nifti_class_init( VipsForeignLoadNiftiClass *class )
|
||||
{
|
||||
@ -596,29 +582,208 @@ vips_foreign_load_nifti_class_init( VipsForeignLoadNiftiClass *class )
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "niftiload";
|
||||
object_class->nickname = "niftiload_base";
|
||||
object_class->description = _( "load a NIFTI image" );
|
||||
object_class->build = vips_foreign_load_nifti_build;
|
||||
|
||||
/* is_a() is not that quick ... lower the priority.
|
||||
*/
|
||||
foreign_class->priority = -50;
|
||||
|
||||
foreign_class->suffs = vips__nifti_suffs;
|
||||
|
||||
load_class->is_a = vips_foreign_load_nifti_is_a;
|
||||
load_class->header = vips_foreign_load_nifti_header;
|
||||
load_class->load = vips_foreign_load_nifti_load;
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_nifti_init( VipsForeignLoadNifti *nifti )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadNiftiFile {
|
||||
VipsForeignLoadNifti parent_object;
|
||||
|
||||
/* Filename for load.
|
||||
*/
|
||||
char *filename;
|
||||
|
||||
} VipsForeignLoadNiftiFile;
|
||||
|
||||
typedef VipsForeignLoadNiftiClass VipsForeignLoadNiftiFileClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadNiftiFile, vips_foreign_load_nifti_file,
|
||||
vips_foreign_load_nifti_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_load_nifti_file_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) object;
|
||||
VipsForeignLoadNiftiFile *file = (VipsForeignLoadNiftiFile *) object;
|
||||
|
||||
if( file->filename &&
|
||||
!(nifti->source =
|
||||
vips_source_new_from_file( file->filename )) )
|
||||
return( -1 );
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_load_nifti_file_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
const char *vips_foreign_nifti_suffs[] = {
|
||||
".nii", ".nii.gz",
|
||||
".hdr", ".hdr.gz",
|
||||
".img", ".img.gz",
|
||||
".nia", ".nia.gz",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int
|
||||
vips_foreign_load_nifti_is_a( const char *filename )
|
||||
{
|
||||
char *hfile;
|
||||
znzFile fp;
|
||||
nifti_1_header nhdr;
|
||||
|
||||
/* Unfortunately is_nifti_file() is very slow and produces lots of
|
||||
* output. We have to make our own.
|
||||
*/
|
||||
|
||||
if( !(hfile = nifti_findhdrname( filename )) )
|
||||
return( 0 );
|
||||
|
||||
fp = znzopen( hfile, "rb", nifti_is_gzfile( hfile ));
|
||||
if( znz_isnull( fp ) ) {
|
||||
free( hfile );
|
||||
return( 0 );
|
||||
}
|
||||
free( hfile );
|
||||
|
||||
(void) znzread( &nhdr, 1, sizeof( nhdr ), fp );
|
||||
|
||||
znzclose( fp );
|
||||
|
||||
/* Test for sanity both ways around. There's a thing to test for byte
|
||||
* order in niftilib, but it's static :(
|
||||
*/
|
||||
if( nifti_hdr_looks_good( &nhdr ) )
|
||||
return( 1 );
|
||||
swap_nifti_header( &nhdr, FALSE );
|
||||
if( nifti_hdr_looks_good( &nhdr ) )
|
||||
return( 1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_nifti_file_class_init(
|
||||
VipsForeignLoadNiftiFileClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "niftiload";
|
||||
object_class->description = _( "load NIfTI volume" );
|
||||
object_class->build = vips_foreign_load_nifti_file_build;
|
||||
|
||||
foreign_class->suffs = vips_foreign_nifti_suffs;
|
||||
|
||||
load_class->is_a = vips_foreign_load_nifti_is_a;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "Filename to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadNifti, filename ),
|
||||
G_STRUCT_OFFSET( VipsForeignLoadNiftiFile, filename ),
|
||||
NULL );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_nifti_init( VipsForeignLoadNifti *nifti )
|
||||
vips_foreign_load_nifti_file_init( VipsForeignLoadNiftiFile *nifti )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadNiftiSource {
|
||||
VipsForeignLoadNifti parent_object;
|
||||
|
||||
/* Load from a source.
|
||||
*/
|
||||
VipsSource *source;
|
||||
|
||||
} VipsForeignLoadNiftiSource;
|
||||
|
||||
typedef VipsForeignLoadNiftiClass VipsForeignLoadNiftiSourceClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadNiftiSource, vips_foreign_load_nifti_source,
|
||||
vips_foreign_load_nifti_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_load_nifti_source_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) object;
|
||||
VipsForeignLoadNiftiSource *source =
|
||||
(VipsForeignLoadNiftiSource *) object;
|
||||
|
||||
if( source->source ) {
|
||||
nifti->source = source->source;
|
||||
g_object_ref( nifti->source );
|
||||
}
|
||||
|
||||
if( VIPS_OBJECT_CLASS(
|
||||
vips_foreign_load_nifti_source_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static gboolean
|
||||
vips_foreign_load_nifti_source_is_a_source( VipsSource *source )
|
||||
{
|
||||
const char *filename;
|
||||
|
||||
return( (filename =
|
||||
vips_connection_filename( VIPS_CONNECTION( source ) )) &&
|
||||
vips_foreign_load_nifti_is_a( filename ) );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_nifti_source_class_init(
|
||||
VipsForeignLoadNiftiSourceClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "niftiload_source";
|
||||
object_class->description = _( "load NIfTI volumes" );
|
||||
object_class->build = vips_foreign_load_nifti_source_build;
|
||||
|
||||
load_class->is_a_source =
|
||||
vips_foreign_load_nifti_source_is_a_source;
|
||||
|
||||
VIPS_ARG_OBJECT( class, "source", 1,
|
||||
_( "Source" ),
|
||||
_( "Source to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadNiftiSource, source ),
|
||||
VIPS_TYPE_SOURCE );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_nifti_source_init(
|
||||
VipsForeignLoadNiftiSource *nifti )
|
||||
{
|
||||
}
|
||||
|
||||
@ -650,3 +815,26 @@ vips_niftiload( const char *filename, VipsImage **out, ... )
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_niftiload_source:
|
||||
* @source: source to load from
|
||||
* @out: (out): decompressed image
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Exactly as vips_niftiload(), but read from a source.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_niftiload_source( VipsSource *source, VipsImage **out, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, out );
|
||||
result = vips_call_split( "niftiload_source", ap, source, out );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
@ -428,7 +428,7 @@ vips_foreign_save_nifti_class_init( VipsForeignSaveNiftiClass *class )
|
||||
object_class->description = _( "save image to nifti file" );
|
||||
object_class->build = vips_foreign_save_nifti_build;
|
||||
|
||||
foreign_class->suffs = vips__nifti_suffs;
|
||||
foreign_class->suffs = vips_foreign_nifti_suffs;
|
||||
|
||||
save_class->saveable = VIPS_SAVEABLE_ANY;
|
||||
save_class->format_table = vips_nifti_bandfmt;
|
||||
|
@ -260,7 +260,7 @@ vips_foreign_load_openslide_class_init( VipsForeignLoadOpenslideClass *class )
|
||||
|
||||
VIPS_ARG_BOOL( class, "attach-associated", 13,
|
||||
_( "Attach associated" ),
|
||||
_( "Attach all asssociated images" ),
|
||||
_( "Attach all associated images" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadOpenslide, attach_associated ),
|
||||
FALSE );
|
||||
|
@ -168,7 +168,7 @@ int vips__jpeg_write_target( VipsImage *in, VipsTarget *target,
|
||||
gboolean optimize_coding, gboolean progressive, gboolean strip,
|
||||
gboolean trellis_quant, gboolean overshoot_deringing,
|
||||
gboolean optimize_scans, int quant_table,
|
||||
VipsForeignJpegSubsample subsample_mode );
|
||||
VipsForeignSubsample subsample_mode );
|
||||
|
||||
int vips__jpeg_read_source( VipsSource *source, VipsImage *out,
|
||||
gboolean header_only, int shrink, int fail, gboolean autorotate );
|
||||
@ -229,7 +229,7 @@ int vips__quantise_image( VipsImage *in,
|
||||
VipsImage **index_out, VipsImage **palette_out,
|
||||
int colours, int Q, double dither );
|
||||
|
||||
extern const char *vips__nifti_suffs[];
|
||||
extern const char *vips_foreign_nifti_suffs[];
|
||||
|
||||
VipsBandFormat vips__foreign_nifti_datatype2BandFmt( int datatype );
|
||||
int vips__foreign_nifti_BandFmt2datatype( VipsBandFormat fmt );
|
||||
|
@ -102,10 +102,31 @@ vips_foreign_save_png_build( VipsObject *object )
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSavePng *png = (VipsForeignSavePng *) object;
|
||||
|
||||
VipsImage *in;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_png_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
in = save->ready;
|
||||
g_object_ref( in );
|
||||
|
||||
/* save->ready will have been converted to uint16 for high-bitdepth
|
||||
* formats (eg. float) ... we need to check Type to see if we want
|
||||
* to save as 8 or 16-bits. Eg. imagine a float image tagged as sRGB.
|
||||
*/
|
||||
if( in->Type == VIPS_INTERPRETATION_sRGB ||
|
||||
in->Type == VIPS_INTERPRETATION_B_W ) {
|
||||
VipsImage *x;
|
||||
|
||||
if( vips_cast( in, &x, VIPS_FORMAT_UCHAR, NULL ) ) {
|
||||
g_object_unref( in );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( in );
|
||||
in = x;
|
||||
}
|
||||
|
||||
/* Deprecated "colours" arg just sets bitdepth large enough to hold
|
||||
* that many colours.
|
||||
*/
|
||||
@ -113,21 +134,31 @@ vips_foreign_save_png_build( VipsObject *object )
|
||||
png->bitdepth = ceil( log2( png->colours ) );
|
||||
|
||||
if( !vips_object_argument_isset( object, "bitdepth" ) )
|
||||
png->bitdepth =
|
||||
save->ready->BandFmt == VIPS_FORMAT_UCHAR ? 8 : 16;
|
||||
png->bitdepth = in->BandFmt == VIPS_FORMAT_UCHAR ? 8 : 16;
|
||||
|
||||
/* Filtering usually reduces the compression ratio for palette images,
|
||||
* so default off.
|
||||
*/
|
||||
if( !vips_object_argument_isset( object, "filter" ) &&
|
||||
png->palette )
|
||||
png->filter = VIPS_FOREIGN_PNG_FILTER_NONE;
|
||||
|
||||
/* If this is a RGB or RGBA image and a low bit depth has been
|
||||
* requested, enable palettization.
|
||||
*/
|
||||
if( save->ready->Bands > 2 &&
|
||||
if( in->Bands > 2 &&
|
||||
png->bitdepth < 8 )
|
||||
png->palette = TRUE;
|
||||
|
||||
if( vips__png_write_target( save->ready, png->target,
|
||||
if( vips__png_write_target( in, png->target,
|
||||
png->compression, png->interlace, png->profile, png->filter,
|
||||
save->strip, png->palette, png->Q, png->dither,
|
||||
png->bitdepth ) )
|
||||
png->bitdepth ) ) {
|
||||
g_object_unref( in );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
g_object_unref( in );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -145,9 +176,12 @@ vips_foreign_save_png_build( VipsObject *object )
|
||||
#define D VIPS_FORMAT_DOUBLE
|
||||
#define DX VIPS_FORMAT_DPCOMPLEX
|
||||
|
||||
/* Except for 8-bit inputs, we send everything else to 16. We decide on png8
|
||||
* vs. png16 based on Type in_build(), see above.
|
||||
*/
|
||||
static int bandfmt_png[10] = {
|
||||
/* UC C US S UI I F X D DX */
|
||||
UC, UC, US, US, US, US, UC, UC, UC, UC
|
||||
UC, UC, US, US, US, US, US, US, US, US
|
||||
};
|
||||
|
||||
static void
|
||||
@ -449,7 +483,8 @@ vips_foreign_save_png_buffer_init( VipsForeignSavePngBuffer *buffer )
|
||||
* profile from the VIPS header will be attached.
|
||||
*
|
||||
* Use @filter to specify one or more filters (instead of adaptive filtering),
|
||||
* see #VipsForeignPngFilter.
|
||||
* see #VipsForeignPngFilter. @filter defaults to NONE for palette images,
|
||||
* since they generally have very low local correlation.
|
||||
*
|
||||
* The image is automatically converted to RGB, RGBA, Monochrome or Mono +
|
||||
* alpha before saving. Images with more than one byte per band element are
|
||||
|
@ -194,6 +194,10 @@ vips_foreign_load_ppm_dispose( GObject *gobject )
|
||||
{
|
||||
VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) gobject;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_load_ppm_dispose: %p\n", ppm );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
VIPS_UNREF( ppm->sbuf );
|
||||
VIPS_UNREF( ppm->source );
|
||||
|
||||
@ -789,12 +793,9 @@ vips_foreign_load_ppm_file_build( VipsObject *object )
|
||||
VipsForeignLoadPpmFile *file = (VipsForeignLoadPpmFile *) object;
|
||||
VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) object;
|
||||
|
||||
if( file->filename ) {
|
||||
if( !(ppm->source =
|
||||
vips_source_new_from_file( file->filename )) )
|
||||
return( -1 );
|
||||
ppm->sbuf = vips_sbuf_new_from_source( ppm->source );
|
||||
}
|
||||
if( file->filename &&
|
||||
!(ppm->source = vips_source_new_from_file( file->filename )) )
|
||||
return( -1 );
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_load_ppm_file_parent_class )->
|
||||
build( object ) )
|
||||
|
@ -212,13 +212,50 @@ vips_foreign_save_ppm_block( VipsRegion *region, VipsRect *area, void *a )
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_save_ppm( VipsForeignSavePpm *ppm, VipsImage *image )
|
||||
vips_foreign_save_ppm_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignSave *save = (VipsForeignSave *) ppm;
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) object;
|
||||
|
||||
VipsImage *image;
|
||||
char *magic;
|
||||
char *date;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_ppm_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
image = save->ready;
|
||||
|
||||
/* Handle the deprecated squash parameter.
|
||||
*/
|
||||
if( vips_object_argument_isset( object, "squash" ) )
|
||||
ppm->bitdepth = 1;
|
||||
|
||||
if( vips_check_uintorf( "vips2ppm", image ) ||
|
||||
vips_check_bands_1or3( "vips2ppm", image ) ||
|
||||
vips_check_uncoded( "vips2ppm", image ) ||
|
||||
vips_image_pio_input( image ) )
|
||||
return( -1 );
|
||||
|
||||
if( ppm->ascii &&
|
||||
image->BandFmt == VIPS_FORMAT_FLOAT ) {
|
||||
g_warning( "%s",
|
||||
_( "float images must be binary -- disabling ascii" ) );
|
||||
ppm->ascii = FALSE;
|
||||
}
|
||||
|
||||
/* One bit images must come from a 8 bit, one band source.
|
||||
*/
|
||||
if( ppm->bitdepth &&
|
||||
(image->Bands != 1 ||
|
||||
image->BandFmt != VIPS_FORMAT_UCHAR) ) {
|
||||
g_warning( "%s",
|
||||
_( "can only save 1 band uchar images as 1 bit -- "
|
||||
"disabling 1 bit save" ) );
|
||||
ppm->bitdepth = 0;
|
||||
}
|
||||
|
||||
magic = "unset";
|
||||
if( image->BandFmt == VIPS_FORMAT_FLOAT &&
|
||||
image->Bands == 3 )
|
||||
@ -315,8 +352,11 @@ vips_foreign_save_ppm( VipsForeignSavePpm *ppm, VipsImage *image )
|
||||
|
||||
if( vips__byteswap_bool( image, &x, !vips_amiMSBfirst() ) )
|
||||
return( -1 );
|
||||
VIPS_UNREF( image );
|
||||
image = x;
|
||||
|
||||
/* image must now be unreffed on exit.
|
||||
*/
|
||||
vips_object_local( VIPS_OBJECT( ppm->target ), image );
|
||||
}
|
||||
|
||||
if( vips_sink_disc( image, vips_foreign_save_ppm_block, ppm ) )
|
||||
@ -327,54 +367,6 @@ vips_foreign_save_ppm( VipsForeignSavePpm *ppm, VipsImage *image )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_save_ppm_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) object;
|
||||
|
||||
VipsImage *image;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_ppm_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
/* Handle the deprecated squash parameter.
|
||||
*/
|
||||
if( vips_object_argument_isset( object, "squash" ) )
|
||||
ppm->bitdepth = 1;
|
||||
|
||||
image = save->ready;
|
||||
if( vips_check_uintorf( "vips2ppm", image ) ||
|
||||
vips_check_bands_1or3( "vips2ppm", image ) ||
|
||||
vips_check_uncoded( "vips2ppm", image ) ||
|
||||
vips_image_pio_input( image ) )
|
||||
return( -1 );
|
||||
|
||||
if( ppm->ascii &&
|
||||
image->BandFmt == VIPS_FORMAT_FLOAT ) {
|
||||
g_warning( "%s",
|
||||
_( "float images must be binary -- disabling ascii" ) );
|
||||
ppm->ascii = FALSE;
|
||||
}
|
||||
|
||||
/* One bit images must come from a 8 bit, one band source.
|
||||
*/
|
||||
if( ppm->bitdepth &&
|
||||
(image->Bands != 1 ||
|
||||
image->BandFmt != VIPS_FORMAT_UCHAR) ) {
|
||||
g_warning( "%s",
|
||||
_( "can only save 1 band uchar images as 1 bit -- "
|
||||
"disabling 1 bit save" ) );
|
||||
ppm->bitdepth = 0;
|
||||
}
|
||||
|
||||
if( vips_foreign_save_ppm( ppm, image ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Save a bit of typing.
|
||||
*/
|
||||
#define UC VIPS_FORMAT_UCHAR
|
||||
|
@ -2,6 +2,8 @@
|
||||
*
|
||||
* 1/5/20
|
||||
* - from pngload.c
|
||||
* 19/2/21 781545872
|
||||
* - read out background, if we can
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -199,6 +201,7 @@ vips_foreign_load_png_set_header( VipsForeignLoadPng *png, VipsImage *image )
|
||||
struct spng_iccp iccp;
|
||||
struct spng_exif exif;
|
||||
struct spng_phys phys;
|
||||
struct spng_bkgd bkgd;
|
||||
guint32 n_text;
|
||||
|
||||
/* Get resolution. Default to 72 pixels per inch.
|
||||
@ -267,6 +270,42 @@ vips_foreign_load_png_set_header( VipsForeignLoadPng *png, VipsImage *image )
|
||||
*/
|
||||
if( png->ihdr.interlace_method != SPNG_INTERLACE_NONE )
|
||||
vips_image_set_int( image, "interlaced", 1 );
|
||||
|
||||
if( !spng_get_bkgd( png->ctx, &bkgd ) ) {
|
||||
const int scale = image->BandFmt ==
|
||||
VIPS_FORMAT_UCHAR ? 1 : 256;
|
||||
|
||||
double array[3];
|
||||
int n;
|
||||
|
||||
switch( png->ihdr.color_type ) {
|
||||
case SPNG_COLOR_TYPE_GRAYSCALE:
|
||||
case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA:
|
||||
array[0] = bkgd.gray / scale;
|
||||
n = 1;
|
||||
break;
|
||||
|
||||
case SPNG_COLOR_TYPE_TRUECOLOR:
|
||||
case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA:
|
||||
array[0] = bkgd.red / scale;
|
||||
array[1] = bkgd.green / scale;
|
||||
array[2] = bkgd.blue / scale;
|
||||
n = 3;
|
||||
break;
|
||||
|
||||
case SPNG_COLOR_TYPE_INDEXED:
|
||||
default:
|
||||
/* Not sure what to do here. I suppose we should read
|
||||
* the palette.
|
||||
*/
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if( n > 0 )
|
||||
vips_image_set_array_double( image, "background",
|
||||
array, n );
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
@ -308,7 +347,6 @@ vips_foreign_load_png_header( VipsForeignLoad *load )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "width: %d\nheight: %d\nbit depth: %d\ncolor type: %d\n",
|
||||
png->ihdr.width, png->ihdr.height,
|
||||
|
@ -80,13 +80,6 @@
|
||||
*/
|
||||
#define RSVG_MAX_WIDTH (32767)
|
||||
|
||||
/* Old librsvg versions don't include librsvg-features.h by default.
|
||||
* Newer versions deprecate direct inclusion.
|
||||
*/
|
||||
#ifndef LIBRSVG_FEATURES_H
|
||||
#include <librsvg/librsvg-features.h>
|
||||
#endif
|
||||
|
||||
/* A handy #define for we-will-handle-svgz.
|
||||
*/
|
||||
#if LIBRSVG_CHECK_FEATURE(SVGZ) && defined(HAVE_ZLIB)
|
||||
|
@ -590,8 +590,8 @@ vips_foreign_save_tiff_buffer_init( VipsForeignSaveTiffBuffer *buffer )
|
||||
* User @level to set the ZSTD compression level. Use @lossless to
|
||||
* set WEBP lossless mode on. Use @Q to set the WEBP compression level.
|
||||
*
|
||||
* Use @predictor to set the predictor for lzw and deflate compression. It
|
||||
* defaults to #VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL, meaning horizontal
|
||||
* Use @predictor to set the predictor for lzw, deflate and zstd compression.
|
||||
* It defaults to #VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL, meaning horizontal
|
||||
* differencing. Please refer to the libtiff
|
||||
* specifications for further discussion of various predictors.
|
||||
*
|
||||
|
@ -540,7 +540,7 @@ write_vips( Write *write, int qfac, const char *profile,
|
||||
gboolean optimize_coding, gboolean progressive, gboolean strip,
|
||||
gboolean trellis_quant, gboolean overshoot_deringing,
|
||||
gboolean optimize_scans, int quant_table,
|
||||
VipsForeignJpegSubsample subsample_mode )
|
||||
VipsForeignSubsample subsample_mode )
|
||||
{
|
||||
VipsImage *in;
|
||||
J_COLOR_SPACE space;
|
||||
@ -687,8 +687,8 @@ write_vips( Write *write, int qfac, const char *profile,
|
||||
if( progressive )
|
||||
jpeg_simple_progression( &write->cinfo );
|
||||
|
||||
if( subsample_mode == VIPS_FOREIGN_JPEG_SUBSAMPLE_OFF ||
|
||||
(subsample_mode == VIPS_FOREIGN_JPEG_SUBSAMPLE_AUTO &&
|
||||
if( subsample_mode == VIPS_FOREIGN_SUBSAMPLE_OFF ||
|
||||
(subsample_mode == VIPS_FOREIGN_SUBSAMPLE_AUTO &&
|
||||
qfac >= 90) ) {
|
||||
int i;
|
||||
|
||||
@ -846,7 +846,7 @@ vips__jpeg_write_target( VipsImage *in, VipsTarget *target,
|
||||
gboolean optimize_coding, gboolean progressive,
|
||||
gboolean strip, gboolean trellis_quant,
|
||||
gboolean overshoot_deringing, gboolean optimize_scans,
|
||||
int quant_table, VipsForeignJpegSubsample subsample_mode)
|
||||
int quant_table, VipsForeignSubsample subsample_mode)
|
||||
{
|
||||
Write *write;
|
||||
|
||||
|
@ -333,9 +333,9 @@ struct _Wtiff {
|
||||
VipsPel *tbuf; /* TIFF output buffer */
|
||||
int tls; /* Tile line size */
|
||||
|
||||
int compression; /* Compression type */
|
||||
int compression; /* libtiff compression type */
|
||||
int Q; /* JPEG q-factor, webp level */
|
||||
int predictor; /* Predictor value */
|
||||
int predictor; /* libtiff predictor type */
|
||||
int tile; /* Tile or not */
|
||||
int tilew, tileh; /* Tile size */
|
||||
int pyramid; /* Wtiff pyramid */
|
||||
@ -659,12 +659,16 @@ wtiff_write_header( Wtiff *wtiff, Layer *layer )
|
||||
TIFFSetField( tif, TIFFTAG_WEBP_LEVEL, wtiff->Q );
|
||||
TIFFSetField( tif, TIFFTAG_WEBP_LOSSLESS, wtiff->lossless );
|
||||
}
|
||||
if( wtiff->compression == COMPRESSION_ZSTD )
|
||||
if( wtiff->compression == COMPRESSION_ZSTD ) {
|
||||
TIFFSetField( tif, TIFFTAG_ZSTD_LEVEL, wtiff->level );
|
||||
if( wtiff->predictor != VIPS_FOREIGN_TIFF_PREDICTOR_NONE )
|
||||
TIFFSetField( tif,
|
||||
TIFFTAG_PREDICTOR, wtiff->predictor );
|
||||
}
|
||||
#endif /*HAVE_TIFF_COMPRESSION_WEBP*/
|
||||
|
||||
if( (wtiff->compression == VIPS_FOREIGN_TIFF_COMPRESSION_DEFLATE ||
|
||||
wtiff->compression == VIPS_FOREIGN_TIFF_COMPRESSION_LZW) &&
|
||||
if( (wtiff->compression == COMPRESSION_ADOBE_DEFLATE ||
|
||||
wtiff->compression == COMPRESSION_LZW) &&
|
||||
wtiff->predictor != VIPS_FOREIGN_TIFF_PREDICTOR_NONE )
|
||||
TIFFSetField( tif, TIFFTAG_PREDICTOR, wtiff->predictor );
|
||||
|
||||
@ -1862,10 +1866,19 @@ wtiff_copy_tiff( Wtiff *wtiff, TIFF *out, TIFF *in )
|
||||
TIFFSetField( out, TIFFTAG_WEBP_LEVEL, wtiff->Q );
|
||||
TIFFSetField( out, TIFFTAG_WEBP_LOSSLESS, wtiff->lossless );
|
||||
}
|
||||
if( wtiff->compression == COMPRESSION_ZSTD )
|
||||
if( wtiff->compression == COMPRESSION_ZSTD ) {
|
||||
TIFFSetField( out, TIFFTAG_ZSTD_LEVEL, wtiff->level );
|
||||
if( wtiff->predictor != VIPS_FOREIGN_TIFF_PREDICTOR_NONE )
|
||||
TIFFSetField( out,
|
||||
TIFFTAG_PREDICTOR, wtiff->predictor );
|
||||
}
|
||||
#endif /*HAVE_TIFF_COMPRESSION_WEBP*/
|
||||
|
||||
if( (wtiff->compression == COMPRESSION_ADOBE_DEFLATE ||
|
||||
wtiff->compression == COMPRESSION_LZW) &&
|
||||
wtiff->predictor != VIPS_FOREIGN_TIFF_PREDICTOR_NONE )
|
||||
TIFFSetField( out, TIFFTAG_PREDICTOR, wtiff->predictor );
|
||||
|
||||
/* We can't copy profiles or xmp :( Set again from wtiff.
|
||||
*/
|
||||
if( !wtiff->strip )
|
||||
|
@ -318,7 +318,6 @@ vips_foreign_load_vips_source_init( VipsForeignLoadVipsSource *source )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* vips_vipsload:
|
||||
* @filename: file to load
|
||||
|
@ -79,6 +79,8 @@
|
||||
* - revise for connection IO
|
||||
* 11/5/20
|
||||
* - only warn for saving bad profiles, don't fail
|
||||
* 19/2/21 781545872
|
||||
* - read out background, if we can
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -559,6 +561,49 @@ png2vips_header( Read *read, VipsImage *out )
|
||||
if( color_type == PNG_COLOR_TYPE_PALETTE )
|
||||
vips_image_set_int( out, "palette-bit-depth", bitdepth );
|
||||
|
||||
/* Note the PNG background colour, if any.
|
||||
*/
|
||||
#ifdef PNG_bKGD_SUPPORTED
|
||||
{
|
||||
png_color_16 *background;
|
||||
|
||||
if( png_get_bKGD( read->pPng, read->pInfo, &background ) ) {
|
||||
const int scale = out->BandFmt == VIPS_FORMAT_UCHAR ? 1 : 256;
|
||||
|
||||
double array[3];
|
||||
int n;
|
||||
|
||||
switch( color_type ) {
|
||||
case PNG_COLOR_TYPE_GRAY:
|
||||
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
||||
array[0] = background->gray / scale;
|
||||
n = 1;
|
||||
break;
|
||||
|
||||
case PNG_COLOR_TYPE_RGB:
|
||||
case PNG_COLOR_TYPE_RGB_ALPHA:
|
||||
array[0] = background->red / scale;
|
||||
array[1] = background->green / scale;
|
||||
array[2] = background->blue / scale;
|
||||
n = 3;
|
||||
break;
|
||||
|
||||
case PNG_COLOR_TYPE_PALETTE:
|
||||
default:
|
||||
/* Not sure what to do here. I suppose we should read
|
||||
* the palette.
|
||||
*/
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if( n > 0 )
|
||||
vips_image_set_array_double( out, "background",
|
||||
array, n );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
@ -783,12 +828,15 @@ typedef struct {
|
||||
} Write;
|
||||
|
||||
static void
|
||||
write_finish( Write *write )
|
||||
write_destroy( Write *write )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf( "write_destroy: %p\n", write );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
VIPS_UNREF( write->memory );
|
||||
if( write->target )
|
||||
vips_target_finish( write->target );
|
||||
VIPS_UNREF( write->target );
|
||||
if( write->pPng )
|
||||
png_destroy_write_struct( &write->pPng, &write->pInfo );
|
||||
VIPS_FREE( write->row_pointer );
|
||||
@ -811,18 +859,19 @@ write_new( VipsImage *in, VipsTarget *target )
|
||||
|
||||
if( !(write = VIPS_NEW( NULL, Write )) )
|
||||
return( NULL );
|
||||
memset( write, 0, sizeof( Write ) );
|
||||
write->in = in;
|
||||
write->memory = NULL;
|
||||
write->target = target;
|
||||
g_object_ref( target );
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "write_new: %p\n", write );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( !(write->row_pointer = VIPS_ARRAY( NULL, in->Ysize, png_bytep )) )
|
||||
return( NULL );
|
||||
if( !(write->pPng = png_create_write_struct(
|
||||
PNG_LIBPNG_VER_STRING, NULL,
|
||||
user_error_function, user_warning_function )) ) {
|
||||
write_finish( write );
|
||||
write_destroy( write );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
@ -840,12 +889,12 @@ write_new( VipsImage *in, VipsTarget *target )
|
||||
/* Catch PNG errors from png_create_info_struct().
|
||||
*/
|
||||
if( setjmp( png_jmpbuf( write->pPng ) ) ) {
|
||||
write_finish( write );
|
||||
write_destroy( write );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
if( !(write->pInfo = png_create_info_struct( write->pPng )) ) {
|
||||
write_finish( write );
|
||||
write_destroy( write );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
@ -1196,8 +1245,6 @@ write_vips( Write *write,
|
||||
|
||||
png_write_end( write->pPng, write->pInfo );
|
||||
|
||||
vips_target_finish( write->target );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
@ -1216,13 +1263,13 @@ vips__png_write_target( VipsImage *in, VipsTarget *target,
|
||||
if( write_vips( write,
|
||||
compression, interlace, profile, filter, strip, palette,
|
||||
Q, dither, bitdepth ) ) {
|
||||
write_finish( write );
|
||||
write_destroy( write );
|
||||
vips_error( "vips2png", _( "unable to write to target %s" ),
|
||||
vips_connection_nick( VIPS_CONNECTION( target ) ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
write_finish( write );
|
||||
write_destroy( write );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
@ -108,8 +108,8 @@ vips_hist_local_stop( void *vseq, void *a, void *b )
|
||||
|
||||
for( i = 0; i < in->Bands; i++ )
|
||||
VIPS_FREE( seq->hist[i] );
|
||||
VIPS_FREE( seq->hist );
|
||||
}
|
||||
VIPS_FREE( seq->hist );
|
||||
VIPS_FREE( seq );
|
||||
|
||||
return( 0 );
|
||||
|
@ -33,11 +33,12 @@
|
||||
#ifndef IM_DISPATCH_H
|
||||
#define IM_DISPATCH_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <vips/vips.h>
|
||||
#include <vips/util.h>
|
||||
|
||||
|
@ -58,6 +58,8 @@ GType vips_foreign_flags_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_FOREIGN_FLAGS (vips_foreign_flags_get_type())
|
||||
GType vips_saveable_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_SAVEABLE (vips_saveable_get_type())
|
||||
GType vips_foreign_subsample_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_FOREIGN_SUBSAMPLE (vips_foreign_subsample_get_type())
|
||||
GType vips_foreign_jpeg_subsample_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_FOREIGN_JPEG_SUBSAMPLE (vips_foreign_jpeg_subsample_get_type())
|
||||
GType vips_foreign_webp_preset_get_type (void) G_GNUC_CONST;
|
||||
|
@ -370,6 +370,21 @@ int vips_openslideload( const char *filename, VipsImage **out, ... )
|
||||
int vips_openslideload_source( VipsSource *source, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
/**
|
||||
* VipsForeignSubsample:
|
||||
* @VIPS_FOREIGN_SUBSAMPLE_AUTO: prevent subsampling when quality > 90
|
||||
* @VIPS_FOREIGN_SUBSAMPLE_ON: always perform subsampling
|
||||
* @VIPS_FOREIGN_SUBSAMPLE_OFF: never perform subsampling
|
||||
*
|
||||
* Set subsampling mode.
|
||||
*/
|
||||
typedef enum {
|
||||
VIPS_FOREIGN_SUBSAMPLE_AUTO,
|
||||
VIPS_FOREIGN_SUBSAMPLE_ON,
|
||||
VIPS_FOREIGN_SUBSAMPLE_OFF,
|
||||
VIPS_FOREIGN_SUBSAMPLE_LAST
|
||||
} VipsForeignSubsample;
|
||||
|
||||
/**
|
||||
* VipsForeignJpegSubsample:
|
||||
* @VIPS_FOREIGN_JPEG_SUBSAMPLE_AUTO: default preset
|
||||
@ -377,6 +392,8 @@ int vips_openslideload_source( VipsSource *source, VipsImage **out, ... )
|
||||
* @VIPS_FOREIGN_JPEG_SUBSAMPLE_OFF: never perform subsampling
|
||||
*
|
||||
* Set jpeg subsampling mode.
|
||||
*
|
||||
* DEPRECATED: use #VipsForeignSubsample
|
||||
*/
|
||||
typedef enum {
|
||||
VIPS_FOREIGN_JPEG_SUBSAMPLE_AUTO,
|
||||
@ -654,6 +671,8 @@ int vips_heifsave_target( VipsImage *in, VipsTarget *target, ... )
|
||||
|
||||
int vips_niftiload( const char *filename, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_niftiload_source( VipsSource *source, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_niftisave( VipsImage *in, const char *filename, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
|
@ -230,6 +230,10 @@ void vips_image_set_array_int( VipsImage *image, const char *name,
|
||||
const int *array, int n );
|
||||
int vips_image_get_array_int( VipsImage *image, const char *name,
|
||||
int **out, int *n );
|
||||
int vips_image_get_array_double( VipsImage *image, const char *name,
|
||||
double **out, int *n );
|
||||
void vips_image_set_array_double( VipsImage *image, const char *name,
|
||||
const double *array, int n );
|
||||
|
||||
int vips_image_history_printf( VipsImage *image, const char *format, ... )
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
|
@ -114,9 +114,9 @@ void vips__threadpool_init( void );
|
||||
|
||||
void vips__cache_init( void );
|
||||
|
||||
void vips__print_renders( void );
|
||||
|
||||
void vips__type_leak( void );
|
||||
int vips__print_renders( void );
|
||||
int vips__type_leak( void );
|
||||
int vips__object_leak( void );
|
||||
|
||||
typedef int (*im__fftproc_fn)( VipsImage *, VipsImage *, VipsImage * );
|
||||
int im__fftproc( VipsImage *dummy,
|
||||
|
@ -80,10 +80,6 @@
|
||||
#ifndef VIPS_VIPS_H
|
||||
#define VIPS_VIPS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <gmodule.h>
|
||||
@ -93,6 +89,10 @@ extern "C" {
|
||||
*/
|
||||
#include <gio/gio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
/* If we're being parsed by SWIG, remove gcc attributes.
|
||||
*/
|
||||
#ifdef SWIG
|
||||
|
@ -401,7 +401,7 @@ vips_buf_removec( VipsBuf *buf, char ch )
|
||||
* @ap: arguments to format string
|
||||
*
|
||||
* Append to @buf, args as <function>vprintf()</function>.
|
||||
*
|
||||
*
|
||||
* Returns: %FALSE on overflow, %TRUE otherwise.
|
||||
*/
|
||||
gboolean
|
||||
|
@ -658,16 +658,10 @@ buffer_thread_destroy_notify( VipsBufferThread *buffer_thread )
|
||||
void
|
||||
vips__buffer_init( void )
|
||||
{
|
||||
#ifdef HAVE_PRIVATE_INIT
|
||||
static GPrivate private =
|
||||
G_PRIVATE_INIT( (GDestroyNotify) buffer_thread_destroy_notify );
|
||||
|
||||
buffer_thread_key = &private;
|
||||
#else
|
||||
if( !buffer_thread_key )
|
||||
buffer_thread_key = g_private_new(
|
||||
(GDestroyNotify) buffer_thread_destroy_notify );
|
||||
#endif
|
||||
|
||||
if( buffer_cache_max_reserve < 1 )
|
||||
printf( "vips__buffer_init: buffer reserve disabled\n" );
|
||||
|
@ -111,14 +111,6 @@ static GMutex *vips_cache_lock = NULL;
|
||||
#define INT64_HASH(X) (g_direct_hash(X))
|
||||
#define DOUBLE_HASH(X) (g_direct_hash(X))
|
||||
|
||||
/* Old glibs use g_value_get_char(), new ones g_value_get_schar().
|
||||
*/
|
||||
#ifdef HAVE_VALUE_GET_SCHAR
|
||||
#define VIPS_VALUE_GET_CHAR g_value_get_schar
|
||||
#else
|
||||
#define VIPS_VALUE_GET_CHAR g_value_get_char
|
||||
#endif
|
||||
|
||||
/* A cache entry.
|
||||
*/
|
||||
typedef struct _VipsOperationCacheEntry {
|
||||
@ -156,7 +148,7 @@ vips_value_hash( GParamSpec *pspec, GValue *value )
|
||||
if( generic == G_TYPE_PARAM_BOOLEAN )
|
||||
return( (unsigned int) g_value_get_boolean( value ) );
|
||||
else if( generic == G_TYPE_PARAM_CHAR )
|
||||
return( (unsigned int) VIPS_VALUE_GET_CHAR( value ) );
|
||||
return( (unsigned int) g_value_get_schar( value ) );
|
||||
else if( generic == G_TYPE_PARAM_UCHAR )
|
||||
return( (unsigned int) g_value_get_uchar( value ) );
|
||||
else if( generic == G_TYPE_PARAM_INT )
|
||||
@ -258,8 +250,8 @@ vips_value_equal( GParamSpec *pspec, GValue *v1, GValue *v2 )
|
||||
return( g_value_get_boolean( v1 ) ==
|
||||
g_value_get_boolean( v2 ) );
|
||||
else if( generic == G_TYPE_PARAM_CHAR )
|
||||
return( VIPS_VALUE_GET_CHAR( v1 ) ==
|
||||
VIPS_VALUE_GET_CHAR( v2 ) );
|
||||
return( g_value_get_schar( v1 ) ==
|
||||
g_value_get_schar( v2 ) );
|
||||
if( generic == G_TYPE_PARAM_UCHAR )
|
||||
return( g_value_get_uchar( v1 ) ==
|
||||
g_value_get_uchar( v2 ) );
|
||||
|
@ -500,6 +500,25 @@ vips_saveable_get_type( void )
|
||||
return( etype );
|
||||
}
|
||||
GType
|
||||
vips_foreign_subsample_get_type( void )
|
||||
{
|
||||
static GType etype = 0;
|
||||
|
||||
if( etype == 0 ) {
|
||||
static const GEnumValue values[] = {
|
||||
{VIPS_FOREIGN_SUBSAMPLE_AUTO, "VIPS_FOREIGN_SUBSAMPLE_AUTO", "auto"},
|
||||
{VIPS_FOREIGN_SUBSAMPLE_ON, "VIPS_FOREIGN_SUBSAMPLE_ON", "on"},
|
||||
{VIPS_FOREIGN_SUBSAMPLE_OFF, "VIPS_FOREIGN_SUBSAMPLE_OFF", "off"},
|
||||
{VIPS_FOREIGN_SUBSAMPLE_LAST, "VIPS_FOREIGN_SUBSAMPLE_LAST", "last"},
|
||||
{0, NULL, NULL}
|
||||
};
|
||||
|
||||
etype = g_enum_register_static( "VipsForeignSubsample", values );
|
||||
}
|
||||
|
||||
return( etype );
|
||||
}
|
||||
GType
|
||||
vips_foreign_jpeg_subsample_get_type( void )
|
||||
{
|
||||
static GType etype = 0;
|
||||
|
@ -200,7 +200,7 @@ vips_error_buffer_copy( void )
|
||||
|
||||
g_mutex_lock( vips__global_lock );
|
||||
msg = g_strdup( vips_buf_all( &vips_error_buf ) );
|
||||
vips_error_clear();
|
||||
vips_buf_rewind( &vips_error_buf );
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
|
||||
return( msg );
|
||||
|
@ -213,16 +213,10 @@ vips__thread_profile_init_cb( VipsThreadProfile *profile )
|
||||
static void *
|
||||
vips__thread_profile_init( void *data )
|
||||
{
|
||||
#ifdef HAVE_PRIVATE_INIT
|
||||
static GPrivate private =
|
||||
G_PRIVATE_INIT( (GDestroyNotify) vips__thread_profile_init_cb );
|
||||
|
||||
vips_thread_profile_key = &private;
|
||||
#else
|
||||
if( !vips_thread_profile_key )
|
||||
vips_thread_profile_key = g_private_new(
|
||||
(GDestroyNotify) vips__thread_profile_init_cb );
|
||||
#endif
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
@ -300,20 +294,6 @@ vips_thread_gate_block_add( VipsThreadGateBlock **block )
|
||||
*block = new_block;
|
||||
}
|
||||
|
||||
static gint64
|
||||
vips_get_time( void )
|
||||
{
|
||||
#ifdef HAVE_MONOTONIC_TIME
|
||||
return( g_get_monotonic_time() );
|
||||
#else
|
||||
GTimeVal time;
|
||||
|
||||
g_get_current_time( &time );
|
||||
|
||||
return( (gint64) time.tv_usec );
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
vips__thread_gate_start( const char *gate_name )
|
||||
{
|
||||
@ -322,7 +302,7 @@ vips__thread_gate_start( const char *gate_name )
|
||||
VIPS_DEBUG_MSG_RED( "vips__thread_gate_start: %s\n", gate_name );
|
||||
|
||||
if( (profile = vips_thread_profile_get()) ) {
|
||||
gint64 time = vips_get_time();
|
||||
gint64 time = g_get_monotonic_time();
|
||||
|
||||
VipsThreadGate *gate;
|
||||
|
||||
@ -350,7 +330,7 @@ vips__thread_gate_stop( const char *gate_name )
|
||||
VIPS_DEBUG_MSG_RED( "vips__thread_gate_stop: %s\n", gate_name );
|
||||
|
||||
if( (profile = vips_thread_profile_get()) ) {
|
||||
gint64 time = vips_get_time();
|
||||
gint64 time = g_get_monotonic_time();
|
||||
|
||||
VipsThreadGate *gate;
|
||||
|
||||
@ -385,7 +365,7 @@ vips__thread_malloc_free( gint64 size )
|
||||
#endif /*VIPS_DEBUG*/
|
||||
|
||||
if( (profile = vips_thread_profile_get()) ) {
|
||||
gint64 time = vips_get_time();
|
||||
gint64 time = g_get_monotonic_time();
|
||||
VipsThreadGate *gate = profile->memory;
|
||||
|
||||
if( gate->start->i >= VIPS_GATE_SIZE ) {
|
||||
|
@ -1959,6 +1959,64 @@ vips_image_set_array_int( VipsImage *image, const char *name,
|
||||
g_value_unset( &value );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_image_get_array_double: (method)
|
||||
* @image: image to get the metadata from
|
||||
* @name: metadata name
|
||||
* @out: (transfer none): return pointer to array
|
||||
* @n: (allow-none): return the number of elements here, optionally
|
||||
*
|
||||
* Gets @out from @im under the name @name.
|
||||
* The field must be of type
|
||||
* #VIPS_TYPE_ARRAY_INT.
|
||||
*
|
||||
* Do not free @out. @out is valid as long as @image is valid.
|
||||
*
|
||||
* Use vips_image_get_typeof() to test for the
|
||||
* existence of a piece of metadata.
|
||||
*
|
||||
* See also: vips_image_get(), vips_image_set_image()
|
||||
*
|
||||
* Returns: 0 on success, -1 otherwise.
|
||||
*/
|
||||
int
|
||||
vips_image_get_array_double( VipsImage *image, const char *name,
|
||||
double **out, int *n )
|
||||
{
|
||||
GValue value = { 0 };
|
||||
|
||||
if( meta_get_value( image, name, VIPS_TYPE_ARRAY_DOUBLE, &value ) )
|
||||
return( -1 );
|
||||
*out = vips_value_get_array_double( &value, n );
|
||||
g_value_unset( &value );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_image_set_array_double: (method)
|
||||
* @image: image to attach the metadata to
|
||||
* @name: metadata name
|
||||
* @array: (array length=n) (allow-none): array of doubles
|
||||
* @n: the number of elements
|
||||
*
|
||||
* Attaches @array as a metadata item on @image as @name.
|
||||
* A convenience function over vips_image_set().
|
||||
*
|
||||
* See also: vips_image_get_image(), vips_image_set().
|
||||
*/
|
||||
void
|
||||
vips_image_set_array_double( VipsImage *image, const char *name,
|
||||
const double *array, int n )
|
||||
{
|
||||
GValue value = { 0 };
|
||||
|
||||
g_value_init( &value, VIPS_TYPE_ARRAY_DOUBLE );
|
||||
vips_value_set_array_double( &value, array, n );
|
||||
vips_image_set( image, name, &value );
|
||||
g_value_unset( &value );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_image_history_printf: (method)
|
||||
* @image: add history line to this image
|
||||
|
@ -1607,7 +1607,7 @@ vips_image_set_progress( VipsImage *image, gboolean progress )
|
||||
image, image->filename );
|
||||
image->progress_signal = image;
|
||||
}
|
||||
else
|
||||
else if( !progress )
|
||||
image->progress_signal = NULL;
|
||||
}
|
||||
|
||||
|
@ -417,19 +417,6 @@ vips_init( const char *argv0 )
|
||||
(void) _setmaxstdio( 2048 );
|
||||
#endif /*OS_WIN32*/
|
||||
|
||||
#ifdef HAVE_TYPE_INIT
|
||||
/* Before glib 2.36 you have to call this on startup.
|
||||
*/
|
||||
g_type_init();
|
||||
#endif /*HAVE_TYPE_INIT*/
|
||||
|
||||
/* Older glibs need this.
|
||||
*/
|
||||
#ifndef HAVE_THREAD_NEW
|
||||
if( !g_thread_supported() )
|
||||
g_thread_init( NULL );
|
||||
#endif /*HAVE_THREAD_NEW*/
|
||||
|
||||
vips__threadpool_init();
|
||||
vips__buffer_init();
|
||||
vips__meta_init();
|
||||
@ -643,13 +630,20 @@ vips_check_init( void )
|
||||
vips_error_clear();
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
vips_leak( void )
|
||||
{
|
||||
char txt[1024];
|
||||
VipsBuf buf = VIPS_BUF_STATIC( txt );
|
||||
int n_leaks;
|
||||
|
||||
vips_object_print_all();
|
||||
n_leaks = 0;
|
||||
|
||||
n_leaks += vips__object_leak();
|
||||
n_leaks += vips__type_leak();
|
||||
n_leaks += vips_tracked_get_allocs();
|
||||
n_leaks += vips_tracked_get_mem();
|
||||
n_leaks += vips_tracked_get_files();
|
||||
|
||||
if( vips_tracked_get_allocs() ||
|
||||
vips_tracked_get_mem() ||
|
||||
@ -664,21 +658,27 @@ vips_leak( void )
|
||||
vips_buf_append_size( &buf, vips_tracked_get_mem_highwater() );
|
||||
vips_buf_appends( &buf, "\n" );
|
||||
|
||||
if( strlen( vips_error_buffer() ) > 0 )
|
||||
if( strlen( vips_error_buffer() ) > 0 ) {
|
||||
vips_buf_appendf( &buf, "error buffer: %s",
|
||||
vips_error_buffer() );
|
||||
n_leaks += strlen( vips_error_buffer() );
|
||||
}
|
||||
|
||||
if( vips__n_active_threads > 0 )
|
||||
if( vips__n_active_threads > 0 ) {
|
||||
vips_buf_appendf( &buf, "threads: %d not joined\n",
|
||||
vips__n_active_threads );
|
||||
n_leaks += vips__n_active_threads;
|
||||
}
|
||||
|
||||
fprintf( stderr, "%s", vips_buf_all( &buf ) );
|
||||
|
||||
vips__print_renders();
|
||||
n_leaks += vips__print_renders();
|
||||
|
||||
#ifdef DEBUG
|
||||
vips_buffer_dump_all();
|
||||
#endif /*DEBUG*/
|
||||
|
||||
return( n_leaks );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -755,8 +755,9 @@ vips_shutdown( void )
|
||||
{
|
||||
static gboolean done = FALSE;
|
||||
|
||||
if( !done )
|
||||
vips_leak();
|
||||
if( !done &&
|
||||
vips_leak() )
|
||||
exit( 1 );
|
||||
|
||||
done = TRUE;
|
||||
}
|
||||
|
@ -3084,7 +3084,7 @@ vips_object_local_array_cb( VipsObject *parent, VipsObjectLocal *local )
|
||||
* |[
|
||||
* VipsObject **t;
|
||||
*
|
||||
* t = vips_object_local_array( a, 5 );
|
||||
* t = vips_object_local_array( parent, 5 );
|
||||
* if(
|
||||
* vips_add( a, b, &t[0], NULL ) ||
|
||||
* vips_invert( t[0], &t[1], NULL ) ||
|
||||
@ -3167,23 +3167,34 @@ vips_object_print_all_cb( VipsObject *object, int *n, void *b )
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
void
|
||||
vips_object_print_all( void )
|
||||
int
|
||||
vips__object_leak( void )
|
||||
{
|
||||
int n_leaks;
|
||||
|
||||
n_leaks = 0;
|
||||
|
||||
/* Don't count static objects.
|
||||
*/
|
||||
if( vips__object_all &&
|
||||
g_hash_table_size( vips__object_all ) >
|
||||
vips_object_n_static() ) {
|
||||
int n;
|
||||
|
||||
fprintf( stderr, "%d objects alive:\n",
|
||||
g_hash_table_size( vips__object_all ) );
|
||||
|
||||
n = 0;
|
||||
vips_object_map(
|
||||
(VipsSListMap2Fn) vips_object_print_all_cb, &n, NULL );
|
||||
(VipsSListMap2Fn) vips_object_print_all_cb,
|
||||
&n_leaks, NULL );
|
||||
}
|
||||
|
||||
vips__type_leak();
|
||||
return( n_leaks );
|
||||
}
|
||||
|
||||
void
|
||||
vips_object_print_all( void )
|
||||
{
|
||||
(void) vips__object_leak();
|
||||
(void) vips__type_leak();
|
||||
}
|
||||
|
||||
static void *
|
||||
|
@ -48,6 +48,9 @@
|
||||
* 9/6/19
|
||||
* - saner behaviour for vips_region_fetch() if the request is partly
|
||||
* outside the image
|
||||
* 22/2/21 f1ac
|
||||
* - fix int overflow in vips_region_copy(), could cause crashes with
|
||||
* very wide images
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -1048,12 +1051,13 @@ void
|
||||
vips_region_copy( VipsRegion *reg,
|
||||
VipsRegion *dest, const VipsRect *r, int x, int y )
|
||||
{
|
||||
int z;
|
||||
int len = VIPS_IMAGE_SIZEOF_PEL( reg->im ) * r->width;
|
||||
size_t len = VIPS_IMAGE_SIZEOF_PEL( reg->im ) * r->width;
|
||||
VipsPel *p = VIPS_REGION_ADDR( reg, r->left, r->top );
|
||||
VipsPel *q = VIPS_REGION_ADDR( dest, x, y );
|
||||
int plsk = VIPS_REGION_LSKIP( reg );
|
||||
int qlsk = VIPS_REGION_LSKIP( dest );
|
||||
size_t plsk = VIPS_REGION_LSKIP( reg );
|
||||
size_t qlsk = VIPS_REGION_LSKIP( dest );
|
||||
|
||||
int z;
|
||||
|
||||
#ifdef DEBUG
|
||||
/* Find the area we will write to in dest.
|
||||
|
@ -1146,23 +1146,29 @@ vips_sink_screen( VipsImage *in, VipsImage *out, VipsImage *mask,
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
vips__print_renders( void )
|
||||
{
|
||||
int n_leaks;
|
||||
|
||||
n_leaks = 0;
|
||||
|
||||
#ifdef VIPS_DEBUG_AMBER
|
||||
if( render_num_renders > 0 )
|
||||
if( render_num_renders > 0 ) {
|
||||
printf( "%d active renders\n", render_num_renders );
|
||||
n_leaks += render_num_renders;
|
||||
}
|
||||
#endif /*VIPS_DEBUG_AMBER*/
|
||||
|
||||
if( render_dirty_lock ) {
|
||||
int n_dirty;
|
||||
|
||||
g_mutex_lock( render_dirty_lock );
|
||||
|
||||
n_dirty = g_slist_length( render_dirty_all );
|
||||
if( n_dirty > 0 )
|
||||
printf( "%d dirty renders\n", n_dirty );
|
||||
n_leaks += g_slist_length( render_dirty_all );
|
||||
if( render_dirty_all )
|
||||
printf( "dirty renders\n" );
|
||||
|
||||
g_mutex_unlock( render_dirty_lock );
|
||||
}
|
||||
|
||||
return( n_leaks );
|
||||
}
|
||||
|
@ -1159,8 +1159,10 @@ vips_source_seek( VipsSource *source, gint64 offset, int whence )
|
||||
* vips_source_rewind:
|
||||
* @source: source to operate on
|
||||
*
|
||||
* Rewind the source to the start. You can't do this after pixel decode phase
|
||||
* starts.
|
||||
* Rewind the source to the start.
|
||||
*
|
||||
* You can't always do this after the pixel decode phase starts -- for
|
||||
* example, pipe-like sources can't be rewound.
|
||||
*
|
||||
* Returns: 0 on success, or -1 on error.
|
||||
*/
|
||||
@ -1175,6 +1177,12 @@ vips_source_rewind( VipsSource *source )
|
||||
vips_source_seek( source, 0, SEEK_SET ) != 0 )
|
||||
return( -1 );
|
||||
|
||||
/* Back into sniff + header decode state.
|
||||
*/
|
||||
source->decode = FALSE;
|
||||
if( !source->sniff )
|
||||
source->sniff = g_byte_array_new();
|
||||
|
||||
SANITY( source );
|
||||
|
||||
return( 0 );
|
||||
|
@ -129,12 +129,8 @@ vips_g_mutex_new( void )
|
||||
{
|
||||
GMutex *mutex;
|
||||
|
||||
#ifdef HAVE_MUTEX_INIT
|
||||
mutex = g_new( GMutex, 1 );
|
||||
g_mutex_init( mutex );
|
||||
#else
|
||||
mutex = g_mutex_new();
|
||||
#endif
|
||||
|
||||
return( mutex );
|
||||
}
|
||||
@ -142,12 +138,8 @@ vips_g_mutex_new( void )
|
||||
void
|
||||
vips_g_mutex_free( GMutex *mutex )
|
||||
{
|
||||
#ifdef HAVE_MUTEX_INIT
|
||||
g_mutex_clear( mutex );
|
||||
g_free( mutex );
|
||||
#else
|
||||
g_mutex_free( mutex );
|
||||
#endif
|
||||
}
|
||||
|
||||
GCond *
|
||||
@ -155,12 +147,8 @@ vips_g_cond_new( void )
|
||||
{
|
||||
GCond *cond;
|
||||
|
||||
#ifdef HAVE_COND_INIT
|
||||
cond = g_new( GCond, 1 );
|
||||
g_cond_init( cond );
|
||||
#else
|
||||
cond = g_cond_new();
|
||||
#endif
|
||||
|
||||
return( cond );
|
||||
}
|
||||
@ -168,12 +156,8 @@ vips_g_cond_new( void )
|
||||
void
|
||||
vips_g_cond_free( GCond *cond )
|
||||
{
|
||||
#ifdef HAVE_COND_INIT
|
||||
g_cond_clear( cond );
|
||||
g_free( cond );
|
||||
#else
|
||||
g_cond_free( cond );
|
||||
#endif
|
||||
}
|
||||
|
||||
/* TRUE if we are a vips worker thread. We sometimes manage resource allocation
|
||||
@ -239,11 +223,7 @@ vips_g_thread_new( const char *domain, GThreadFunc func, gpointer data )
|
||||
else {
|
||||
#endif /*DEBUG_OUT_OF_THREADS*/
|
||||
|
||||
#ifdef HAVE_THREAD_NEW
|
||||
thread = g_thread_try_new( domain, vips_thread_run, info, &error );
|
||||
#else
|
||||
thread = g_thread_create( vips_thread_run, info, TRUE, &error );
|
||||
#endif
|
||||
|
||||
VIPS_DEBUG_MSG_RED( "vips_g_thread_new: g_thread_create( %s ) = %p\n",
|
||||
domain, thread );
|
||||
@ -1015,16 +995,9 @@ vips_threadpool_run( VipsImage *im,
|
||||
void
|
||||
vips__threadpool_init( void )
|
||||
{
|
||||
/* We need to work with the pre-2.32 threading API.
|
||||
*/
|
||||
#ifdef HAVE_PRIVATE_INIT
|
||||
static GPrivate private = { 0 };
|
||||
|
||||
is_worker_key = &private;
|
||||
#else
|
||||
if( !is_worker_key )
|
||||
is_worker_key = g_private_new( NULL );
|
||||
#endif
|
||||
|
||||
if( g_getenv( "VIPS_STALL" ) )
|
||||
vips__stall = TRUE;
|
||||
|
@ -285,9 +285,13 @@ vips_area_new( VipsCallbackFn free_fn, void *data )
|
||||
return( area );
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
vips__type_leak( void )
|
||||
{
|
||||
int n_leaks;
|
||||
|
||||
n_leaks = 0;
|
||||
|
||||
if( vips_area_all ) {
|
||||
GSList *p;
|
||||
|
||||
@ -298,8 +302,12 @@ vips__type_leak( void )
|
||||
|
||||
fprintf( stderr, "\t%p count = %d, bytes = %zd\n",
|
||||
area, area->count, area->length );
|
||||
|
||||
n_leaks += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return( n_leaks );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,6 +26,8 @@
|
||||
* - redone as a class
|
||||
* 12/11/16
|
||||
* - oop, allow index == 0, thanks Rob
|
||||
* 12/1/21
|
||||
* - add hist path for large windows on uchar images
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -81,6 +83,8 @@ typedef struct _VipsRank {
|
||||
|
||||
int n;
|
||||
|
||||
gboolean hist_path;
|
||||
|
||||
} VipsRank;
|
||||
|
||||
typedef VipsMorphologyClass VipsRankClass;
|
||||
@ -91,15 +95,33 @@ G_DEFINE_TYPE( VipsRank, vips_rank, VIPS_TYPE_MORPHOLOGY );
|
||||
*/
|
||||
typedef struct {
|
||||
VipsRegion *ir;
|
||||
|
||||
/* Sort array.
|
||||
*/
|
||||
VipsPel *sort;
|
||||
|
||||
/* For large uchar images, the sort histogram.
|
||||
*/
|
||||
unsigned int **hist;
|
||||
} VipsRankSequence;
|
||||
|
||||
static int
|
||||
vips_rank_stop( void *vseq, void *a, void *b )
|
||||
{
|
||||
VipsRankSequence *seq = (VipsRankSequence *) vseq;
|
||||
VipsImage *in = (VipsImage *) a;
|
||||
|
||||
VIPS_UNREF( seq->ir );
|
||||
VIPS_FREE( seq->sort );
|
||||
|
||||
if( seq->hist &&
|
||||
in ) {
|
||||
int i;
|
||||
|
||||
for( i = 0; i < in->Bands; i++ )
|
||||
VIPS_FREE( seq->hist[i] );
|
||||
}
|
||||
VIPS_FREE( seq->hist );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -115,17 +137,109 @@ vips_rank_start( VipsImage *out, void *a, void *b )
|
||||
return( NULL );
|
||||
seq->ir = NULL;
|
||||
seq->sort = NULL;
|
||||
seq->hist = NULL;
|
||||
|
||||
seq->ir = vips_region_new( in );
|
||||
if( !(seq->sort = VIPS_ARRAY( out,
|
||||
if( !(seq->sort = VIPS_ARRAY( NULL,
|
||||
VIPS_IMAGE_SIZEOF_ELEMENT( in ) * rank->n, VipsPel )) ) {
|
||||
vips_rank_stop( seq, in, rank );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
if( rank->hist_path ) {
|
||||
int i;
|
||||
|
||||
if( !(seq->hist =
|
||||
VIPS_ARRAY( NULL, in->Bands, unsigned int * )) ) {
|
||||
vips_rank_stop( seq, in, rank );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
for( i = 0; i < in->Bands; i++ )
|
||||
if( !(seq->hist[i] =
|
||||
VIPS_ARRAY( NULL, 256, unsigned int )) ) {
|
||||
vips_rank_stop( seq, in, rank );
|
||||
return( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
return( (void *) seq );
|
||||
}
|
||||
|
||||
/* Histogram path for large uchar ranks.
|
||||
*/
|
||||
static void
|
||||
vips_rank_generate_uchar( VipsRegion *or,
|
||||
VipsRankSequence *seq, VipsRank *rank, int y )
|
||||
{
|
||||
VipsImage *in = seq->ir->im;
|
||||
VipsRect *r = &or->valid;
|
||||
const int bands = in->Bands;
|
||||
|
||||
/* Get input and output pointers for this line.
|
||||
*/
|
||||
VipsPel * restrict p =
|
||||
VIPS_REGION_ADDR( seq->ir, r->left, r->top + y );
|
||||
VipsPel * restrict q =
|
||||
VIPS_REGION_ADDR( or, r->left, r->top + y );
|
||||
|
||||
VipsPel * restrict p1;
|
||||
int lsk;
|
||||
int x, i, j, b;
|
||||
|
||||
lsk = VIPS_REGION_LSKIP( seq->ir );
|
||||
|
||||
/* Find histogram for the first output pixel.
|
||||
*/
|
||||
for( b = 0; b < bands; b++ )
|
||||
memset( seq->hist[b], 0, 256 * sizeof( unsigned int ) );
|
||||
p1 = p;
|
||||
for( j = 0; j < rank->height; j++ ) {
|
||||
for( i = 0, x = 0; x < rank->width; x++ )
|
||||
for( b = 0; b < bands; b++, i++ )
|
||||
seq->hist[b][p1[i]] += 1;
|
||||
|
||||
p1 += lsk;
|
||||
}
|
||||
|
||||
/* Loop for output pels.
|
||||
*/
|
||||
for( x = 0; x < r->width; x++ ) {
|
||||
for( b = 0; b < bands; b++ ) {
|
||||
/* Calculate cumulative histogram -- the value is the
|
||||
* index at which we pass the rank.
|
||||
*/
|
||||
unsigned int * restrict hist = seq->hist[b];
|
||||
|
||||
int sum;
|
||||
int i;
|
||||
|
||||
sum = 0;
|
||||
for( i = 0; i < 256; i++ ) {
|
||||
sum += hist[i];
|
||||
if( sum > rank->index )
|
||||
break;
|
||||
}
|
||||
q[b] = i;
|
||||
|
||||
/* Adapt histogram --- remove the pels from
|
||||
* the left hand column, add in pels for a
|
||||
* new right-hand column.
|
||||
*/
|
||||
p1 = p + b;
|
||||
for( j = 0; j < rank->height; j++ ) {
|
||||
hist[p1[0]] -= 1;
|
||||
hist[p1[bands * rank->width]] += 1;
|
||||
|
||||
p1 += lsk;
|
||||
}
|
||||
}
|
||||
|
||||
p += bands;
|
||||
q += bands;
|
||||
}
|
||||
}
|
||||
|
||||
/* Inner loop for select-sorting TYPE.
|
||||
*/
|
||||
#define LOOP_SELECT( TYPE ) { \
|
||||
@ -318,7 +432,9 @@ vips_rank_generate( VipsRegion *or,
|
||||
ls = VIPS_REGION_LSKIP( ir ) / VIPS_IMAGE_SIZEOF_ELEMENT( in );
|
||||
|
||||
for( y = 0; y < r->height; y++ ) {
|
||||
if( rank->index == 0 )
|
||||
if( rank->hist_path )
|
||||
vips_rank_generate_uchar( or, seq, rank, y );
|
||||
else if( rank->index == 0 )
|
||||
SWITCH( LOOP_MIN )
|
||||
else if( rank->index == rank->n - 1 )
|
||||
SWITCH( LOOP_MAX )
|
||||
@ -360,6 +476,20 @@ vips_rank_build( VipsObject *object )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Enable the hist path if it'll probably help.
|
||||
*/
|
||||
if( in->BandFmt == VIPS_FORMAT_UCHAR ) {
|
||||
/* The hist path is always faster for windows larger than about
|
||||
* 10x10, and faster for >3x3 on the non-max/min case.
|
||||
*/
|
||||
if( rank->n > 90 )
|
||||
rank->hist_path = TRUE;
|
||||
else if( rank->n > 10 &&
|
||||
rank->index != 0 &&
|
||||
rank->index != rank->n - 1 )
|
||||
rank->hist_path = TRUE;
|
||||
}
|
||||
|
||||
/* Expand the input.
|
||||
*/
|
||||
if( vips_embed( in, &t[1],
|
||||
|
@ -97,7 +97,7 @@ vips__correl( VipsImage *ref, VipsImage *sec,
|
||||
{
|
||||
VipsImage *surface = vips_image_new();
|
||||
VipsImage **t = (VipsImage **)
|
||||
vips_object_local_array( VIPS_OBJECT( surface ), 4 );
|
||||
vips_object_local_array( VIPS_OBJECT( surface ), 5 );
|
||||
|
||||
VipsRect refr, secr;
|
||||
VipsRect winr, srhr;
|
||||
@ -137,35 +137,37 @@ vips__correl( VipsImage *ref, VipsImage *sec,
|
||||
g_object_unref( surface );
|
||||
return( -1 );
|
||||
}
|
||||
ref = t[0];
|
||||
sec = t[1];
|
||||
|
||||
/* Make sure we have just one band. From vips_*mosaic() we will, but
|
||||
* from vips_match() etc. we may not.
|
||||
*/
|
||||
if( t[0]->Bands != 1 ) {
|
||||
if( vips_extract_band( t[0], &t[2], 0, NULL ) ) {
|
||||
if( ref->Bands != 1 ) {
|
||||
if( vips_extract_band( ref, &t[2], 0, NULL ) ) {
|
||||
g_object_unref( surface );
|
||||
return( -1 );
|
||||
}
|
||||
t[0] = t[2];
|
||||
ref = t[2];
|
||||
}
|
||||
if( t[1]->Bands != 1 ) {
|
||||
if( vips_extract_band( t[1], &t[3], 0, NULL ) ) {
|
||||
if( sec->Bands != 1 ) {
|
||||
if( vips_extract_band( sec, &t[3], 0, NULL ) ) {
|
||||
g_object_unref( surface );
|
||||
return( -1 );
|
||||
}
|
||||
t[1] = t[3];
|
||||
sec = t[3];
|
||||
}
|
||||
|
||||
/* Search!
|
||||
*/
|
||||
if( vips_spcor( t[1], t[0], &surface, NULL ) ) {
|
||||
if( vips_spcor( sec, ref, &t[4], NULL ) ) {
|
||||
g_object_unref( surface );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Find maximum of correlation surface.
|
||||
*/
|
||||
if( vips_max( surface, correlation, "x", x, "y", y, NULL ) ) {
|
||||
if( vips_max( t[4], correlation, "x", x, "y", y, NULL ) ) {
|
||||
g_object_unref( surface );
|
||||
return( -1 );
|
||||
}
|
||||
|
@ -175,7 +175,8 @@ vips__global_open_image( SymbolTable *st, char *name )
|
||||
if( !(image = vips_image_new_from_file( name, NULL ))) {
|
||||
/* TODO(kleisauke): Is this behavior the same as im_skip_dir?
|
||||
* i.e. could we open a filename which came
|
||||
* from a win32 (`\\`) on a *nix machine? */
|
||||
* from a win32 (`\\`) on a *nix machine?
|
||||
*/
|
||||
basename = g_path_get_basename( name );
|
||||
|
||||
if( !(image = vips_image_new_from_file( basename, NULL ))) {
|
||||
@ -1018,7 +1019,8 @@ extract_rect( VipsImage *in, VipsImage **out, VipsRect *r )
|
||||
* has 255 for every pixel where both images are non-zero.
|
||||
*/
|
||||
static int
|
||||
make_overlap_mask( VipsImage *mem, VipsImage *ref, VipsImage *sec, VipsImage **mask,
|
||||
make_overlap_mask( VipsImage *mem,
|
||||
VipsImage *ref, VipsImage *sec, VipsImage **mask,
|
||||
VipsRect *rarea, VipsRect *sarea )
|
||||
{
|
||||
VipsImage **t = (VipsImage **)
|
||||
@ -1054,12 +1056,12 @@ count_nonzero( VipsImage *in, gint64 *count )
|
||||
* mask is true.
|
||||
*/
|
||||
static VipsImage *
|
||||
find_image_stats( VipsImage *mem, VipsImage *in, VipsImage *mask, VipsRect *area )
|
||||
find_image_stats( VipsImage *mem,
|
||||
VipsImage *in, VipsImage *mask, VipsRect *area )
|
||||
{
|
||||
VipsImage **t = (VipsImage **)
|
||||
vips_object_local_array( VIPS_OBJECT( mem ), 4 );
|
||||
vips_object_local_array( VIPS_OBJECT( mem ), 5 );
|
||||
|
||||
VipsImage *stats;
|
||||
gint64 count;
|
||||
|
||||
/* Extract area, build black image, mask out pixels we want.
|
||||
@ -1074,7 +1076,7 @@ find_image_stats( VipsImage *mem, VipsImage *in, VipsImage *mask, VipsRect *area
|
||||
|
||||
/* Get stats from masked image.
|
||||
*/
|
||||
if( vips_stats( t[3], &stats, NULL ) )
|
||||
if( vips_stats( t[3], &t[4], NULL ) )
|
||||
return( NULL );
|
||||
|
||||
/* Number of non-zero pixels in mask.
|
||||
@ -1084,19 +1086,20 @@ find_image_stats( VipsImage *mem, VipsImage *in, VipsImage *mask, VipsRect *area
|
||||
|
||||
/* And scale masked average to match.
|
||||
*/
|
||||
*VIPS_MATRIX( stats, 4, 0 ) *= (double) count / VIPS_IMAGE_N_PELS( mask );
|
||||
*VIPS_MATRIX( t[4], 4, 0 ) *=
|
||||
(double) count / VIPS_IMAGE_N_PELS( mask );
|
||||
|
||||
/* Yuk! Zap the deviation column with the pixel count. Used later to
|
||||
* determine if this is likely to be a significant overlap.
|
||||
*/
|
||||
*VIPS_MATRIX( stats, 5, 0 ) = count;
|
||||
*VIPS_MATRIX( t[4], 5, 0 ) = count;
|
||||
|
||||
#ifdef DEBUG
|
||||
if( count == 0 )
|
||||
g_warning( "global_balance %s", _( "empty overlap!" ) );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
return( stats );
|
||||
return( t[4] );
|
||||
}
|
||||
|
||||
/* Find the stats for an overlap struct.
|
||||
@ -1767,7 +1770,7 @@ transform( JoinNode *node, double *gamma )
|
||||
VipsImage *in = node->im;
|
||||
double fac = st->fac[node->index];
|
||||
VipsImage **t = (VipsImage **)
|
||||
vips_object_local_array( VIPS_OBJECT( st->im ), 5 );
|
||||
vips_object_local_array( VIPS_OBJECT( st->im ), 8 );
|
||||
|
||||
VipsImage *out;
|
||||
|
||||
@ -1776,7 +1779,8 @@ transform( JoinNode *node, double *gamma )
|
||||
*/
|
||||
out = in;
|
||||
}
|
||||
/* TODO(kleisauke): Could we call vips_gamma instead? */
|
||||
/* TODO(kleisauke): Could we call vips_gamma instead?
|
||||
*/
|
||||
else if( in->BandFmt == VIPS_FORMAT_UCHAR ||
|
||||
in->BandFmt == VIPS_FORMAT_USHORT ) {
|
||||
if( vips_identity( &t[0],
|
||||
@ -1789,15 +1793,17 @@ transform( JoinNode *node, double *gamma )
|
||||
vips_linear1( t[1], &t[2], fac, 0.0, NULL ) ||
|
||||
vips_pow_const1( t[2], &t[3], *gamma, NULL ) ||
|
||||
vips_cast( t[3], &t[4], in->BandFmt, NULL ) ||
|
||||
vips_maplut( in, &out, t[4], NULL ) )
|
||||
vips_maplut( in, &t[5], t[4], NULL ) )
|
||||
return( NULL );
|
||||
out = t[5];
|
||||
}
|
||||
else {
|
||||
/* Just vips_linear1 it.
|
||||
*/
|
||||
if( vips_linear1( in, &t[0], fac, 0.0, NULL ) ||
|
||||
vips_cast( t[0], &out, in->BandFmt, NULL ) )
|
||||
if( vips_linear1( in, &t[6], fac, 0.0, NULL ) ||
|
||||
vips_cast( t[6], &t[7], in->BandFmt, NULL ) )
|
||||
return( NULL );
|
||||
out = t[7];
|
||||
}
|
||||
|
||||
vips_image_set_string( out, "mosaic-name", node->name );
|
||||
@ -1814,7 +1820,7 @@ transformf( JoinNode *node, double *gamma )
|
||||
VipsImage *in = node->im;
|
||||
double fac = node->st->fac[node->index];
|
||||
VipsImage **t = (VipsImage **)
|
||||
vips_object_local_array( VIPS_OBJECT( st->im ), 4 );
|
||||
vips_object_local_array( VIPS_OBJECT( st->im ), 6 );
|
||||
|
||||
VipsImage *out;
|
||||
|
||||
@ -1834,14 +1840,16 @@ transformf( JoinNode *node, double *gamma )
|
||||
1.0 / (*gamma), NULL ) ||
|
||||
vips_linear1( t[1], &t[2], fac, 0.0, NULL ) ||
|
||||
vips_pow_const1( t[2], &t[3], *gamma, NULL ) ||
|
||||
vips_maplut( in, &out, t[3], NULL ) )
|
||||
vips_maplut( in, &t[4], t[3], NULL ) )
|
||||
return( NULL );
|
||||
out = t[4];
|
||||
}
|
||||
else {
|
||||
/* Just vips_linear1 it.
|
||||
*/
|
||||
if( vips_linear1( in, &out, fac, 0.0, NULL ) )
|
||||
if( vips_linear1( in, &t[5], fac, 0.0, NULL ) )
|
||||
return( NULL );
|
||||
out = t[5];
|
||||
}
|
||||
|
||||
vips_image_set_string( out, "mosaic-name", node->name );
|
||||
|
@ -7,6 +7,8 @@
|
||||
* - a bit quicker
|
||||
* 17/12/18
|
||||
* - we were not offsetting pixel fetches by window_offset
|
||||
* 30/1/21 afontenot
|
||||
* - avoid NaN
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -78,7 +80,7 @@ G_DEFINE_TYPE( VipsMapim, vips_mapim, VIPS_TYPE_RESAMPLE );
|
||||
/* Minmax of a line of pixels. Pass in a thing to convert back to int
|
||||
* coordinates.
|
||||
*/
|
||||
#define MINMAX( TYPE, CLIP_LOW, CLIP_HIGH ) { \
|
||||
#define MINMAX( TYPE, CLIP ) { \
|
||||
TYPE * restrict p1 = (TYPE *) p; \
|
||||
TYPE t_max_x = max_x; \
|
||||
TYPE t_min_x = min_x; \
|
||||
@ -112,10 +114,57 @@ G_DEFINE_TYPE( VipsMapim, vips_mapim, VIPS_TYPE_RESAMPLE );
|
||||
p1 += 2; \
|
||||
} \
|
||||
\
|
||||
min_x = CLIP_LOW( t_min_x ); \
|
||||
max_x = CLIP_HIGH( t_max_x ); \
|
||||
min_y = CLIP_LOW( t_min_y ); \
|
||||
max_y = CLIP_HIGH( t_max_y ); \
|
||||
min_x = CLIP( t_min_x ); \
|
||||
max_x = CLIP( t_max_x ); \
|
||||
min_y = CLIP( t_min_y ); \
|
||||
max_y = CLIP( t_max_y ); \
|
||||
}
|
||||
|
||||
/* Minmax of a line of float pixels. We have to ignore NaN.
|
||||
*/
|
||||
#define FMINMAX( TYPE ) { \
|
||||
TYPE * restrict p1 = (TYPE *) p; \
|
||||
TYPE t_max_x = max_x; \
|
||||
TYPE t_min_x = min_x; \
|
||||
TYPE t_max_y = max_y; \
|
||||
TYPE t_min_y = min_y; \
|
||||
\
|
||||
for( x = 0; x < r->width; x++ ) { \
|
||||
TYPE px = p1[0]; \
|
||||
TYPE py = p1[1]; \
|
||||
\
|
||||
if( !VIPS_ISNAN( px ) && \
|
||||
!VIPS_ISNAN( py ) ) { \
|
||||
if( first ) { \
|
||||
t_min_x = px; \
|
||||
t_max_x = px; \
|
||||
t_min_y = py; \
|
||||
t_max_y = py; \
|
||||
\
|
||||
first = FALSE; \
|
||||
} \
|
||||
else { \
|
||||
if( px > t_max_x ) \
|
||||
t_max_x = px; \
|
||||
else if( px < t_min_x ) \
|
||||
t_min_x = px; \
|
||||
\
|
||||
if( py > t_max_y ) \
|
||||
t_max_y = py; \
|
||||
else if( py < t_min_y ) \
|
||||
t_min_y = py; \
|
||||
} \
|
||||
}\
|
||||
\
|
||||
p1 += 2; \
|
||||
} \
|
||||
\
|
||||
if( !first ) { \
|
||||
min_x = VIPS_CLIP( 0, floor( t_min_x ), VIPS_MAX_COORD ); \
|
||||
max_x = VIPS_CLIP( 0, floor( t_max_x ), VIPS_MAX_COORD ); \
|
||||
min_y = VIPS_CLIP( 0, floor( t_min_y ), VIPS_MAX_COORD ); \
|
||||
max_y = VIPS_CLIP( 0, floor( t_max_y ), VIPS_MAX_COORD ); \
|
||||
} \
|
||||
}
|
||||
|
||||
/* All the clippers. These vary with TYPE.
|
||||
@ -137,11 +186,6 @@ G_DEFINE_TYPE( VipsMapim, vips_mapim, VIPS_TYPE_RESAMPLE );
|
||||
*/
|
||||
#define CLIP_SINT_LARGE( X ) VIPS_CLIP( 0, X, VIPS_MAX_COORD );
|
||||
|
||||
/* Float types must clip the range, and also round up or down at the extremes.
|
||||
*/
|
||||
#define CLIP_FLOAT_LOW( X ) VIPS_CLIP( 0, floor( X ), VIPS_MAX_COORD );
|
||||
#define CLIP_FLOAT_HIGH( X ) VIPS_CLIP( 0, ceil( X ), VIPS_MAX_COORD );
|
||||
|
||||
/* Scan a region and find min/max in the two axes.
|
||||
*/
|
||||
static void
|
||||
@ -165,45 +209,37 @@ vips_mapim_region_minmax( VipsRegion *region, VipsRect *r, VipsRect *bounds )
|
||||
|
||||
switch( region->im->BandFmt ) {
|
||||
case VIPS_FORMAT_UCHAR:
|
||||
MINMAX( unsigned char,
|
||||
CLIP_UINT_SMALL, CLIP_UINT_SMALL );
|
||||
MINMAX( unsigned char, CLIP_UINT_SMALL );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_CHAR:
|
||||
MINMAX( signed char,
|
||||
CLIP_SINT_SMALL, CLIP_SINT_SMALL );
|
||||
MINMAX( signed char, CLIP_SINT_SMALL );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_USHORT:
|
||||
MINMAX( unsigned short,
|
||||
CLIP_UINT_SMALL, CLIP_UINT_SMALL );
|
||||
MINMAX( unsigned short, CLIP_UINT_SMALL );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_SHORT:
|
||||
MINMAX( signed short,
|
||||
CLIP_SINT_SMALL, CLIP_SINT_SMALL );
|
||||
MINMAX( signed short, CLIP_SINT_SMALL );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_UINT:
|
||||
MINMAX( unsigned int,
|
||||
CLIP_UINT_LARGE, CLIP_UINT_LARGE );
|
||||
MINMAX( unsigned int, CLIP_UINT_LARGE );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_INT:
|
||||
MINMAX( signed int,
|
||||
CLIP_SINT_LARGE, CLIP_SINT_LARGE );
|
||||
MINMAX( signed int, CLIP_SINT_LARGE );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_FLOAT:
|
||||
case VIPS_FORMAT_COMPLEX:
|
||||
MINMAX( float,
|
||||
CLIP_FLOAT_LOW, CLIP_FLOAT_HIGH );
|
||||
FMINMAX( float );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_DOUBLE:
|
||||
case VIPS_FORMAT_DPCOMPLEX:
|
||||
MINMAX( double,
|
||||
CLIP_FLOAT_LOW, CLIP_FLOAT_HIGH );
|
||||
FMINMAX( double );
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -217,6 +253,8 @@ vips_mapim_region_minmax( VipsRegion *region, VipsRect *r, VipsRect *bounds )
|
||||
bounds->height = max_y - min_y + 1;
|
||||
}
|
||||
|
||||
/* Unsigned int types.
|
||||
*/
|
||||
#define ULOOKUP( TYPE ) { \
|
||||
TYPE * restrict p1 = (TYPE *) p; \
|
||||
\
|
||||
@ -238,6 +276,8 @@ vips_mapim_region_minmax( VipsRegion *region, VipsRect *r, VipsRect *bounds )
|
||||
} \
|
||||
}
|
||||
|
||||
/* Signed int types.
|
||||
*/
|
||||
#define LOOKUP( TYPE ) { \
|
||||
TYPE * restrict p1 = (TYPE *) p; \
|
||||
\
|
||||
@ -261,6 +301,33 @@ vips_mapim_region_minmax( VipsRegion *region, VipsRect *r, VipsRect *bounds )
|
||||
} \
|
||||
}
|
||||
|
||||
/* Float types.
|
||||
*/
|
||||
#define FLOOKUP( TYPE ) { \
|
||||
TYPE * restrict p1 = (TYPE *) p; \
|
||||
\
|
||||
for( x = 0; x < r->width; x++ ) { \
|
||||
TYPE px = p1[0]; \
|
||||
TYPE py = p1[1]; \
|
||||
\
|
||||
if( VIPS_ISNAN( px ) || \
|
||||
VIPS_ISNAN( py ) || \
|
||||
px < 0 || \
|
||||
px >= clip_width || \
|
||||
py < 0 || \
|
||||
py >= clip_height ) { \
|
||||
for( z = 0; z < ps; z++ ) \
|
||||
q[z] = 0; \
|
||||
} \
|
||||
else \
|
||||
interpolate( mapim->interpolate, q, ir[0], \
|
||||
px + window_offset, py + window_offset ); \
|
||||
\
|
||||
p1 += 2; \
|
||||
q += ps; \
|
||||
} \
|
||||
}
|
||||
|
||||
static int
|
||||
vips_mapim_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
|
||||
{
|
||||
@ -306,7 +373,6 @@ vips_mapim_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
|
||||
|
||||
/* The bounding box of that area is what we will need from @in. Add
|
||||
* enough for the interpolation stencil as well.
|
||||
*
|
||||
*/
|
||||
bounds.width += window_size - 1;
|
||||
bounds.height += window_size - 1;
|
||||
@ -361,12 +427,12 @@ vips_mapim_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
|
||||
|
||||
case VIPS_FORMAT_FLOAT:
|
||||
case VIPS_FORMAT_COMPLEX:
|
||||
LOOKUP( float ); break;
|
||||
FLOOKUP( float ); break;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_DOUBLE:
|
||||
case VIPS_FORMAT_DPCOMPLEX:
|
||||
LOOKUP( double ); break;
|
||||
FLOOKUP( double ); break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
|
@ -30,6 +30,9 @@
|
||||
* - add thumbnail_source
|
||||
* 2/6/20
|
||||
* - add subifd pyr support
|
||||
* 27/2/21
|
||||
* - simplify rules re. processing space, colour management and linear
|
||||
* mode
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -624,16 +627,19 @@ vips_thumbnail_build( VipsObject *object )
|
||||
int preshrunk_page_height;
|
||||
double hshrink;
|
||||
double vshrink;
|
||||
VipsInterpretation interpretation;
|
||||
|
||||
/* TRUE if we've done the import of an ICC transform and still need to
|
||||
* export.
|
||||
*/
|
||||
gboolean have_imported;
|
||||
|
||||
/* TRUE if we've premultiplied and need to unpremultiply.
|
||||
/* If we shrink in linear space, we need to return to the input
|
||||
* colourspace after the shrink.
|
||||
*/
|
||||
VipsInterpretation input_interpretation;
|
||||
|
||||
/* The format we need to revert to after unpremultiply.
|
||||
*/
|
||||
gboolean have_premultiplied;
|
||||
VipsBandFormat unpremultiplied_format;
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -679,60 +685,78 @@ vips_thumbnail_build( VipsObject *object )
|
||||
in = t[12];
|
||||
}
|
||||
|
||||
/* In linear mode, we import right at the start.
|
||||
*
|
||||
* We also have to import the whole image if it's CMYK, since
|
||||
* vips_colourspace() (see below) doesn't let you specify the fallback
|
||||
* profile.
|
||||
*
|
||||
* This is only going to work for images in device space. If you have
|
||||
* an image in PCS which also has an attached profile, strange things
|
||||
* will happen.
|
||||
/* In linear mode, we need to transform to a linear space before
|
||||
* vips_resize().
|
||||
*/
|
||||
have_imported = FALSE;
|
||||
if( thumbnail->linear &&
|
||||
in->Coding == VIPS_CODING_NONE &&
|
||||
(in->BandFmt == VIPS_FORMAT_UCHAR ||
|
||||
in->BandFmt == VIPS_FORMAT_USHORT) &&
|
||||
(vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ||
|
||||
thumbnail->import_profile) ) {
|
||||
g_info( "importing to XYZ PCS" );
|
||||
if( thumbnail->import_profile )
|
||||
g_info( "fallback input profile %s",
|
||||
thumbnail->import_profile );
|
||||
if( thumbnail->linear ) {
|
||||
/* If we are doing colour management (there's an import
|
||||
* profile), then we can use XYZ PCS as the resize space.
|
||||
*/
|
||||
if( in->Coding == VIPS_CODING_NONE &&
|
||||
(in->BandFmt == VIPS_FORMAT_UCHAR ||
|
||||
in->BandFmt == VIPS_FORMAT_USHORT) &&
|
||||
(vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ||
|
||||
thumbnail->import_profile) ) {
|
||||
g_info( "importing to XYZ PCS" );
|
||||
if( thumbnail->import_profile )
|
||||
g_info( "fallback input profile %s",
|
||||
thumbnail->import_profile );
|
||||
|
||||
if( vips_icc_import( in, &t[1],
|
||||
"input_profile", thumbnail->import_profile,
|
||||
"embedded", TRUE,
|
||||
"intent", thumbnail->intent,
|
||||
"pcs", VIPS_PCS_XYZ,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
if( vips_icc_import( in, &t[1],
|
||||
"input_profile", thumbnail->import_profile,
|
||||
"embedded", TRUE,
|
||||
"intent", thumbnail->intent,
|
||||
"pcs", VIPS_PCS_XYZ,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
in = t[1];
|
||||
|
||||
in = t[1];
|
||||
have_imported = TRUE;
|
||||
}
|
||||
else {
|
||||
/* Otherwise, use scRGB or GREY16 for linear shrink.
|
||||
*/
|
||||
VipsInterpretation interpretation;
|
||||
|
||||
have_imported = TRUE;
|
||||
/* Note the interpretation we will revert to after
|
||||
* linear.
|
||||
*/
|
||||
input_interpretation = in->Type;
|
||||
|
||||
if( in->Bands < 3 )
|
||||
interpretation = VIPS_INTERPRETATION_GREY16;
|
||||
else
|
||||
interpretation = VIPS_INTERPRETATION_scRGB;
|
||||
|
||||
g_info( "converting to processing space %s",
|
||||
vips_enum_nick( VIPS_TYPE_INTERPRETATION,
|
||||
interpretation ) );
|
||||
if( vips_colourspace( in, &t[2], interpretation,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
in = t[2];
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* In non-linear mode, use sRGB or B_W as the processing
|
||||
* space.
|
||||
*/
|
||||
VipsInterpretation interpretation;
|
||||
|
||||
/* To the processing colourspace. This will unpack LABQ, import CMYK,
|
||||
* etc.
|
||||
*
|
||||
* If this is a CMYK image, we need to set have_imported since we only
|
||||
* want to export at the end.
|
||||
*/
|
||||
if( in->Type == VIPS_INTERPRETATION_CMYK )
|
||||
have_imported = TRUE;
|
||||
if( thumbnail->linear )
|
||||
interpretation = VIPS_INTERPRETATION_scRGB;
|
||||
else if( in->Bands < 3 )
|
||||
interpretation = VIPS_INTERPRETATION_B_W;
|
||||
else
|
||||
interpretation = VIPS_INTERPRETATION_sRGB;
|
||||
g_info( "converting to processing space %s",
|
||||
vips_enum_nick( VIPS_TYPE_INTERPRETATION, interpretation ) );
|
||||
if( vips_colourspace( in, &t[2], interpretation, NULL ) )
|
||||
return( -1 );
|
||||
in = t[2];
|
||||
if( in->Bands < 3 )
|
||||
interpretation = VIPS_INTERPRETATION_B_W;
|
||||
else
|
||||
interpretation = VIPS_INTERPRETATION_sRGB;
|
||||
|
||||
g_info( "converting to processing space %s",
|
||||
vips_enum_nick( VIPS_TYPE_INTERPRETATION,
|
||||
interpretation ) );
|
||||
if( vips_colourspace( in, &t[2], interpretation,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
in = t[2];
|
||||
}
|
||||
|
||||
/* Shrink to preshrunk_page_height, so we work for multi-page images.
|
||||
*/
|
||||
@ -751,23 +775,23 @@ vips_thumbnail_build( VipsObject *object )
|
||||
vshrink = (double) in->Ysize / target_image_height;
|
||||
}
|
||||
|
||||
/* vips_premultiply() makes a float image, so when we unpremultiply
|
||||
* below we must cast back to the original format. Use NOTSET to
|
||||
* meran no pre/unmultiply.
|
||||
*/
|
||||
unpremultiplied_format = VIPS_FORMAT_NOTSET;
|
||||
|
||||
/* If there's an alpha, we have to premultiply before shrinking. See
|
||||
* https://github.com/libvips/libvips/issues/291
|
||||
*/
|
||||
have_premultiplied = FALSE;
|
||||
if( vips_image_hasalpha( in ) &&
|
||||
hshrink != 1.0 &&
|
||||
vshrink != 1.0 ) {
|
||||
g_info( "premultiplying alpha" );
|
||||
unpremultiplied_format = in->BandFmt;
|
||||
|
||||
if( vips_premultiply( in, &t[3], NULL ) )
|
||||
return( -1 );
|
||||
have_premultiplied = TRUE;
|
||||
|
||||
/* vips_premultiply() makes a float image. When we
|
||||
* vips_unpremultiply() below, we need to cast back to the
|
||||
* pre-premultiply format.
|
||||
*/
|
||||
unpremultiplied_format = in->BandFmt;
|
||||
in = t[3];
|
||||
}
|
||||
|
||||
@ -777,6 +801,14 @@ vips_thumbnail_build( VipsObject *object )
|
||||
return( -1 );
|
||||
in = t[4];
|
||||
|
||||
if( unpremultiplied_format != VIPS_FORMAT_NOTSET ) {
|
||||
g_info( "unpremultiplying alpha" );
|
||||
if( vips_unpremultiply( in, &t[5], NULL ) ||
|
||||
vips_cast( t[5], &t[6], unpremultiplied_format, NULL ) )
|
||||
return( -1 );
|
||||
in = t[6];
|
||||
}
|
||||
|
||||
/* Only set page-height if we have more than one page, or this could
|
||||
* accidentally turn into an animated image later.
|
||||
*/
|
||||
@ -792,21 +824,12 @@ vips_thumbnail_build( VipsObject *object )
|
||||
VIPS_META_PAGE_HEIGHT, output_page_height );
|
||||
}
|
||||
|
||||
if( have_premultiplied ) {
|
||||
g_info( "unpremultiplying alpha" );
|
||||
if( vips_unpremultiply( in, &t[5], NULL ) ||
|
||||
vips_cast( t[5], &t[6], unpremultiplied_format, NULL ) )
|
||||
return( -1 );
|
||||
in = t[6];
|
||||
}
|
||||
|
||||
/* Colour management.
|
||||
*
|
||||
* If we've already imported, just export. Otherwise, we're in
|
||||
* device space and we need a combined import/export to transform to
|
||||
* the target space.
|
||||
*/
|
||||
if( have_imported ) {
|
||||
/* We've already imported, just export. Go to sRGB if there's
|
||||
* no export profile.
|
||||
*/
|
||||
if( thumbnail->export_profile ||
|
||||
vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) {
|
||||
g_info( "exporting to device space with a profile" );
|
||||
@ -825,21 +848,54 @@ vips_thumbnail_build( VipsObject *object )
|
||||
in = t[7];
|
||||
}
|
||||
}
|
||||
else if( thumbnail->export_profile &&
|
||||
(vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ||
|
||||
thumbnail->import_profile) ) {
|
||||
else if( thumbnail->export_profile ) {
|
||||
/* Not imported, but we are doing colour management. Transform
|
||||
* to the output space.
|
||||
*/
|
||||
g_info( "transforming to %s", thumbnail->export_profile );
|
||||
if( thumbnail->import_profile )
|
||||
g_info( "fallback input profile %s",
|
||||
thumbnail->import_profile );
|
||||
|
||||
if( vips_icc_transform( in, &t[7],
|
||||
thumbnail->export_profile,
|
||||
"input_profile", thumbnail->import_profile,
|
||||
"intent", thumbnail->intent,
|
||||
"embedded", TRUE,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
/* If there's some kind of import profile, we can transform to
|
||||
* the output. Otherwise we have to convert to PCS and then
|
||||
* export.
|
||||
*/
|
||||
if( thumbnail->import_profile ||
|
||||
(vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ||
|
||||
thumbnail->import_profile) ) {
|
||||
g_info( "transforming with supplied profiles" );
|
||||
if( vips_icc_transform( in, &t[7],
|
||||
thumbnail->export_profile,
|
||||
"input_profile", thumbnail->import_profile,
|
||||
"intent", thumbnail->intent,
|
||||
"embedded", TRUE,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
|
||||
in = t[7];
|
||||
}
|
||||
else {
|
||||
g_info( "exporting with %s",
|
||||
thumbnail->export_profile );
|
||||
if( vips_colourspace( in, &t[7],
|
||||
VIPS_INTERPRETATION_XYZ, NULL ) ||
|
||||
vips_icc_export( t[7], &t[8],
|
||||
"output_profile",
|
||||
thumbnail->export_profile,
|
||||
"intent", thumbnail->intent,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
in = t[8];
|
||||
}
|
||||
}
|
||||
else if( thumbnail->linear ) {
|
||||
/* Linear mode, no colour management. We went to scRGB for
|
||||
* processing, so we now revert to the input colourspace.
|
||||
*/
|
||||
g_info( "reverting to input space %s",
|
||||
vips_enum_nick( VIPS_TYPE_INTERPRETATION,
|
||||
input_interpretation ) );
|
||||
if( vips_colourspace( in, &t[7],
|
||||
input_interpretation, NULL ) )
|
||||
return( -1 );
|
||||
in = t[7];
|
||||
}
|
||||
|
||||
@ -1368,7 +1424,7 @@ vips_thumbnail_buffer_init( VipsThumbnailBuffer *buffer )
|
||||
* * @intent: #VipsIntent, rendering intent
|
||||
* * @option_string: %gchararray, extra loader options
|
||||
*
|
||||
* Exacty as vips_thumbnail(), but read from a memory buffer. One extra
|
||||
* Exactly as vips_thumbnail(), but read from a memory buffer. One extra
|
||||
* optional argument, @option_string, lets you pass options to the underlying
|
||||
* loader.
|
||||
*
|
||||
@ -1564,7 +1620,7 @@ vips_thumbnail_source_init( VipsThumbnailSource *source )
|
||||
* * @intent: #VipsIntent, rendering intent
|
||||
* * @option_string: %gchararray, extra loader options
|
||||
*
|
||||
* Exacty as vips_thumbnail(), but read from a source. One extra
|
||||
* Exactly as vips_thumbnail(), but read from a source. One extra
|
||||
* optional argument, @option_string, lets you pass options to the underlying
|
||||
* loader.
|
||||
*
|
||||
@ -1671,7 +1727,7 @@ vips_thumbnail_image_init( VipsThumbnailImage *image )
|
||||
* * @export_profile: %gchararray, export ICC profile
|
||||
* * @intent: #VipsIntent, rendering intent
|
||||
*
|
||||
* Exacty as vips_thumbnail(), but read from an existing image.
|
||||
* Exactly as vips_thumbnail(), but read from an existing image.
|
||||
*
|
||||
* This operation
|
||||
* is not able to exploit shrink-on-load features of image load libraries, so
|
||||
|
@ -3,5 +3,6 @@ leak:python3
|
||||
leak:bash
|
||||
leak:libfontconfig.so
|
||||
leak:libglib-2.0.so
|
||||
leak:libIlmImf-2_2.so
|
||||
leak:libIlmImf-2_3.so
|
||||
leak:libIlmThread-2_3.so
|
||||
leak:libstdc++.so
|
||||
|
@ -41,7 +41,10 @@ DICOM_FILE = os.path.join(IMAGES, "dicom_test_image.dcm")
|
||||
BMP_FILE = os.path.join(IMAGES, "MARBLES.BMP")
|
||||
NIFTI_FILE = os.path.join(IMAGES, "avg152T1_LR_nifti.nii.gz")
|
||||
ICO_FILE = os.path.join(IMAGES, "favicon.ico")
|
||||
AVIF_FILE = os.path.join(IMAGES, "avif-orientation-6.avif")
|
||||
HEIC_FILE = os.path.join(IMAGES, "heic-orientation-6.heic")
|
||||
RGBA_FILE = os.path.join(IMAGES, "rgba.png")
|
||||
RGBA_CORRECT_FILE = os.path.join(IMAGES, "rgba-correct.ppm")
|
||||
MOSAIC_FILES = [os.path.join(IMAGES, "cd1.1.jpg"), os.path.join(IMAGES, "cd1.2.jpg"),
|
||||
os.path.join(IMAGES, "cd2.1.jpg"), os.path.join(IMAGES, "cd2.2.jpg"),
|
||||
os.path.join(IMAGES, "cd3.1.jpg"), os.path.join(IMAGES, "cd3.2.jpg"),
|
||||
|
Binary file not shown.
BIN
test/test-suite/images/avif-orientation-6.avif
Normal file
BIN
test/test-suite/images/avif-orientation-6.avif
Normal file
Binary file not shown.
After Width: | Height: | Size: 158 KiB |
Binary file not shown.
5
test/test-suite/images/rgba-correct.ppm
Normal file
5
test/test-suite/images/rgba-correct.ppm
Normal file
File diff suppressed because one or more lines are too long
BIN
test/test-suite/images/rgba.png
Normal file
BIN
test/test-suite/images/rgba.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
@ -72,6 +72,8 @@ class TestCreate:
|
||||
assert im.max() == 255.0
|
||||
assert im.min() == 0.0
|
||||
|
||||
@pytest.mark.skipif(pyvips.type_find("VipsOperation", "fwfft") == 0,
|
||||
reason="no FFTW, skipping test")
|
||||
def test_fractsurf(self):
|
||||
im = pyvips.Image.fractsurf(100, 90, 2.5)
|
||||
assert im.width == 100
|
||||
|
@ -11,7 +11,7 @@ from helpers import \
|
||||
JPEG_FILE, SRGB_FILE, MATLAB_FILE, PNG_FILE, TIF_FILE, OME_FILE, \
|
||||
ANALYZE_FILE, GIF_FILE, WEBP_FILE, EXR_FILE, FITS_FILE, OPENSLIDE_FILE, \
|
||||
PDF_FILE, SVG_FILE, SVGZ_FILE, SVG_GZ_FILE, GIF_ANIM_FILE, DICOM_FILE, \
|
||||
BMP_FILE, NIFTI_FILE, ICO_FILE, HEIC_FILE, TRUNCATED_FILE, \
|
||||
BMP_FILE, NIFTI_FILE, ICO_FILE, AVIF_FILE, TRUNCATED_FILE, \
|
||||
GIF_ANIM_EXPECTED_PNG_FILE, GIF_ANIM_DISPOSE_BACKGROUND_FILE, \
|
||||
GIF_ANIM_DISPOSE_BACKGROUND_EXPECTED_PNG_FILE, \
|
||||
GIF_ANIM_DISPOSE_PREVIOUS_FILE, \
|
||||
@ -75,7 +75,7 @@ class TestForeign:
|
||||
max_diff = (im - x).abs().max()
|
||||
assert max_diff == 0
|
||||
|
||||
def save_load_file(self, format, options, im, thresh):
|
||||
def save_load_file(self, format, options, im, max_diff=0):
|
||||
# yuk!
|
||||
# but we can't set format parameters for pyvips.Image.new_temp_file()
|
||||
filename = temp_filename(self.tempdir, format)
|
||||
@ -86,8 +86,7 @@ class TestForeign:
|
||||
assert im.width == x.width
|
||||
assert im.height == x.height
|
||||
assert im.bands == x.bands
|
||||
max_diff = (im - x).abs().max()
|
||||
assert max_diff <= thresh
|
||||
assert (im - x).abs().max() <= max_diff
|
||||
x = None
|
||||
|
||||
def save_load_buffer(self, saver, loader, im, max_diff=0, **kwargs):
|
||||
@ -115,7 +114,7 @@ class TestForeign:
|
||||
assert (im - x).abs().max() <= max_diff
|
||||
|
||||
def test_vips(self):
|
||||
self.save_load_file(".v", "", self.colour, 0)
|
||||
self.save_load_file(".v", "", self.colour)
|
||||
|
||||
# check we can save and restore metadata
|
||||
filename = temp_filename(self.tempdir, ".v")
|
||||
@ -321,8 +320,8 @@ class TestForeign:
|
||||
self.save_load_buffer("pngsave_buffer", "pngload_buffer", self.colour)
|
||||
self.save_load("%s.png", self.mono)
|
||||
self.save_load("%s.png", self.colour)
|
||||
self.save_load_file(".png", "[interlace]", self.colour, 0)
|
||||
self.save_load_file(".png", "[interlace]", self.mono, 0)
|
||||
self.save_load_file(".png", "[interlace]", self.colour)
|
||||
self.save_load_file(".png", "[interlace]", self.mono)
|
||||
|
||||
# size of a regular mono PNG
|
||||
len_mono = len(self.mono.write_to_buffer(".png"))
|
||||
@ -391,30 +390,30 @@ class TestForeign:
|
||||
self.save_load("%s.tif", self.cmyk)
|
||||
|
||||
self.save_load("%s.tif", self.onebit)
|
||||
self.save_load_file(".tif", "[bitdepth=1]", self.onebit, 0)
|
||||
self.save_load_file(".tif", "[miniswhite]", self.onebit, 0)
|
||||
self.save_load_file(".tif", "[bitdepth=1,miniswhite]", self.onebit, 0)
|
||||
self.save_load_file(".tif", "[bitdepth=1]", self.onebit)
|
||||
self.save_load_file(".tif", "[miniswhite]", self.onebit)
|
||||
self.save_load_file(".tif", "[bitdepth=1,miniswhite]", self.onebit)
|
||||
|
||||
self.save_load_file(".tif",
|
||||
"[profile={0}]".format(SRGB_FILE),
|
||||
self.colour, 0)
|
||||
self.save_load_file(".tif", "[tile]", self.colour, 0)
|
||||
self.save_load_file(".tif", "[tile,pyramid]", self.colour, 0)
|
||||
self.save_load_file(".tif", "[tile,pyramid,subifd]", self.colour, 0)
|
||||
self.colour)
|
||||
self.save_load_file(".tif", "[tile]", self.colour)
|
||||
self.save_load_file(".tif", "[tile,pyramid]", self.colour)
|
||||
self.save_load_file(".tif", "[tile,pyramid,subifd]", self.colour)
|
||||
self.save_load_file(".tif",
|
||||
"[tile,pyramid,compression=jpeg]", self.colour, 80)
|
||||
self.save_load_file(".tif",
|
||||
"[tile,pyramid,subifd,compression=jpeg]",
|
||||
self.colour, 80)
|
||||
self.save_load_file(".tif", "[bigtiff]", self.colour, 0)
|
||||
self.save_load_file(".tif", "[bigtiff]", self.colour)
|
||||
self.save_load_file(".tif", "[compression=jpeg]", self.colour, 80)
|
||||
self.save_load_file(".tif",
|
||||
"[tile,tile-width=256]", self.colour, 10)
|
||||
|
||||
im = pyvips.Image.new_from_file(TIF2_FILE)
|
||||
self.save_load_file(".tif", "[bitdepth=2]", im, 0)
|
||||
self.save_load_file(".tif", "[bitdepth=2]", im)
|
||||
im = pyvips.Image.new_from_file(TIF4_FILE)
|
||||
self.save_load_file(".tif", "[bitdepth=4]", im, 0)
|
||||
self.save_load_file(".tif", "[bitdepth=4]", im)
|
||||
|
||||
filename = temp_filename(self.tempdir, '.tif')
|
||||
x = pyvips.Image.new_from_file(TIF_FILE)
|
||||
@ -1072,37 +1071,47 @@ class TestForeign:
|
||||
def test_heifload(self):
|
||||
def heif_valid(im):
|
||||
a = im(10, 10)
|
||||
# different versions of HEIC decode have slightly different
|
||||
# different versions of libheif decode have slightly different
|
||||
# rounding
|
||||
assert_almost_equal_objects(a, [197.0, 181.0, 158.0], threshold=2)
|
||||
assert im.width == 3024
|
||||
assert im.height == 4032
|
||||
assert im.bands == 3
|
||||
|
||||
self.file_loader("heifload", HEIC_FILE, heif_valid)
|
||||
self.buffer_loader("heifload_buffer", HEIC_FILE, heif_valid)
|
||||
self.file_loader("heifload", AVIF_FILE, heif_valid)
|
||||
self.buffer_loader("heifload_buffer", AVIF_FILE, heif_valid)
|
||||
|
||||
@skip_if_no("heifsave")
|
||||
def test_heifsave(self):
|
||||
self.save_load_buffer("heifsave_buffer", "heifload_buffer",
|
||||
self.colour, 80)
|
||||
self.save_load("%s.heic", self.colour)
|
||||
self.colour, 80, compression="av1")
|
||||
# TODO: perhaps we should automatically set the compression to
|
||||
# av1 when we save to *.avif?
|
||||
#self.save_load("%s.avif", self.colour)
|
||||
self.save_load_file(".avif", "[compression=av1]",
|
||||
self.colour, 80)
|
||||
|
||||
# test lossless mode
|
||||
im = pyvips.Image.new_from_file(HEIC_FILE)
|
||||
buf = im.heifsave_buffer(lossless=True)
|
||||
im2 = pyvips.Image.new_from_buffer(buf, "")
|
||||
# uncomment to test lossless mode, will take a while
|
||||
#im = pyvips.Image.new_from_file(AVIF_FILE)
|
||||
#buf = im.heifsave_buffer(lossless=True, compression="av1")
|
||||
#im2 = pyvips.Image.new_from_buffer(buf, "")
|
||||
# not in fact quite lossless
|
||||
assert abs(im.avg() - im2.avg()) < 3
|
||||
#assert abs(im.avg() - im2.avg()) < 3
|
||||
|
||||
# higher Q should mean a bigger buffer
|
||||
b1 = im.heifsave_buffer(Q=10)
|
||||
b2 = im.heifsave_buffer(Q=90)
|
||||
# higher Q should mean a bigger buffer, needs libheif >= v1.8.0,
|
||||
# see: https://github.com/libvips/libvips/issues/1757
|
||||
b1 = self.mono.heifsave_buffer(Q=10, compression="av1")
|
||||
b2 = self.mono.heifsave_buffer(Q=90, compression="av1")
|
||||
assert len(b2) > len(b1)
|
||||
|
||||
# Chroma subsampling should produce smaller file size for same Q
|
||||
b1 = self.colour.heifsave_buffer(compression="av1", subsample_mode="on")
|
||||
b2 = self.colour.heifsave_buffer(compression="av1", subsample_mode="off")
|
||||
assert len(b2) > len(b1)
|
||||
|
||||
# try saving an image with an ICC profile and reading it back
|
||||
# not all libheif have profile support, so put it in an if
|
||||
buf = self.colour.heifsave_buffer()
|
||||
buf = self.colour.heifsave_buffer(Q=10, compression="av1")
|
||||
im = pyvips.Image.new_from_buffer(buf, "")
|
||||
p1 = self.colour.get("icc-profile-data")
|
||||
if im.get_typeof("icc-profile-data") != 0:
|
||||
@ -1113,18 +1122,16 @@ class TestForeign:
|
||||
# the exif test will need us to be able to walk the header,
|
||||
# we can't just check exif-data
|
||||
|
||||
# libheif 1.1 (on ubuntu 18.04, current LTS) does not support exif
|
||||
# write, so this test is commented out
|
||||
|
||||
# test that exif changes change the output of heifsave
|
||||
# first make sure we have exif support
|
||||
#z = pyvips.Image.new_from_file(JPEG_FILE)
|
||||
#if z.get_typeof("exif-ifd0-Orientation") != 0:
|
||||
# x = self.colour.copy()
|
||||
# x.set("exif-ifd0-Make", "banana")
|
||||
# buf = x.heifsave_buffer()
|
||||
# y = pyvips.Image.new_from_buffer(buf, "")
|
||||
# assert y.get("exif-ifd0-Make").split(" ")[0] == "banana"
|
||||
z = pyvips.Image.new_from_file(AVIF_FILE)
|
||||
if z.get_typeof("exif-ifd0-Make") != 0:
|
||||
x = z.copy()
|
||||
x.set("exif-ifd0-Make", "banana")
|
||||
buf = x.heifsave_buffer(Q=10, compression="av1")
|
||||
y = pyvips.Image.new_from_buffer(buf, "")
|
||||
assert y.get("exif-ifd0-Make").split(" ")[0] == "banana"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main()
|
||||
|
@ -5,11 +5,80 @@ import pyvips
|
||||
from helpers import MOSAIC_FILES, MOSAIC_MARKS, MOSAIC_VERTICAL_MARKS
|
||||
|
||||
class TestMosaicing:
|
||||
|
||||
def test_lrmerge(self):
|
||||
left = pyvips.Image.new_from_file(MOSAIC_FILES[0])
|
||||
right = pyvips.Image.new_from_file(MOSAIC_FILES[1])
|
||||
join = left.merge(right, 'horizontal', 10 - left.width, 0)
|
||||
|
||||
assert join.width == left.width + right.width - 10
|
||||
assert join.height == max(left.height, right.height)
|
||||
assert join.bands == 1
|
||||
|
||||
def test_tbmerge(self):
|
||||
top = pyvips.Image.new_from_file(MOSAIC_FILES[0])
|
||||
bottom = pyvips.Image.new_from_file(MOSAIC_FILES[2])
|
||||
join = top.merge(bottom, 'vertical', 0, 10 - top.height)
|
||||
|
||||
assert join.width == max(top.width, bottom.width)
|
||||
assert join.height == top.height + bottom.height - 10
|
||||
assert join.bands == 1
|
||||
|
||||
def test_lrmosaic(self):
|
||||
left = pyvips.Image.new_from_file(MOSAIC_FILES[0])
|
||||
right = pyvips.Image.new_from_file(MOSAIC_FILES[1])
|
||||
join = left.mosaic(right, 'horizontal', left.width - 30, 0, 30, 0)
|
||||
|
||||
assert join.width == 1014
|
||||
assert join.height == 379
|
||||
assert join.bands == 1
|
||||
|
||||
def test_tbmosaic(self):
|
||||
top = pyvips.Image.new_from_file(MOSAIC_FILES[0])
|
||||
bottom = pyvips.Image.new_from_file(MOSAIC_FILES[2])
|
||||
join = top.mosaic(bottom, 'vertical', 0, top.height - 30, 0, 30)
|
||||
|
||||
assert join.width == 542
|
||||
assert join.height == 688
|
||||
assert join.bands == 1
|
||||
|
||||
def test_mosaic(self):
|
||||
# ported from https://github.com/libvips/nip2/tree/master/share/nip2/data/examples/1_point_mosaic
|
||||
|
||||
mosaiced_image = None
|
||||
|
||||
for i in range(0, len(MOSAIC_FILES), 2):
|
||||
files = MOSAIC_FILES[i:i + 2]
|
||||
marks = MOSAIC_MARKS[i:i + 2]
|
||||
|
||||
im = pyvips.Image.new_from_file(files[0])
|
||||
sec_im = pyvips.Image.new_from_file(files[1])
|
||||
horizontal_part = im.mosaic(sec_im,
|
||||
pyvips.Direction.HORIZONTAL,
|
||||
marks[0][0], marks[0][1],
|
||||
marks[1][0], marks[1][1])
|
||||
|
||||
if mosaiced_image is None:
|
||||
mosaiced_image = horizontal_part
|
||||
else:
|
||||
vertical_marks = MOSAIC_VERTICAL_MARKS[i - 2:i]
|
||||
mosaiced_image = mosaiced_image.mosaic(horizontal_part,
|
||||
pyvips.Direction.VERTICAL,
|
||||
vertical_marks[1][0], vertical_marks[1][1],
|
||||
vertical_marks[0][0], vertical_marks[0][1])
|
||||
|
||||
# Uncomment to see output file
|
||||
#mosaiced_image.write_to_file('after.jpg')
|
||||
|
||||
# hard to test much more than this
|
||||
assert mosaiced_image.width == 1005
|
||||
assert mosaiced_image.height == 1295
|
||||
assert mosaiced_image.interpretation == pyvips.Interpretation.B_W
|
||||
assert mosaiced_image.bands == 1
|
||||
|
||||
def test_globalbalance(self):
|
||||
mosaiced_image = None
|
||||
|
||||
for i in range(0, len(MOSAIC_FILES), 2):
|
||||
files = MOSAIC_FILES[i:i + 2]
|
||||
marks = MOSAIC_MARKS[i:i + 2]
|
||||
|
@ -2,7 +2,8 @@
|
||||
import pytest
|
||||
|
||||
import pyvips
|
||||
from helpers import JPEG_FILE, OME_FILE, HEIC_FILE, TIF_FILE, all_formats, have
|
||||
from helpers import JPEG_FILE, OME_FILE, HEIC_FILE, TIF_FILE, all_formats, \
|
||||
have, RGBA_FILE, RGBA_CORRECT_FILE, AVIF_FILE
|
||||
|
||||
|
||||
# Run a function expecting a complex image on a two-band image
|
||||
@ -193,10 +194,15 @@ class TestResample:
|
||||
im2 = pyvips.Image.thumbnail_buffer(buf, 100)
|
||||
assert abs(im1.avg() - im2.avg()) < 1
|
||||
|
||||
# linear shrink should work on rgba images
|
||||
im1 = pyvips.Image.thumbnail(RGBA_FILE, 64, linear=True)
|
||||
im2 = pyvips.Image.new_from_file(RGBA_CORRECT_FILE)
|
||||
assert abs(im1.flatten(background=255).avg() - im2.avg()) < 1
|
||||
|
||||
if have("heifload"):
|
||||
# this image is orientation 6 ... thumbnail should flip it
|
||||
im = pyvips.Image.new_from_file(HEIC_FILE)
|
||||
thumb = pyvips.Image.thumbnail(HEIC_FILE, 100)
|
||||
im = pyvips.Image.new_from_file(AVIF_FILE)
|
||||
thumb = pyvips.Image.thumbnail(AVIF_FILE, 100)
|
||||
|
||||
# thumb should be portrait
|
||||
assert thumb.width < thumb.height
|
||||
|
Loading…
x
Reference in New Issue
Block a user