diff --git a/ChangeLog b/ChangeLog index cc007f1f..88e28630 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,7 +6,8 @@ - tiffsave has a "depth" param to set max pyr depth - libtiff LOGLUV images load and save as libvips XYZ - add gifload_source, csvload_source, csvsave_target, matrixload_source, - matrixsave_source, pdfload_source, heifload_source, heifsave_target + matrixsave_source, pdfload_source, heifload_source, heifsave_target, + ppmload_source, ppmsave_target - revise vipsthumbnail flags - add VIPS_LEAK env var - add vips_pipe_read_limit_set(), --vips-pipe-read-limit, diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index e0367c4d..f7fdd496 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -2047,7 +2047,9 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_load_mat_get_type( void ); extern GType vips_foreign_load_ppm_file_get_type( void ); + extern GType vips_foreign_load_ppm_source_get_type( void ); extern GType vips_foreign_save_ppm_file_get_type( void ); + extern GType vips_foreign_save_ppm_target_get_type( void ); extern GType vips_foreign_load_png_file_get_type( void ); extern GType vips_foreign_load_png_buffer_get_type( void ); @@ -2157,7 +2159,9 @@ vips_foreign_operation_init( void ) #ifdef HAVE_PPM vips_foreign_load_ppm_file_get_type(); + vips_foreign_load_ppm_source_get_type(); vips_foreign_save_ppm_file_get_type(); + vips_foreign_save_ppm_target_get_type(); #endif /*HAVE_PPM*/ #ifdef HAVE_RADIANCE diff --git a/libvips/foreign/ppmload.c b/libvips/foreign/ppmload.c index 727536cb..bcc2e79a 100644 --- a/libvips/foreign/ppmload.c +++ b/libvips/foreign/ppmload.c @@ -35,8 +35,10 @@ * - redone with source/target * - sequential load, plus mmap for filename sources * - faster plus lower memory use - * 02/02/2020 + * 02/02/20 * - ban max_vaue < 0 + * 27/6/20 + * - add ppmload_source */ /* @@ -193,6 +195,21 @@ vips_foreign_load_ppm_dispose( GObject *gobject ) dispose( gobject ); } +static int +vips_foreign_load_ppm_build( VipsObject *object ) +{ + VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) object; + + if( ppm->source ) + ppm->sbuf = vips_sbuf_new_from_source( ppm->source ); + + if( VIPS_OBJECT_CLASS( vips_foreign_load_ppm_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + /* Scan the header into our class. */ static int @@ -626,6 +643,7 @@ vips_foreign_load_ppm_class_init( VipsForeignLoadPpmClass *class ) object_class->nickname = "ppmload_base"; object_class->description = _( "load ppm base class" ); + object_class->build = vips_foreign_load_ppm_build; foreign_class->suffs = vips__ppm_suffs; @@ -720,6 +738,62 @@ vips_foreign_load_ppm_file_init( VipsForeignLoadPpmFile *file ) { } +typedef struct _VipsForeignLoadPpmSource { + VipsForeignLoadPpm parent_object; + + VipsSource *source; + +} VipsForeignLoadPpmSource; + +typedef VipsForeignLoadPpmClass VipsForeignLoadPpmSourceClass; + +G_DEFINE_TYPE( VipsForeignLoadPpmSource, vips_foreign_load_ppm_source, + vips_foreign_load_ppm_get_type() ); + +static int +vips_foreign_load_ppm_source_build( VipsObject *object ) +{ + VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) object; + VipsForeignLoadPpmSource *source = (VipsForeignLoadPpmSource *) object; + + if( source->source ) { + ppm->source = source->source; + g_object_ref( ppm->source ); + } + + if( VIPS_OBJECT_CLASS( vips_foreign_load_ppm_source_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_foreign_load_ppm_source_class_init( VipsForeignLoadPpmFileClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "ppmload_source"; + object_class->build = vips_foreign_load_ppm_source_build; + + VIPS_ARG_OBJECT( class, "source", 1, + _( "Source" ), + _( "Source to load from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadPpmSource, source ), + VIPS_TYPE_SOURCE ); + +} + +static void +vips_foreign_load_ppm_source_init( VipsForeignLoadPpmSource *source ) +{ +} + #endif /*HAVE_PPM*/ /** @@ -751,3 +825,35 @@ vips_ppmload( const char *filename, VipsImage **out, ... ) return( result ); } +/** + * vips_ppmload_source: + * @source: source to load + * @out: (out): output image + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * * @skip: skip this many lines at start of file + * * @lines: read this many lines from file + * * @whitespace: set of whitespace characters + * * @separator: set of separator characters + * * @fail: %gboolean, fail on errors + * + * Exactly as vips_ppmload(), but read from a source. + * + * See also: vips_ppmload(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_ppmload_source( VipsSource *source, VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "ppmload_source", ap, source, out ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/foreign/ppmsave.c b/libvips/foreign/ppmsave.c index c728d7d5..cbfaf787 100644 --- a/libvips/foreign/ppmsave.c +++ b/libvips/foreign/ppmsave.c @@ -6,6 +6,8 @@ * - redone with targets * 18/6/20 * - add "bitdepth" param, cf. tiffsave + * 27/6/20 + * - add ppmsave_target */ /* @@ -469,6 +471,61 @@ vips_foreign_save_ppm_file_init( VipsForeignSavePpmFile *file ) { } +typedef struct _VipsForeignSavePpmTarget { + VipsForeignSavePpm parent_object; + + VipsTarget *target; +} VipsForeignSavePpmTarget; + +typedef VipsForeignSavePpmClass VipsForeignSavePpmTargetClass; + +G_DEFINE_TYPE( VipsForeignSavePpmTarget, vips_foreign_save_ppm_target, + vips_foreign_save_ppm_get_type() ); + +static int +vips_foreign_save_ppm_target_build( VipsObject *object ) +{ + VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) object; + VipsForeignSavePpmTarget *target = + (VipsForeignSavePpmTarget *) object; + + if( target->target ) { + ppm->target = target->target; + g_object_ref( ppm->target ); + } + + return( VIPS_OBJECT_CLASS( + vips_foreign_save_ppm_target_parent_class )-> + build( object ) ); +} + +static void +vips_foreign_save_ppm_target_class_init( + VipsForeignSavePpmTargetClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "ppmsave_target"; + object_class->build = vips_foreign_save_ppm_target_build; + + VIPS_ARG_OBJECT( class, "target", 1, + _( "Target" ), + _( "Target to save to" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignSavePpmTarget, target ), + VIPS_TYPE_TARGET ); + +} + +static void +vips_foreign_save_ppm_target_init( VipsForeignSavePpmTarget *target ) +{ +} + #endif /*HAVE_PPM*/ /** @@ -512,3 +569,28 @@ vips_ppmsave( VipsImage *in, const char *filename, ... ) return( result ); } + +/** + * vips_ppmsave_target: (method) + * @in: image to save + * @target: save image to this target + * @...: %NULL-terminated list of optional named arguments + * + * As vips_ppmsave(), but save to a target. + * + * See also: vips_ppmsave(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_ppmsave_target( VipsImage *in, VipsTarget *target, ... ) +{ + va_list ap; + int result; + + va_start( ap, target ); + result = vips_call_split( "ppmsave_target", ap, in, target ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index 1f06f838..944f802b 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -587,8 +587,12 @@ int vips_pngsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) int vips_ppmload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_ppmload_source( VipsSource *source, VipsImage **out, ... ) + __attribute__((sentinel)); int vips_ppmsave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); +int vips_ppmsave_target( VipsImage *in, VipsTarget *target, ... ) + __attribute__((sentinel)); int vips_matload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); diff --git a/test/test-suite/test_connection.py b/test/test-suite/test_connection.py index 7a21485d..f088ad78 100644 --- a/test/test-suite/test_connection.py +++ b/test/test-suite/test_connection.py @@ -94,5 +94,35 @@ class TestConnection: assert x.get("blob") == y + @skip_if_no("matrixload_source") + @skip_if_no("matrixsave_target") + def test_connection_matrix(self): + x = pyvips.Target.new_to_memory() + self.mono.matrixsave_target(x) + y = pyvips.Source.new_from_memory(x.get("blob")) + im = pyvips.Image.matrixload_source(y) + + assert (im - self.mono).abs().max() == 0 + + @skip_if_no("csvload_source") + @skip_if_no("csvsave_target") + def test_connection_csv(self): + x = pyvips.Target.new_to_memory() + self.mono.csvsave_target(x) + y = pyvips.Source.new_from_memory(x.get("blob")) + im = pyvips.Image.csvload_source(y) + + assert (im - self.mono).abs().max() == 0 + + @skip_if_no("ppmload_source") + @skip_if_no("ppmsave_target") + def test_connection_ppm(self): + x = pyvips.Target.new_to_memory() + self.mono.ppmsave_target(x) + y = pyvips.Source.new_from_memory(x.get("blob")) + im = pyvips.Image.ppmload_source(y) + + assert (im - self.mono).abs().max() == 0 + if __name__ == '__main__': pytest.main() diff --git a/test/test-suite/test_foreign.py b/test/test-suite/test_foreign.py index ce9fbfb7..182f8af8 100644 --- a/test/test-suite/test_foreign.py +++ b/test/test-suite/test_foreign.py @@ -327,7 +327,7 @@ class TestForeign: assert( len_mono1 < len_mono2 ) # we can't test palette save since we can't be sure libimagequant is - # available + # available and there's no easy test for its presence @skip_if_no("tiffload") def test_tiff(self):