diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..2e614077 --- /dev/null +++ b/.github/workflows/ci.yml @@ -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 + echo "DLCLOSE_PRELOAD=${{ github.workspace }}/dlclose.so" >> $GITHUB_ENV + echo -e '#include \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 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index b10b237c..00000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,84 +0,0 @@ -name: Test - -# to-do: -# - add a macos test with brew etc. -# - build with clang/asan/etc. and run the fuzz tests - -on: - - push - - pull_request - - workflow_dispatch # manually triggered workflow - -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: Add libheif PPA - run: | - sudo add-apt-repository ppa:strukturag/libde265 - sudo add-apt-repository ppa:strukturag/libheif - - - 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 -j$(nproc) - - - 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 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bebdeff5..00000000 --- a/.travis.yml +++ /dev/null @@ -1,163 +0,0 @@ -language: cpp - -env: - global: - - PYTHON=python3 - - PYVIPS_VERSION=master - -addons: - apt: - update: true - sources: &common_sources - # add support for AVIF 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: - update: true - packages: - - ccache - - cfitsio - - fftw - - giflib - - glib - - gobject-introspection - - gtk-doc - - libexif - - libgsf - - libheif - - libjpeg-turbo - - libmatio - - librsvg - - libspng - - libtiff - - little-cms2 - - openexr - - openslide - - orc - - pango - - poppler - - webp - -jobs: - allow_failures: - - os: osx - fast_finish: true - include: - - os: linux - dist: bionic - compiler: gcc - name: "Linux x64 (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: "Linux x64 (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 - - echo -e '#include \nint dlclose(void*handle){return 0;}' | $CC -shared -xc -odlclose.so - - cache: ccache - - os: osx - osx_image: xcode11 - name: "macOS (10.14.6) - Xcode 11" - env: - - JPEG=/usr/local/opt/jpeg-turbo - - JOBS="`sysctl -n hw.ncpu`" - - WITH_MAGICK=no - - 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="clang" - - CXX="clang++" - 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 diff --git a/ChangeLog b/ChangeLog index 81d66c2c..5e6ed5d1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,6 +16,8 @@ - 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()m vips_niftiload_source() 22/12/20 start 8.10.6 - don't seek on bad file descriptors [kleisauke] @@ -23,6 +25,8 @@ - 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] 18/12/20 started 8.10.5 - fix potential /0 in animated webp load [lovell] diff --git a/README.md b/README.md index 80d2bbaa..8d362ff7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # libvips : an image processing library -[![Test](https://github.com/libvips/libvips/workflows/Test/badge.svg)](https://github.com/libvips/libvips/actions?query=workflow%3ATest) +[![CI](https://github.com/libvips/libvips/workflows/CI/badge.svg)](https://github.com/libvips/libvips/actions) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/libvips.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=2&q=proj:libvips) [![Coverity Status](https://scan.coverity.com/projects/6503/badge.svg)](https://scan.coverity.com/projects/jcupitt-libvips) [![Gitter](https://badges.gitter.im/libvips/devchat.svg)](https://gitter.im/libvips/devchat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) diff --git a/libvips/foreign/csvload.c b/libvips/foreign/csvload.c index db223abe..c2a13ad8 100644 --- a/libvips/foreign/csvload.c +++ b/libvips/foreign/csvload.c @@ -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" ), diff --git a/libvips/foreign/fits.c b/libvips/foreign/fits.c index 73c9a584..0c3536df 100644 --- a/libvips/foreign/fits.c +++ b/libvips/foreign/fits.c @@ -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 ); } diff --git a/libvips/foreign/fitsload.c b/libvips/foreign/fitsload.c index 25765827..83fe0765 100644 --- a/libvips/foreign/fitsload.c +++ b/libvips/foreign/fitsload.c @@ -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 ); +} diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 326b4507..12ea483a 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -2117,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 ); @@ -2182,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 ); @@ -2324,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*/ @@ -2333,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*/ diff --git a/libvips/foreign/niftiload.c b/libvips/foreign/niftiload.c index ef49a8fa..4427a99f 100644 --- a/libvips/foreign/niftiload.c +++ b/libvips/foreign/niftiload.c @@ -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 ); +} diff --git a/libvips/foreign/niftisave.c b/libvips/foreign/niftisave.c index 4de24b87..485b89f1 100644 --- a/libvips/foreign/niftisave.c +++ b/libvips/foreign/niftisave.c @@ -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; diff --git a/libvips/foreign/pforeign.h b/libvips/foreign/pforeign.h index 0683850e..18e472bf 100644 --- a/libvips/foreign/pforeign.h +++ b/libvips/foreign/pforeign.h @@ -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 ); diff --git a/libvips/foreign/svgload.c b/libvips/foreign/svgload.c index 4a8c2b1c..45c2e3f8 100644 --- a/libvips/foreign/svgload.c +++ b/libvips/foreign/svgload.c @@ -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 -#endif - /* A handy #define for we-will-handle-svgz. */ #if LIBRSVG_CHECK_FEATURE(SVGZ) && defined(HAVE_ZLIB) diff --git a/libvips/include/vips/dispatch.h b/libvips/include/vips/dispatch.h index 457c2cd4..c859e78b 100644 --- a/libvips/include/vips/dispatch.h +++ b/libvips/include/vips/dispatch.h @@ -33,11 +33,12 @@ #ifndef IM_DISPATCH_H #define IM_DISPATCH_H +#include + #ifdef __cplusplus extern "C" { #endif /*__cplusplus*/ -#include #include #include diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index e2f329fe..42a9bad5 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -671,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)); diff --git a/libvips/include/vips/internal.h b/libvips/include/vips/internal.h index 05b1c7df..d3436472 100644 --- a/libvips/include/vips/internal.h +++ b/libvips/include/vips/internal.h @@ -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, diff --git a/libvips/include/vips/vips.h b/libvips/include/vips/vips.h index 20d77a43..4c161c43 100644 --- a/libvips/include/vips/vips.h +++ b/libvips/include/vips/vips.h @@ -80,10 +80,6 @@ #ifndef VIPS_VIPS_H #define VIPS_VIPS_H -#ifdef __cplusplus -extern "C" { -#endif /*__cplusplus*/ - #include #include #include @@ -93,6 +89,10 @@ extern "C" { */ #include +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + /* If we're being parsed by SWIG, remove gcc attributes. */ #ifdef SWIG diff --git a/libvips/iofuncs/error.c b/libvips/iofuncs/error.c index 746742cb..cbb44162 100644 --- a/libvips/iofuncs/error.c +++ b/libvips/iofuncs/error.c @@ -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 ); diff --git a/libvips/iofuncs/init.c b/libvips/iofuncs/init.c index 2784a403..10f2f565 100644 --- a/libvips/iofuncs/init.c +++ b/libvips/iofuncs/init.c @@ -643,13 +643,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 +671,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 +768,9 @@ vips_shutdown( void ) { static gboolean done = FALSE; - if( !done ) - vips_leak(); + if( !done && + vips_leak() ) + exit( 1 ); done = TRUE; } diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index 1ee34307..7152537b 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -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 * diff --git a/libvips/iofuncs/sinkscreen.c b/libvips/iofuncs/sinkscreen.c index c432cdcf..c6e5ead1 100644 --- a/libvips/iofuncs/sinkscreen.c +++ b/libvips/iofuncs/sinkscreen.c @@ -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 ); } diff --git a/libvips/iofuncs/type.c b/libvips/iofuncs/type.c index f706655c..ee77bf5b 100644 --- a/libvips/iofuncs/type.c +++ b/libvips/iofuncs/type.c @@ -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 ); } /** diff --git a/suppressions/lsan.supp b/suppressions/lsan.supp index ec44e871..47d8282e 100644 --- a/suppressions/lsan.supp +++ b/suppressions/lsan.supp @@ -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