diff --git a/ChangeLog b/ChangeLog index 588672ed..75cf5fbe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,8 @@ - added vips_mapim() ... resample with an index image, plus test - try to improve vips_resize() quality a little more - vips_resize() can do non-square resizes +- dzsave removes tile metadata by default, thanks Benjamin +- added vips_image_new_from_memory_copy() 7/5/15 started 8.1.1 - oop, vips-8.0 wrapper script should be vips-8.1, thanks Danilo diff --git a/doc/gtk-doc.make b/doc/gtk-doc.make index e7916563..9ccd0b04 100644 --- a/doc/gtk-doc.make +++ b/doc/gtk-doc.make @@ -25,6 +25,7 @@ TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE) SETUP_FILES = \ $(content_files) \ + $(expand_content_files) \ $(DOC_MAIN_SGML_FILE) \ $(DOC_MODULE)-sections.txt \ $(DOC_MODULE)-overrides.txt @@ -86,7 +87,7 @@ GTK_DOC_V_SETUP_0=@echo " DOC Preparing build"; setup-build.stamp: -$(GTK_DOC_V_SETUP)if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ - files=`echo $(SETUP_FILES) $(expand_content_files) $(DOC_MODULE).types`; \ + files=`echo $(SETUP_FILES) $(DOC_MODULE).types`; \ if test "x$$files" != "x" ; then \ for file in $$files ; do \ destdir=`dirname $(abs_builddir)/$$file`; \ @@ -118,7 +119,7 @@ scan-build.stamp: setup-build.stamp $(HFILE_GLOB) $(CFILE_GLOB) $(GTK_DOC_V_INTROSPECT)if grep -l '^..*$$' $(DOC_MODULE).types > /dev/null 2>&1 ; then \ scanobj_options=""; \ gtkdoc-scangobj 2>&1 --help | grep >/dev/null "\-\-verbose"; \ - if test "$(?)" = "0"; then \ + if test "$$?" = "0"; then \ if test "x$(V)" = "x1"; then \ scanobj_options="--verbose"; \ fi; \ @@ -162,17 +163,17 @@ GTK_DOC_V_XREF=$(GTK_DOC_V_XREF_$(V)) GTK_DOC_V_XREF_=$(GTK_DOC_V_XREF_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_XREF_0=@echo " DOC Fixing cross-references"; -html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) +html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(expand_content_files) $(GTK_DOC_V_HTML)rm -rf html && mkdir html && \ mkhtml_options=""; \ gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-verbose"; \ - if test "$(?)" = "0"; then \ + if test "$$?" = "0"; then \ if test "x$(V)" = "x1"; then \ mkhtml_options="$$mkhtml_options --verbose"; \ fi; \ fi; \ gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-path"; \ - if test "$(?)" = "0"; then \ + if test "$$?" = "0"; then \ mkhtml_options="$$mkhtml_options --path=\"$(abs_srcdir)\""; \ fi; \ cd html && gtkdoc-mkhtml $$mkhtml_options $(MKHTML_OPTIONS) $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE) @@ -194,11 +195,11 @@ GTK_DOC_V_PDF=$(GTK_DOC_V_PDF_$(V)) GTK_DOC_V_PDF_=$(GTK_DOC_V_PDF_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_PDF_0=@echo " DOC Building PDF"; -pdf-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) +pdf-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(expand_content_files) $(GTK_DOC_V_PDF)rm -f $(DOC_MODULE).pdf && \ mkpdf_options=""; \ gtkdoc-mkpdf 2>&1 --help | grep >/dev/null "\-\-verbose"; \ - if test "$(?)" = "0"; then \ + if test "$$?" = "0"; then \ if test "x$(V)" = "x1"; then \ mkpdf_options="$$mkpdf_options --verbose"; \ fi; \ @@ -223,12 +224,15 @@ clean-local: @if echo $(SCAN_OPTIONS) | grep -q "\-\-rebuild-types" ; then \ rm -f $(DOC_MODULE).types; \ fi + @if echo $(SCAN_OPTIONS) | grep -q "\-\-rebuild-sections" ; then \ + rm -f $(DOC_MODULE)-sections.txt; \ + fi distclean-local: @rm -rf xml html $(REPORT_FILES) $(DOC_MODULE).pdf \ $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt @if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ - rm -f $(SETUP_FILES) $(expand_content_files) $(DOC_MODULE).types; \ + rm -f $(SETUP_FILES) $(DOC_MODULE).types; \ fi maintainer-clean-local: diff --git a/libvips/arithmetic/max.c b/libvips/arithmetic/max.c index c40a2f1b..c189384ae 100644 --- a/libvips/arithmetic/max.c +++ b/libvips/arithmetic/max.c @@ -434,14 +434,14 @@ vips_max_class_init( VipsMaxClass *class ) _( "Horizontal position of maximum" ), VIPS_ARGUMENT_OPTIONAL_OUTPUT, G_STRUCT_OFFSET( VipsMax, x ), - 0, 1000000, 0 ); + 0, VIPS_MAX_COORD, 0 ); VIPS_ARG_INT( class, "y", 3, _( "y" ), _( "Vertical position of maximum" ), VIPS_ARGUMENT_OPTIONAL_OUTPUT, G_STRUCT_OFFSET( VipsMax, y ), - 0, 1000000, 0 ); + 0, VIPS_MAX_COORD, 0 ); VIPS_ARG_INT( class, "size", 4, _( "Size" ), diff --git a/libvips/arithmetic/min.c b/libvips/arithmetic/min.c index cde84cae..6cadf068 100644 --- a/libvips/arithmetic/min.c +++ b/libvips/arithmetic/min.c @@ -437,14 +437,14 @@ vips_min_class_init( VipsMinClass *class ) _( "Horizontal position of minimum" ), VIPS_ARGUMENT_OPTIONAL_OUTPUT, G_STRUCT_OFFSET( VipsMin, x ), - 0, 1000000, 0 ); + 0, VIPS_MAX_COORD, 0 ); VIPS_ARG_INT( class, "y", 3, _( "y" ), _( "Vertical position of minimum" ), VIPS_ARGUMENT_OPTIONAL_OUTPUT, G_STRUCT_OFFSET( VipsMin, y ), - 0, 1000000, 0 ); + 0, VIPS_MAX_COORD, 0 ); VIPS_ARG_INT( class, "size", 4, _( "Size" ), diff --git a/libvips/conversion/copy.c b/libvips/conversion/copy.c index 4a45dea9..b02359bc 100644 --- a/libvips/conversion/copy.c +++ b/libvips/conversion/copy.c @@ -285,21 +285,21 @@ vips_copy_class_init( VipsCopyClass *class ) _( "Image width in pixels" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsCopy, width ), - 0, 1000000, 0 ); + 0, VIPS_MAX_COORD, 0 ); VIPS_ARG_INT( class, "height", 4, _( "Height" ), _( "Image height in pixels" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsCopy, height ), - 0, 1000000, 0 ); + 0, VIPS_MAX_COORD, 0 ); VIPS_ARG_INT( class, "bands", 5, _( "Bands" ), _( "Number of bands in image" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsCopy, bands ), - 0, 1000000, 0 ); + 0, VIPS_MAX_COORD, 0 ); VIPS_ARG_ENUM( class, "format", 6, _( "Format" ), @@ -341,14 +341,14 @@ vips_copy_class_init( VipsCopyClass *class ) _( "Horizontal offset of origin" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsCopy, xoffset ), - -1000000, 1000000, 0 ); + -VIPS_MAX_COORD, VIPS_MAX_COORD, 0 ); VIPS_ARG_INT( class, "yoffset", 12, _( "Yoffset" ), _( "Vertical offset of origin" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsCopy, yoffset ), - -1000000, 1000000, 0 ); + -VIPS_MAX_COORD, VIPS_MAX_COORD, 0 ); } static void diff --git a/libvips/conversion/wrap.c b/libvips/conversion/wrap.c index c85ed6c6..7c02f208 100644 --- a/libvips/conversion/wrap.c +++ b/libvips/conversion/wrap.c @@ -126,14 +126,14 @@ vips_wrap_class_init( VipsWrapClass *class ) _( "Left edge of input in output" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsWrap, x ), - -1000000, 1000000, 0 ); + -VIPS_MAX_COORD, VIPS_MAX_COORD, 0 ); VIPS_ARG_INT( class, "y", 4, _( "y" ), _( "Top edge of input in output" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsWrap, y ), - -1000000, 1000000, 0 ); + -VIPS_MAX_COORD, VIPS_MAX_COORD, 0 ); } diff --git a/libvips/create/black.c b/libvips/create/black.c index 83ac451c..dfa2ede2 100644 --- a/libvips/create/black.c +++ b/libvips/create/black.c @@ -130,21 +130,21 @@ vips_black_class_init( VipsBlackClass *class ) _( "Image width in pixels" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsBlack, width ), - 1, 1000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); VIPS_ARG_INT( class, "height", 5, _( "Height" ), _( "Image height in pixels" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsBlack, height ), - 1, 1000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); VIPS_ARG_INT( class, "bands", 6, _( "Bands" ), _( "Number of bands in image" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsBlack, bands ), - 1, 1000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); } static void diff --git a/libvips/create/fractsurf.c b/libvips/create/fractsurf.c index 6d571176..e6c07ef8 100644 --- a/libvips/create/fractsurf.c +++ b/libvips/create/fractsurf.c @@ -103,14 +103,14 @@ vips_fractsurf_class_init( VipsFractsurfClass *class ) _( "Image width in pixels" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsFractsurf, width ), - 1, 1000000, 64 ); + 1, VIPS_MAX_COORD, 64 ); VIPS_ARG_INT( class, "height", 5, _( "Height" ), _( "Image height in pixels" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsFractsurf, height ), - 1, 1000000, 64 ); + 1, VIPS_MAX_COORD, 64 ); VIPS_ARG_DOUBLE( class, "fractal_dimension", 8, _( "Fractal dimension" ), diff --git a/libvips/create/gaussnoise.c b/libvips/create/gaussnoise.c index 09145a68..5fee656e 100644 --- a/libvips/create/gaussnoise.c +++ b/libvips/create/gaussnoise.c @@ -154,14 +154,14 @@ vips_gaussnoise_class_init( VipsGaussnoiseClass *class ) _( "Image width in pixels" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsGaussnoise, width ), - 1, 1000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); VIPS_ARG_INT( class, "height", 5, _( "Height" ), _( "Image height in pixels" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsGaussnoise, height ), - 1, 1000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); VIPS_ARG_DOUBLE( class, "mean", 6, _( "Mean" ), diff --git a/libvips/create/point.c b/libvips/create/point.c index 5eff84ac..06e14229 100644 --- a/libvips/create/point.c +++ b/libvips/create/point.c @@ -144,14 +144,14 @@ vips_point_class_init( VipsPointClass *class ) _( "Image width in pixels" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsPoint, width ), - 1, 1000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); VIPS_ARG_INT( class, "height", 3, _( "Height" ), _( "Image height in pixels" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsPoint, height ), - 1, 1000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); VIPS_ARG_BOOL( class, "uchar", 4, _( "Uchar" ), diff --git a/libvips/create/text.c b/libvips/create/text.c index 01262927..f7ea3122 100644 --- a/libvips/create/text.c +++ b/libvips/create/text.c @@ -298,7 +298,7 @@ vips_text_class_init( VipsTextClass *class ) _( "Maximum image width in pixels" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsText, width ), - 0, 1000000, 0 ); + 0, VIPS_MAX_COORD, 0 ); VIPS_ARG_ENUM( class, "align", 7, _( "Align" ), diff --git a/libvips/create/xyz.c b/libvips/create/xyz.c index d4a9940e..852de729 100644 --- a/libvips/create/xyz.c +++ b/libvips/create/xyz.c @@ -194,35 +194,35 @@ vips_xyz_class_init( VipsXyzClass *class ) _( "Image width in pixels" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsXyz, width ), - 1, 1000000, 64 ); + 1, VIPS_MAX_COORD, 64 ); VIPS_ARG_INT( class, "height", 5, _( "Height" ), _( "Image height in pixels" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsXyz, height ), - 1, 1000000, 64 ); + 1, VIPS_MAX_COORD, 64 ); VIPS_ARG_INT( class, "csize", 6, _( "csize" ), _( "Size of third dimension" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsXyz, csize ), - 1, 1000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); VIPS_ARG_INT( class, "dsize", 7, _( "dsize" ), _( "Size of fourth dimension" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsXyz, dsize ), - 1, 1000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); VIPS_ARG_INT( class, "esize", 8, _( "esize" ), _( "Size of fifth dimension" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsXyz, esize ), - 1, 1000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); } diff --git a/libvips/foreign/dzsave.c b/libvips/foreign/dzsave.c index cee9bda7..aac907e4 100644 --- a/libvips/foreign/dzsave.c +++ b/libvips/foreign/dzsave.c @@ -55,6 +55,8 @@ * - allow zip > 4gb if we have a recent libgsf * 9/9/15 * - better overlap handling, thanks robclouth + * 25/11/15 + * - always strip tile metadata */ /* @@ -1141,8 +1143,12 @@ strip_work( VipsThreadState *state, void *a ) x = t; } + /* Hopefully no one will want the same metadata on all the tiles. + */ vips_image_set_int( x, "hide-progress", 1 ); - if( vips_image_write_to_buffer( x, dz->suffix, &buf, &len, NULL ) ) { + if( vips_image_write_to_buffer( x, dz->suffix, &buf, &len, + "strip", TRUE, + NULL ) ) { g_object_unref( x ); return( -1 ); } diff --git a/libvips/foreign/rawload.c b/libvips/foreign/rawload.c index 39f89278..98cf4ec5 100644 --- a/libvips/foreign/rawload.c +++ b/libvips/foreign/rawload.c @@ -124,21 +124,21 @@ vips_foreign_load_raw_class_init( VipsForeignLoadRawClass *class ) _( "Image width in pixels" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsForeignLoadRaw, width ), - 0, 1000000, 0 ); + 0, VIPS_MAX_COORD, 0 ); VIPS_ARG_INT( class, "height", 11, _( "Height" ), _( "Image height in pixels" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsForeignLoadRaw, height ), - 0, 1000000, 0 ); + 0, VIPS_MAX_COORD, 0 ); VIPS_ARG_INT( class, "bands", 12, _( "Bands" ), _( "Number of bands in image" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsForeignLoadRaw, bands ), - 0, 1000000, 0 ); + 0, VIPS_MAX_COORD, 0 ); VIPS_ARG_UINT64( class, "offset", 13, _( "Size of header" ), diff --git a/libvips/foreign/tiff2vips.c b/libvips/foreign/tiff2vips.c index fb620d87..e1fa9609 100644 --- a/libvips/foreign/tiff2vips.c +++ b/libvips/foreign/tiff2vips.c @@ -1126,9 +1126,9 @@ parse_header( ReadTiff *rtiff, VipsImage *out ) */ if( width <= 0 || - width > 10000000 || + width > VIPS_MAX_COORD || height <= 0 || - height > 10000000 ) { + height > VIPS_MAX_COORD ) { vips_error( "tiff2vips", "%s", _( "width/height out of range" ) ); return( -1 ); diff --git a/libvips/foreign/vips2jpeg.c b/libvips/foreign/vips2jpeg.c index 10bae518..dc153732 100644 --- a/libvips/foreign/vips2jpeg.c +++ b/libvips/foreign/vips2jpeg.c @@ -69,7 +69,9 @@ * - omit oversized jpeg markers * 15/7/15 * - exif tags use @name, not @title -* - set arbitrary exif tags from metadata + * - set arbitrary exif tags from metadata + * 25/11/15 + * - don't write JFIF headers if we are stripping, thanks Benjamin */ /* @@ -940,9 +942,9 @@ write_vips( Write *write, int qfac, const char *profile, #ifdef HAVE_JPEG_EXT_PARAMS /* Reset compression profile to libjpeg defaults */ - if( jpeg_c_int_param_supported( &write->cinfo, JINT_COMPRESS_PROFILE ) ) { - jpeg_c_set_int_param( &write->cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST ); - } + if( jpeg_c_int_param_supported( &write->cinfo, JINT_COMPRESS_PROFILE ) ) + jpeg_c_set_int_param( &write->cinfo, + JINT_COMPRESS_PROFILE, JCP_FASTEST ); #endif /* Rest to default. @@ -955,61 +957,62 @@ write_vips( Write *write, int qfac, const char *profile, write->cinfo.optimize_coding = optimize_coding; #ifdef HAVE_JPEG_EXT_PARAMS - /* Apply trellis quantisation to each 8x8 block. Infers "optimize_coding". + /* Apply trellis quantisation to each 8x8 block. Implies + * "optimize_coding". */ if( trellis_quant ) { - if ( jpeg_c_bool_param_supported( - &write->cinfo, JBOOLEAN_TRELLIS_QUANT ) ) { + if( jpeg_c_bool_param_supported( &write->cinfo, + JBOOLEAN_TRELLIS_QUANT ) ) { jpeg_c_set_bool_param( &write->cinfo, JBOOLEAN_TRELLIS_QUANT, TRUE ); write->cinfo.optimize_coding = TRUE; } - else { - vips_warn( "vips2jpeg", "%s", _( "trellis_quant unsupported" ) ); - } + else + vips_warn( "vips2jpeg", + "%s", _( "trellis_quant unsupported" ) ); } - /* Apply overshooting to samples with extreme values e.g. 0 & 255 for 8-bit. + + /* Apply overshooting to samples with extreme values e.g. 0 & 255 + * for 8-bit. */ if( overshoot_deringing ) { - if ( jpeg_c_bool_param_supported( - &write->cinfo, JBOOLEAN_OVERSHOOT_DERINGING ) ) { + if( jpeg_c_bool_param_supported( &write->cinfo, + JBOOLEAN_OVERSHOOT_DERINGING ) ) jpeg_c_set_bool_param( &write->cinfo, JBOOLEAN_OVERSHOOT_DERINGING, TRUE ); - } - else { - vips_warn( "vips2jpeg", "%s", _( "overshoot_deringing unsupported" ) ); - } + else + vips_warn( "vips2jpeg", + "%s", _( "overshoot_deringing unsupported" ) ); } /* Split the spectrum of DCT coefficients into separate scans. - * Requires progressive output. Must be set before jpeg_simple_progression. + * Requires progressive output. Must be set before + * jpeg_simple_progression. */ if( optimize_scans ) { if( progressive ) { - if( jpeg_c_bool_param_supported( - &write->cinfo, JBOOLEAN_OPTIMIZE_SCANS ) ) { - jpeg_c_set_bool_param( &write->cinfo, JBOOLEAN_OPTIMIZE_SCANS, TRUE ); - } - else { - vips_warn( "vips2jpeg", "%s", _( "Ignoring optimize_scans" ) ); - } + if( jpeg_c_bool_param_supported( &write->cinfo, + JBOOLEAN_OPTIMIZE_SCANS ) ) + jpeg_c_set_bool_param( &write->cinfo, + JBOOLEAN_OPTIMIZE_SCANS, TRUE ); + else + vips_warn( "vips2jpeg", + "%s", _( "Ignoring optimize_scans" ) ); } - else { + else vips_warn( "vips2jpeg", "%s", _( "Ignoring optimize_scans for baseline" ) ); - } } #else - /* Using jpeglib.h without extension parameters, warn of ignored options. + /* Using jpeglib.h without extension parameters, warn of ignored + * options. */ - if ( trellis_quant ) { + if( trellis_quant ) vips_warn( "vips2jpeg", "%s", _( "Ignoring trellis_quant" ) ); - } - if ( overshoot_deringing ) { - vips_warn( "vips2jpeg", "%s", _( "Ignoring overshoot_deringing" ) ); - } - if ( optimize_scans ) { + if( overshoot_deringing ) + vips_warn( "vips2jpeg", + "%s", _( "Ignoring overshoot_deringing" ) ); + if( optimize_scans ) vips_warn( "vips2jpeg", "%s", _( "Ignoring optimize_scans" ) ); - } #endif /* Enable progressive write. @@ -1028,6 +1031,11 @@ write_vips( Write *write, int qfac, const char *profile, } } + /* Don't write the APP0 JFIF headers if we are stripping. + */ + if( strip ) + write->cinfo.write_JFIF_header = FALSE; + /* Build compress tables. */ jpeg_start_compress( &write->cinfo, TRUE ); diff --git a/libvips/histogram/hist_local.c b/libvips/histogram/hist_local.c index 15c518f7..59472ea2 100644 --- a/libvips/histogram/hist_local.c +++ b/libvips/histogram/hist_local.c @@ -313,14 +313,14 @@ vips_hist_local_class_init( VipsHistLocalClass *class ) _( "Window width in pixels" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsHistLocal, width ), - 1, 1000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); VIPS_ARG_INT( class, "height", 5, _( "Height" ), _( "Window height in pixels" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsHistLocal, height ), - 1, 1000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); } static void diff --git a/libvips/include/vips/image.h b/libvips/include/vips/image.h index 48fc8068..1c354757 100644 --- a/libvips/include/vips/image.h +++ b/libvips/include/vips/image.h @@ -47,6 +47,15 @@ extern "C" { #define VIPS_MAGIC_INTEL (0xb6a6f208U) #define VIPS_MAGIC_SPARC (0x08f2a6b6U) +/* We have a maximum value for a coordinate at various points for sanity + * checking. For example, vips_black() has a max with and height. We use int + * for width/height so we could go up to 2bn, but it's good to have a lower + * value set so we can see crazy numbers early. + * + * This was 1m for a while, but someone found a use for a 4m wide image. + */ +#define VIPS_MAX_COORD (10000000) + typedef enum { VIPS_DEMAND_STYLE_ERROR = -1, VIPS_DEMAND_STYLE_SMALLTILE, @@ -429,6 +438,8 @@ VipsImage *vips_image_new_from_file_raw( const char *filename, int xsize, int ysize, int bands, guint64 offset ); VipsImage *vips_image_new_from_memory( const void *data, size_t size, int width, int height, int bands, VipsBandFormat format ); +VipsImage *vips_image_new_from_memory_copy( const void *data, size_t size, + int width, int height, int bands, VipsBandFormat format ); VipsImage *vips_image_new_from_buffer( const void *buf, size_t len, const char *option_string, ... ) __attribute__((sentinel)); diff --git a/libvips/iofuncs/generate.c b/libvips/iofuncs/generate.c index 41a6c3da..e24cd1d7 100644 --- a/libvips/iofuncs/generate.c +++ b/libvips/iofuncs/generate.c @@ -193,7 +193,7 @@ vips__link_break_all( VipsImage *image ) typedef struct _LinkMap { gboolean upstream; - int *serial; + int serial; VipsSListMap2Fn fn; void *a; void *b; @@ -206,9 +206,9 @@ vips__link_mapp( VipsImage *image, LinkMap *map ) /* Loop? */ - if( image->serial == *map->serial ) + if( image->serial == map->serial ) return( NULL ); - image->serial = *map->serial; + image->serial = map->serial; if( (res = map->fn( image, map->a, map->b )) ) return( res ); @@ -240,8 +240,6 @@ vips__link_map( VipsImage *image, gboolean upstream, GSList *p; void *result; - serial += 1; - images = NULL; /* The function might do anything, including removing images @@ -251,13 +249,19 @@ vips__link_map( VipsImage *image, gboolean upstream, */ map.upstream = upstream; - map.serial = &serial; map.fn = (VipsSListMap2Fn) vips__link_map_cb; map.a = (void *) &images; map.b = NULL; + /* We will be walking the tree of images and updating the ->serial + * member. There will be intense confusion if two threads try to do + * this at the same time. + */ g_mutex_lock( vips__global_lock ); + serial += 1; + map.serial = serial; + vips__link_mapp( image, &map ); for( p = images; p; p = p->next ) diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index 5969857b..d9608f6f 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -6,6 +6,8 @@ * - vips_image_write() didn't ref non-partial sources * 18/4/15 * - add vips_image_copy_memory() + * 25/11/15 + * - add vips_image_new_from_memory_copy() */ /* @@ -1117,21 +1119,21 @@ vips_image_class_init( VipsImageClass *class ) _( "Image width in pixels" ), VIPS_ARGUMENT_SET_ALWAYS, G_STRUCT_OFFSET( VipsImage, Xsize ), - 1, 1000000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); VIPS_ARG_INT( class, "height", 3, _( "Height" ), _( "Image height in pixels" ), VIPS_ARGUMENT_SET_ALWAYS, G_STRUCT_OFFSET( VipsImage, Ysize ), - 1, 1000000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); VIPS_ARG_INT( class, "bands", 4, _( "Bands" ), _( "Number of bands in image" ), VIPS_ARGUMENT_SET_ALWAYS, G_STRUCT_OFFSET( VipsImage, Bands ), - 1, 1000000000, 1 ); + 1, VIPS_MAX_COORD, 1 ); VIPS_ARG_ENUM( class, "format", 5, _( "Format" ), @@ -1173,14 +1175,14 @@ vips_image_class_init( VipsImageClass *class ) _( "Horizontal offset of origin" ), VIPS_ARGUMENT_SET_ALWAYS, G_STRUCT_OFFSET( VipsImage, Xoffset ), - -1000000, 1000000, 0 ); + -VIPS_MAX_COORD, VIPS_MAX_COORD, 0 ); VIPS_ARG_INT( class, "yoffset", 11, _( "Yoffset" ), _( "Vertical offset of origin" ), VIPS_ARGUMENT_SET_ALWAYS, G_STRUCT_OFFSET( VipsImage, Yoffset ), - -1000000, 1000000, 0 ); + -VIPS_MAX_COORD, VIPS_MAX_COORD, 0 ); VIPS_ARG_STRING( class, "filename", 12, _( "Filename" ), @@ -1415,6 +1417,8 @@ vips_image_invalidate_all_cb( VipsImage *image ) void vips_image_invalidate_all( VipsImage *image ) { + VIPS_DEBUG_MSG( "vips_image_invalidate_all: %p\n", image ); + (void) vips__link_map( image, FALSE, (VipsSListMap2Fn) vips_image_invalidate_all_cb, NULL, NULL ); } @@ -1979,9 +1983,15 @@ vips_image_new_from_file_raw( const char *filename, * responsibility for the area of memory, it's up to you to make sure it's * freed when the image is closed. See for example #VipsObject::close. * + * Because VIPS is "borrowing" @data from the caller, this function is + * extremely dangerous. Unless you are very careful, you will get crashes or + * memory corruption. Use vips_image_new_from_memory_copy() instead if you are + * at all unsure. + * * Use vips_copy() to set other image properties. * - * See also: vips_image_new(), vips_image_write_to_memory(). + * See also: vips_image_new(), vips_image_write_to_memory(), + * vips_image_new_from_memory_copy(). * * Returns: (transfer full): the new #VipsImage, or %NULL on error. */ @@ -2014,7 +2024,7 @@ vips_image_new_from_memory( const void *data, size_t size, if( size > 0 && size < VIPS_IMAGE_SIZEOF_IMAGE( image ) ) { vips_error( "VipsImage", - _( "buffer too small --- " + _( "memory area too small --- " "should be %zd bytes, you passed %zd" ), VIPS_IMAGE_SIZEOF_IMAGE( image ), size ); VIPS_UNREF( image ); @@ -2024,6 +2034,54 @@ vips_image_new_from_memory( const void *data, size_t size, return( image ); } +static void +vips_image_new_from_memory_copy_cb( VipsImage *image, void *data_copy ) +{ + vips_tracked_free( data_copy ); +} + +/** + * vips_image_new_from_memory_copy: + * @data: (array length=size) (element-type guint8) (transfer none): start of memory area + * @size: length of memory area + * @width: image width + * @height: image height + * @bands: image bands (or bytes per pixel) + * @format: image format + * + * Like vips_image_new_from_memory(), but VIPS will make a copy of the memory + * area. This + * means more memory use and an extra copy operation, but is much simpler and + * safer. + * + * See also: vips_image_new_from_memory(). + * + * Returns: (transfer full): the new #VipsImage, or %NULL on error. + */ +VipsImage * +vips_image_new_from_memory_copy( const void *data, size_t size, + int width, int height, int bands, VipsBandFormat format ) +{ + void *data_copy; + VipsImage *image; + + vips_check_init(); + + if( !(data_copy = vips_tracked_malloc( size )) ) + return( NULL ); + memcpy( data_copy, data, size ); + if( !(image = vips_image_new_from_memory( data_copy, size, + width, height, bands, format )) ) { + vips_tracked_free( data_copy ); + return( NULL ); + } + + g_signal_connect( image, "close", + G_CALLBACK( vips_image_new_from_memory_copy_cb ), data_copy ); + + return( image ); +} + /** * vips_image_new_from_buffer: * @buf: (array length=len) (element-type guint8) (transfer none): image data diff --git a/libvips/iofuncs/sinkscreen.c b/libvips/iofuncs/sinkscreen.c index 29e4c512..aea4616c 100644 --- a/libvips/iofuncs/sinkscreen.c +++ b/libvips/iofuncs/sinkscreen.c @@ -7,6 +7,9 @@ * mem use * 20/1/14 * - bg render thread quits on shutdown + * 1/12/15 + * - don't do anything to out or mask after they have closed + * - only run the bg render thread when there's work to do */ /* @@ -135,6 +138,11 @@ typedef struct _Render { /* Hash of tiles with positions. Tiles can be dirty or painted. */ GHashTable *tiles; + + /* A shutdown flag. If ->out or ->mask close, we must no longer do + * anything to them until we shut down too. + */ + gboolean shutdown; } Render; /* Our per-thread state. @@ -158,10 +166,6 @@ G_DEFINE_TYPE( RenderThreadState, render_thread_state, VIPS_TYPE_THREAD_STATE ); */ static GThread *render_thread = NULL; -/* Number of renders with dirty tiles. render_thread queues up on this. - */ -static VipsSemaphore render_dirty_sem; - /* Set this to ask the render thread to quit. */ static gboolean render_kill = FALSE; @@ -217,14 +221,8 @@ render_free( Render *render ) g_assert( render->ref_count == 0 ); g_mutex_lock( render_dirty_lock ); - if( g_slist_find( render_dirty_all, render ) ) { + if( g_slist_find( render_dirty_all, render ) ) render_dirty_all = g_slist_remove( render_dirty_all, render ); - - /* We could vips_semaphore_upn( &render_dirty_sem, -1 ), but - * what's the point. We'd just wake up the bg thread - * for no reason. - */ - } g_mutex_unlock( render_dirty_lock ); vips_g_mutex_free( render->ref_count_lock ); @@ -275,22 +273,16 @@ render_unref( Render *render ) return( 0 ); } -/* Wait for a render with dirty tiles. +/* Get the first render with dirty tiles. */ static Render * render_dirty_get( void ) { Render *render; - /* Wait for a render with dirty tiles. - */ - vips_semaphore_down( &render_dirty_sem ); - g_mutex_lock( render_dirty_lock ); - /* Just take the head of the jobs list ... we sort when we add. If - * render_free() is called between our semaphore letting us in - * and the _lock(), render_dirty_all can be NULL. + /* Just take the head of the jobs list ... we sort when we add. */ render = NULL; if( render_dirty_all ) { @@ -433,51 +425,22 @@ render_work( VipsThreadState *state, void *a ) /* All downstream images must drop caches, since we've (effectively) * modified render->out. */ - vips_image_invalidate_all( render->out ); - if( render->mask ) + if( !render->shutdown ) + vips_image_invalidate_all( render->out ); + if( !render->shutdown && + render->mask ) vips_image_invalidate_all( render->mask ); /* Now clients can update. */ - if( render->notify ) + if( !render->shutdown && + render->notify ) render->notify( render->out, &tile->area, render->a ); return( 0 ); } -static int -render_dirty_sort( Render *a, Render *b ) -{ - return( b->priority - a->priority ); -} - -/* Add to the jobs list, if it has work to be done. - */ -static void -render_dirty_put( Render *render ) -{ - g_mutex_lock( render_dirty_lock ); - - if( render->dirty ) { - if( !g_slist_find( render_dirty_all, render ) ) { - render_dirty_all = g_slist_prepend( render_dirty_all, - render ); - render_dirty_all = g_slist_sort( render_dirty_all, - (GCompareFunc) render_dirty_sort ); - - /* Ask the bg thread to stop and reschedule, if it's - * running. - */ - VIPS_DEBUG_MSG_GREEN( "render_dirty_put: " - "reschedule\n" ); - render_reschedule = TRUE; - - vips_semaphore_up( &render_dirty_sem ); - } - } - - g_mutex_unlock( render_dirty_lock ); -} +static void render_dirty_put( Render *render ); /* Main loop for RenderThreads. */ @@ -515,9 +478,15 @@ render_thread_main( void *client ) render_unref( render ); } + /* We are exiting, so render_thread must now be NULL. + */ + render_thread = NULL; + return( NULL ); } +/* Called from vips_shutdown(). + */ void vips__render_shutdown( void ) { @@ -532,36 +501,52 @@ vips__render_shutdown( void ) GThread *thread; thread = render_thread; - render_thread = NULL; g_mutex_unlock( render_dirty_lock ); render_reschedule = TRUE; render_kill = TRUE; - vips_semaphore_up( &render_dirty_sem ); (void) g_thread_join( thread ); } else g_mutex_unlock( render_dirty_lock ); } -/* Create our set of RenderThread. Assume we're single-threaded here. - */ -static int -render_thread_create( void ) +static int +render_dirty_sort( Render *a, Render *b ) { - if( !render_dirty_lock ) { - render_dirty_lock = vips_g_mutex_new(); - vips_semaphore_init( &render_dirty_sem, 0, "render_dirty_sem" ); + return( b->priority - a->priority ); +} + +/* Add to the jobs list, if it has work to be done. + */ +static void +render_dirty_put( Render *render ) +{ + g_mutex_lock( render_dirty_lock ); + + if( render->dirty ) { + if( !g_slist_find( render_dirty_all, render ) ) { + render_dirty_all = g_slist_prepend( render_dirty_all, + render ); + render_dirty_all = g_slist_sort( render_dirty_all, + (GCompareFunc) render_dirty_sort ); + + /* Make sure there is a bg render thread, and get it to + * reschedule. + */ + if( !render_thread ) { + render_thread = vips_g_thread_new( "sink_screen", + render_thread_main, NULL ); + g_assert( render_thread ); + } + VIPS_DEBUG_MSG_GREEN( "render_dirty_put: " + "reschedule\n" ); + render_reschedule = TRUE; + } } - if( !render_thread ) { - if( !(render_thread = vips_g_thread_new( "sink_screen", - render_thread_main, NULL )) ) - return( -1 ); - } - - return( 0 ); + g_mutex_unlock( render_dirty_lock ); } static guint @@ -590,6 +575,13 @@ render_close_cb( VipsImage *image, Render *render ) { VIPS_DEBUG_MSG_AMBER( "render_close_cb\n" ); + /* The output image or mask are closing. This render will stick + * around for a while, since threads can still be running, but it + * must no longer reference ->out or ->mask (for example, invalidating + * them). + */ + render->shutdown = TRUE; + render_unref( render ); /* If this render is being worked on, we want to jog the bg thread, @@ -639,6 +631,8 @@ render_new( VipsImage *in, VipsImage *out, VipsImage *mask, render->dirty = NULL; + render->shutdown = FALSE; + /* Both out and mask must close before we can free the render. */ g_signal_connect( out, "close", @@ -918,20 +912,12 @@ tile_copy( Tile *tile, VipsRegion *to ) } } -static void * -image_start( IMAGE *out, void *a, void *b ) -{ - Render *render = (Render *) a; - - return( vips_region_new( render->in ) ); -} - /* Loop over the output region, filling with data from cache. */ static int image_fill( VipsRegion *out, void *seq, void *a, void *b, gboolean *stop ) { - Render *render = (Render *) a; + Render *render = (Render *) b; int tile_width = render->tile_width; int tile_height = render->tile_height; VipsRegion *reg = (VipsRegion *) seq; @@ -979,16 +965,6 @@ image_fill( VipsRegion *out, void *seq, void *a, void *b, gboolean *stop ) return( 0 ); } -static int -image_stop( void *seq, void *a, void *b ) -{ - VipsRegion *reg = (VipsRegion *) seq; - - g_object_unref( reg ); - - return( 0 ); -} - /* The mask image is 255 / 0 for the state of painted for each tile. */ static int @@ -1038,6 +1014,12 @@ mask_fill( VipsRegion *out, void *seq, void *a, void *b, gboolean *stop ) return( 0 ); } +static void +vips_sink_screen_init( void ) +{ + render_dirty_lock = vips_g_mutex_new(); +} + /** * vips_sink_screen: * @in: input image @@ -1095,12 +1077,11 @@ vips_sink_screen( VipsImage *in, VipsImage *out, VipsImage *mask, int priority, VipsSinkNotify notify_fn, void *a ) { + static GOnce once = G_ONCE_INIT; + Render *render; - /* Make sure the bg work threads are ready. - */ - if( render_thread_create() ) - return( -1 ); + g_once( &once, (GThreadFunc) vips_sink_screen_init, NULL ); if( tile_width <= 0 || tile_height <= 0 || max_tiles < -1 ) { @@ -1131,7 +1112,7 @@ vips_sink_screen( VipsImage *in, VipsImage *out, VipsImage *mask, VIPS_DEBUG_MSG( "vips_sink_screen: max = %d, %p\n", max_tiles, render ); if( vips_image_generate( out, - image_start, image_fill, image_stop, render, NULL ) ) + vips_start_one, image_fill, vips_stop_one, in, render ) ) return( -1 ); if( mask && vips_image_generate( mask, diff --git a/libvips/resample/mapim.c b/libvips/resample/mapim.c index 36431a10..b1354aa5 100644 --- a/libvips/resample/mapim.c +++ b/libvips/resample/mapim.c @@ -183,9 +183,9 @@ vips_mapim_region_minmax( VipsRegion *region, VipsRect *r, VipsRect *bounds ) TYPE py = p1[1]; \ \ if( px < 0 || \ - px > resample->in->Xsize || \ + px >= resample->in->Xsize || \ py < 0 || \ - py > resample->in->Ysize ) { \ + py >= resample->in->Ysize ) { \ for( z = 0; z < ps; z++ ) \ q[z] = 0; \ } \