From 02327b621415059078272ef621b7832e5fac4dae Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 1 May 2020 16:05:48 +0100 Subject: [PATCH] add an experimental libspng reader it compiles, but I've not tried running it yet heh --- ChangeLog | 1 + README.md | 24 ++ configure.ac | 22 +- libvips/foreign/Makefile.am | 1 + libvips/foreign/spngload.c | 674 ++++++++++++++++++++++++++++++++++++ 5 files changed, 720 insertions(+), 2 deletions(-) create mode 100644 libvips/foreign/spngload.c diff --git a/ChangeLog b/ChangeLog index 350c63f3..4e70da6f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18,6 +18,7 @@ - add all to smartcrop - flood fill could stop half-way for some very complex shapes - better handling of unaligned reads in multipage tiffs [petoor] +- add experimental libspng reader 24/4/20 started 8.9.3 - better iiif tile naming [IllyaMoskvin] diff --git a/README.md b/README.md index 03bab492..1b4c4e6d 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,30 @@ [![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) +# This branch + +Is for experiemtning with [libspng](https://github.com/randy408/libspng). + +## Notes + +Build libspng: + +``` +cd libspng +meson build --prefix=/home/john/vips --libdir=/home/john/vips/lib +cd build +ninja +ninja install +``` + +Installs `spng.pc`. + +Sample code: + +https://github.com/randy408/libspng/blob/master/examples/example.c + +# Introduction + libvips is a [demand-driven, horizontally threaded](https://github.com/libvips/libvips/wiki/Why-is-libvips-quick) image processing library. Compared to similar diff --git a/configure.ac b/configure.ac index 2c712caf..e44866e9 100644 --- a/configure.ac +++ b/configure.ac @@ -1175,6 +1175,22 @@ FIND_GIFLIB( ] ) +# Look for libspng first +AC_ARG_WITH([spng], + AS_HELP_STRING([--without-spng], [build without libspng (default: test)])) + +if test x"$with_spng" != x"no"; then + PKG_CHECK_MODULES(SPNG, spng >= 0.6, + [AC_DEFINE(HAVE_SPNG,1,[define if you have libspng installed.]) + with_spng=yes + PACKAGES_USED="$PACKAGES_USED spng" + with_png=no + ], + [ + ] + ) +fi + # look for PNG with pkg-config ... fall back to our tester AC_ARG_WITH([png], AS_HELP_STRING([--without-png], [build without libpng (default: test)])) @@ -1298,10 +1314,10 @@ if test x"$LIB_FUZZING_ENGINE" = x; then fi # Gather all up for VIPS_CFLAGS, VIPS_INCLUDES, VIPS_LIBS -VIPS_CFLAGS="$VIPS_CFLAGS $GTHREAD_CFLAGS $GIO_CFLAGS $REQUIRED_CFLAGS $EXPAT_CFLAGS $ZLIB_CFLAGS $PANGOFT2_CFLAGS $GSF_CFLAGS $FFTW_CFLAGS $MAGICK_CFLAGS $JPEG_CFLAGS $PNG_CFLAGS $IMAGEQUANT_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $LIBWEBP_CFLAGS $LIBWEBPMUX_CFLAGS $GIFLIB_INCLUDES $RSVG_CFLAGS $PDFIUM_INCLUDES $POPPLER_CFLAGS $OPENEXR_CFLAGS $OPENSLIDE_CFLAGS $ORC_CFLAGS $TIFF_CFLAGS $LCMS_CFLAGS $HEIF_CFLAGS" +VIPS_CFLAGS="$VIPS_CFLAGS $GTHREAD_CFLAGS $GIO_CFLAGS $REQUIRED_CFLAGS $EXPAT_CFLAGS $ZLIB_CFLAGS $PANGOFT2_CFLAGS $GSF_CFLAGS $FFTW_CFLAGS $MAGICK_CFLAGS $JPEG_CFLAGS $SPNG_CFLAGS $PNG_CFLAGS $IMAGEQUANT_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $LIBWEBP_CFLAGS $LIBWEBPMUX_CFLAGS $GIFLIB_INCLUDES $RSVG_CFLAGS $PDFIUM_INCLUDES $POPPLER_CFLAGS $OPENEXR_CFLAGS $OPENSLIDE_CFLAGS $ORC_CFLAGS $TIFF_CFLAGS $LCMS_CFLAGS $HEIF_CFLAGS" VIPS_CFLAGS="$VIPS_DEBUG_FLAGS $VIPS_CFLAGS" VIPS_INCLUDES="$ZLIB_INCLUDES $PNG_INCLUDES $TIFF_INCLUDES $JPEG_INCLUDES $NIFTI_INCLUDES" -VIPS_LIBS="$ZLIB_LIBS $HEIF_LIBS $MAGICK_LIBS $PNG_LIBS $IMAGEQUANT_LIBS $TIFF_LIBS $JPEG_LIBS $GTHREAD_LIBS $GIO_LIBS $REQUIRED_LIBS $EXPAT_LIBS $PANGOFT2_LIBS $GSF_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $GIFLIB_LIBS $RSVG_LIBS $NIFTI_LIBS $PDFIUM_LIBS $POPPLER_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $LIBWEBP_LIBS $LIBWEBPMUX_LIBS $MATIO_LIBS $EXIF_LIBS -lm" +VIPS_LIBS="$ZLIB_LIBS $HEIF_LIBS $MAGICK_LIBS $SPNG_LIBS $PNG_LIBS $IMAGEQUANT_LIBS $TIFF_LIBS $JPEG_LIBS $GTHREAD_LIBS $GIO_LIBS $REQUIRED_LIBS $EXPAT_LIBS $PANGOFT2_LIBS $GSF_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $GIFLIB_LIBS $RSVG_LIBS $NIFTI_LIBS $PDFIUM_LIBS $POPPLER_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $LIBWEBP_LIBS $LIBWEBPMUX_LIBS $MATIO_LIBS $EXIF_LIBS -lm" AC_SUBST(VIPS_LIBDIR) @@ -1397,6 +1413,8 @@ file import with cfitsio: $with_cfitsio file import/export with libwebp: $with_libwebp (requires libwebp, libwebpmux, libwebpdemux 0.6.0 or later) text rendering with pangoft2: $with_pangoft2 +file import/export with libspng: $with_spng + (requires libspng-0.6 or later) file import/export with libpng: $with_png (requires libpng-1.2.9 or later) support 8bpp PNG quantisation: $with_imagequant diff --git a/libvips/foreign/Makefile.am b/libvips/foreign/Makefile.am index d9a68585..759df2e5 100644 --- a/libvips/foreign/Makefile.am +++ b/libvips/foreign/Makefile.am @@ -39,6 +39,7 @@ libforeign_la_SOURCES = \ magickload.c \ magick7load.c \ magicksave.c \ + spngload.c \ pngload.c \ pngsave.c \ vipspng.c \ diff --git a/libvips/foreign/spngload.c b/libvips/foreign/spngload.c new file mode 100644 index 00000000..e89566d2 --- /dev/null +++ b/libvips/foreign/spngload.c @@ -0,0 +1,674 @@ +/* load PNG with libspng + * + * 5/12/11 + * - from pngload.c + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Notes: + * + * an enum for interlace_method would be nice ... ADAM7 == 1, + * no interlace == 0. + * an equivalent of png_sig_cmp() from libpng (is_a_png on a memory area) + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include + +#include "pforeign.h" + +#ifdef HAVE_SPNG + +#include + +typedef struct _VipsForeignLoadPng { + VipsForeignLoad parent_object; + + /* Set by subclasses. + */ + VipsSource *source; + + spng_ctx *ctx; + struct spng_ihdr ihdr; + enum spng_format fmt; + int bands; + VipsInterpretation interpretation; + VipsBandFormat format; + size_t out_size; + size_t out_line_size; + int y_pos; + +} VipsForeignLoadPng; + +typedef VipsForeignLoadClass VipsForeignLoadPngClass; + +G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadPng, vips_foreign_load_png, + VIPS_TYPE_FOREIGN_LOAD ); + +static void +vips_foreign_load_png_dispose( GObject *gobject ) +{ + VipsForeignLoadPng *png = (VipsForeignLoadPng *) gobject; + + VIPS_FREEF( spng_ctx_free, png->ctx ); + VIPS_UNREF( png->source ); + + G_OBJECT_CLASS( vips_foreign_load_png_parent_class )-> + dispose( gobject ); +} + +/* libspng read callbacks should copy length bytes to dest and return 0 + * or SPNG_IO_EOF/SPNG_IO_ERROR on error. + */ +static int +vips_foreign_load_png_stream( spng_ctx *ctx, void *user, + void *dest, size_t length ) +{ + VipsSource *source = VIPS_SOURCE( user ); + + gint64 bytes_read; + + bytes_read = vips_source_read( source, dest, length ); + if( bytes_read < 0 ) + return( SPNG_IO_ERROR ); + if( bytes_read < length ) + return( SPNG_IO_EOF); + + return( 0 ); +} + +static VipsForeignFlags +vips_foreign_load_png_get_flags_source( VipsSource *source ) +{ + spng_ctx *ctx; + struct spng_ihdr ihdr; + VipsForeignFlags flags; + + ctx = spng_ctx_new( 0 ); + spng_set_crc_action( ctx, SPNG_CRC_USE, SPNG_CRC_USE ); + spng_set_png_stream( ctx, + vips_foreign_load_png_stream, source ); + if( spng_get_ihdr( ctx, &ihdr ) ) { + spng_ctx_free( ctx ); + return( 0 ); + } + spng_ctx_free( ctx ); + + flags = 0; + if( ihdr.interlace_method != 0 ) + flags |= VIPS_FOREIGN_PARTIAL; + else + flags |= VIPS_FOREIGN_SEQUENTIAL; + + return( flags ); +} + +static VipsForeignFlags +vips_foreign_load_png_get_flags( VipsForeignLoad *load ) +{ + VipsForeignLoadPng *png = (VipsForeignLoadPng *) load; + + return( vips_foreign_load_png_get_flags_source( png->source ) ); +} + +static VipsForeignFlags +vips_foreign_load_png_get_flags_filename( const char *filename ) +{ + VipsSource *source; + VipsForeignFlags flags; + + if( !(source = vips_source_new_from_file( filename )) ) + return( 0 ); + flags = vips_foreign_load_png_get_flags_source( source ); + VIPS_UNREF( source ); + + return( flags ); +} + +static void +vips_foreign_load_png_set_header( VipsForeignLoadPng *png, VipsImage *image ) +{ + vips_image_init_fields( image, + png->ihdr.width, png->ihdr.height, png->bands, + png->format, VIPS_CODING_NONE, png->interpretation, + 1.0, 1.0 ); + VIPS_SETSTR( image->filename, + vips_connection_filename( VIPS_CONNECTION( png->source ) ) ); + + /* 0 is no interlace. + */ + if( png->ihdr.interlace_method == 0 ) + /* Sequential mode needs thinstrip to work with things like + * vips_shrink(). + */ + vips_image_pipelinev( image, + VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + else + /* Interlaced images are read via a huge memory buffer. + */ + vips_image_pipelinev( image, VIPS_DEMAND_STYLE_ANY, NULL ); +} + +static int +vips_foreign_load_png_header( VipsForeignLoad *load ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load ); + VipsForeignLoadPng *png = (VipsForeignLoadPng *) load; + + int error; + + /* Flags can be eg. SPNG_CTX_IGNORE_ADLER32 to ignore CRC chaekcs in + * deflate. + * + * FIXME ... fail handling + */ + png->ctx = spng_ctx_new( 0 ); + spng_set_crc_action( png->ctx, SPNG_CRC_USE, SPNG_CRC_USE ); + + spng_set_png_stream( png->ctx, + vips_foreign_load_png_stream, png->source ); + if( (error = spng_get_ihdr( png->ctx, &png->ihdr )) ) { + vips_error( class->nickname, "%s", spng_strerror( error ) ); + return( -1 ); + } + + printf(" width: %d\nheight: %d\nbit depth: %d\ncolor type: %d\n", + png->ihdr.width, png->ihdr.height, + png->ihdr.bit_depth, png->ihdr.color_type ); + printf( "compression method: %d\nfilter method: %d\n" + "interlace method: %d\n", + png->ihdr.compression_method, png->ihdr.filter_method, + png->ihdr.interlace_method ); + + switch( png->ihdr.color_type ) { + case SPNG_COLOR_TYPE_GRAYSCALE: + png->interpretation = VIPS_INTERPRETATION_B_W; + png->bands = 1; + break; + + case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: + png->interpretation = VIPS_INTERPRETATION_B_W; + png->bands = 2; + break; + + case SPNG_COLOR_TYPE_TRUECOLOR: + case SPNG_COLOR_TYPE_INDEXED: + png->interpretation = VIPS_INTERPRETATION_sRGB; + png->bands = 3; + break; + + case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: + png->interpretation = VIPS_INTERPRETATION_sRGB; + png->bands = 4; + break; + + default: + vips_error( class->nickname, "%s", "unknown color_type" ); + return( -1 ); + } + + if( png->ihdr.bit_depth == 16 ) { + png->fmt = SPNG_FMT_RGBA16; + png->format = VIPS_FORMAT_USHORT; + if( png->interpretation == VIPS_INTERPRETATION_B_W ) + png->interpretation = VIPS_INTERPRETATION_GREY16; + if( png->interpretation == VIPS_INTERPRETATION_sRGB ) + png->interpretation = VIPS_INTERPRETATION_RGB16; + } + else { + png->fmt = SPNG_FMT_RGBA8; + png->format = VIPS_FORMAT_UCHAR; + } + + /* FIXME ... get resolution. + */ + + vips_source_minimise( png->source ); + + vips_foreign_load_png_set_header( png, load->out ); + + return( 0 ); +} + +static int +vips_foreign_load_png_interlace( VipsForeignLoadPng *png, VipsImage *image ) +{ + /* FIXME ... load interlaced PNG to huge memory image. + */ + + return( 0 ); +} + +static int +vips_foreign_load_png_generate( VipsRegion *or, + void *seq, void *a, void *b, gboolean *stop ) +{ + VipsRect *r = &or->valid; + VipsForeignLoad *load = VIPS_FOREIGN_LOAD( a ); + VipsForeignLoadPng *png = (VipsForeignLoadPng *) load; + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( png ); + + int y; + int error; + +#ifdef DEBUG + printf( "vips_foreign_load_png_generate: line %d, %d rows\n", + r->top, r->height ); + printf( "vips_foreign_load_png_generate: y_top = %d\n", png->y_pos ); +#endif /*DEBUG*/ + + /* We're inside a tilecache where tiles are the full image width, so + * this should always be true. + */ + g_assert( r->left == 0 ); + g_assert( r->width == or->im->Xsize ); + g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize ); + + /* Tiles should always be a strip in height, unless it's the final + * strip. + */ + g_assert( r->height == VIPS_MIN( VIPS__FATSTRIP_HEIGHT, + or->im->Ysize - r->top ) ); + + /* And check that y_pos is correct. It should be, since we are inside + * a vips_sequential(). + */ + if( r->top != png->y_pos ) { + vips_error( class->nickname, + _( "out of order read at line %d" ), png->y_pos ); + return( -1 ); + } + + for( y = 0; y < r->height; y++ ) { + error = spng_decode_row( png->ctx, + VIPS_REGION_ADDR( or, 0, r->top + y ), + VIPS_REGION_SIZEOF_LINE( or ) ); + + /* FIXME .. should allow SPNG_EOI here? + */ + if( error ) { + /* We've failed to read some pixels. Knock this + * operation out of cache. + */ + vips_operation_invalidate( VIPS_OPERATION( png ) ); + +#ifdef DEBUG + printf( "vips_foreign_load_png_generate:\n" ); + printf( " spng_decode_row() failed, line %d\n", + r->top + y ); + printf( " file %s\n", read->name ); + printf( " thread %p\n", g_thread_self() ); +#endif /*DEBUG*/ + + /* And bail if fail is on. We have to add an error + * message, since the handler we install just does + * g_warning(). + */ + if( load->fail ) { + vips_error( class->nickname, + "%s", _( "libpng read error" ) ); + return( -1 ); + } + } + + png->y_pos += 1; + } + + return( 0 ); +} + +static int +vips_foreign_load_png_load( VipsForeignLoad *load ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load ); + VipsForeignLoadPng *png = (VipsForeignLoadPng *) load; + VipsImage **t = (VipsImage **) + vips_object_local_array( VIPS_OBJECT( load ), 3 ); + + int error; + + if( (error = spng_decoded_image_size( png->ctx, + png->fmt, &png->out_size )) ) { + vips_error( class->nickname, "%s", spng_strerror( error ) ); + return( -1 ); + } + png->out_line_size = png->out_size / png->ihdr.height; + + /* Initialize for progressive decoding. + */ + if( (error = spng_decode_image( png->ctx, NULL, 0, + png->fmt, SPNG_DECODE_PROGRESSIVE )) ) { + vips_error( class->nickname, "%s", spng_strerror( error ) ); + return( -1 ); + } + + t[0] = vips_image_new_memory(); + vips_foreign_load_png_set_header( png, t[0] ); + if( vips_source_decode( png->source ) ) + return( -1 ); + + if( png->ihdr.interlace_method != 0 ) { + /* Arg awful interlaced image. We have to load to a huge mem + * buffer, then copy to out. + */ + if( vips_foreign_load_png_interlace( png, t[0] ) || + vips_image_write( t[0], load->real ) ) + return( -1 ); + } + else { + if( vips_image_generate( t[0], + NULL, vips_foreign_load_png_generate, NULL, + read, NULL ) || + vips_sequential( t[0], &t[1], + "tile_height", VIPS__FATSTRIP_HEIGHT, + NULL ) || + vips_image_write( t[1], load->real ) ) + return( -1 ); + } + + return( 0 ); +} + +static void +vips_foreign_load_png_class_init( VipsForeignLoadPngClass *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->dispose = vips_foreign_load_png_dispose; + + object_class->nickname = "pngload_base"; + object_class->description = _( "load png base class" ); + + /* We are fast at is_a(), so high priority. + */ + foreign_class->priority = 200; + + load_class->get_flags_filename = + vips_foreign_load_png_get_flags_filename; + load_class->get_flags = vips_foreign_load_png_get_flags; + load_class->header = vips_foreign_load_png_header; + load_class->load = vips_foreign_load_png_load; + +} + +static void +vips_foreign_load_png_init( VipsForeignLoadPng *png ) +{ +} + +typedef struct _VipsForeignLoadPngSource { + VipsForeignLoadPng parent_object; + + /* Load from a source. + */ + VipsSource *source; + +} VipsForeignLoadPngSource; + +typedef VipsForeignLoadPngClass VipsForeignLoadPngSourceClass; + +G_DEFINE_TYPE( VipsForeignLoadPngSource, vips_foreign_load_png_source, + vips_foreign_load_png_get_type() ); + +static int +vips_foreign_load_png_source_build( VipsObject *object ) +{ + VipsForeignLoadPng *png = (VipsForeignLoadPng *) object; + VipsForeignLoadPngSource *source = (VipsForeignLoadPngSource *) object; + + if( source->source ) { + png->source = source->source; + g_object_ref( png->source ); + } + + if( VIPS_OBJECT_CLASS( vips_foreign_load_png_source_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + +static gboolean +vips_foreign_load_png_source_is_a_source( VipsSource *source ) +{ + static unsigned char signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; + + const unsigned char *p; + + if( (p = vips_source_sniff( source, 8 )) && + memcmp( p, signature, 8 ) == 0 ) + return( TRUE ); + + return( FALSE ); +} + +static void +vips_foreign_load_png_source_class_init( VipsForeignLoadPngSourceClass *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 = "pngload_source"; + object_class->description = _( "load png from source" ); + object_class->build = vips_foreign_load_png_source_build; + + load_class->is_a_source = vips_foreign_load_png_source_is_a_source; + + VIPS_ARG_OBJECT( class, "source", 1, + _( "Source" ), + _( "Source to load from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadPngSource, source ), + VIPS_TYPE_SOURCE ); + +} + +static void +vips_foreign_load_png_source_init( VipsForeignLoadPngSource *source ) +{ +} + +typedef struct _VipsForeignLoadPngFile { + VipsForeignLoadPng parent_object; + + /* Filename for load. + */ + char *filename; + +} VipsForeignLoadPngFile; + +typedef VipsForeignLoadPngClass VipsForeignLoadPngFileClass; + +G_DEFINE_TYPE( VipsForeignLoadPngFile, vips_foreign_load_png_file, + vips_foreign_load_png_get_type() ); + +static int +vips_foreign_load_png_file_build( VipsObject *object ) +{ + VipsForeignLoadPng *png = (VipsForeignLoadPng *) object; + VipsForeignLoadPngFile *file = (VipsForeignLoadPngFile *) object; + + if( file->filename && + !(png->source = vips_source_new_from_file( file->filename )) ) + return( -1 ); + + if( VIPS_OBJECT_CLASS( vips_foreign_load_png_file_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + +static gboolean +vips_foreign_load_png_file_is_a( const char *filename ) +{ + VipsSource *source; + gboolean result; + + if( !(source = vips_source_new_from_file( filename )) ) + return( FALSE ); + result = vips_foreign_load_png_source_is_a_source( source ); + VIPS_UNREF( source ); + + return( result ); +} + +const char *vips_foreign_load_png_file_suffs[] = { ".png", NULL }; + +static void +vips_foreign_load_png_file_class_init( VipsForeignLoadPngFileClass *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 = "pngload"; + object_class->description = _( "load png from file" ); + object_class->build = vips_foreign_load_png_file_build; + + foreign_class->suffs = vips_foreign_load_png_file_suffs; + + load_class->is_a = vips_foreign_load_png_file_is_a; + + VIPS_ARG_STRING( class, "filename", 1, + _( "Filename" ), + _( "Filename to load from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadPngFile, filename ), + NULL ); +} + +static void +vips_foreign_load_png_file_init( VipsForeignLoadPngFile *file ) +{ +} + +typedef struct _VipsForeignLoadPngBuffer { + VipsForeignLoadPng parent_object; + + /* Load from a buffer. + */ + VipsBlob *blob; + +} VipsForeignLoadPngBuffer; + +typedef VipsForeignLoadPngClass VipsForeignLoadPngBufferClass; + +G_DEFINE_TYPE( VipsForeignLoadPngBuffer, vips_foreign_load_png_buffer, + vips_foreign_load_png_get_type() ); + +static int +vips_foreign_load_png_buffer_build( VipsObject *object ) +{ + VipsForeignLoadPng *png = (VipsForeignLoadPng *) object; + VipsForeignLoadPngBuffer *buffer = (VipsForeignLoadPngBuffer *) object; + + if( buffer->blob && + !(png->source = vips_source_new_from_memory( + VIPS_AREA( buffer->blob )->data, + VIPS_AREA( buffer->blob )->length )) ) + return( -1 ); + + if( VIPS_OBJECT_CLASS( vips_foreign_load_png_buffer_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + +static gboolean +vips_foreign_load_png_buffer_is_a_buffer( const void *buf, size_t len ) +{ + VipsSource *source; + gboolean result; + + if( !(source = vips_source_new_from_memory( buf, len )) ) + return( FALSE ); + result = vips_foreign_load_png_source_is_a_source( source ); + VIPS_UNREF( source ); + + return( result ); +} + +static void +vips_foreign_load_png_buffer_class_init( VipsForeignLoadPngBufferClass *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 = "pngload_buffer"; + object_class->description = _( "load png from buffer" ); + object_class->build = vips_foreign_load_png_buffer_build; + + load_class->is_a_buffer = vips_foreign_load_png_buffer_is_a_buffer; + + VIPS_ARG_BOXED( class, "buffer", 1, + _( "Buffer" ), + _( "Buffer to load from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadPngBuffer, blob ), + VIPS_TYPE_BLOB ); + +} + +static void +vips_foreign_load_png_buffer_init( VipsForeignLoadPngBuffer *buffer ) +{ +} + +#endif /*HAVE_SPNG*/