Merge branch 'master' of github.com:jcupitt/libvips

This commit is contained in:
John Cupitt 2015-12-04 07:28:52 +00:00
commit 3570f61f3d
22 changed files with 261 additions and 187 deletions

View File

@ -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

View File

@ -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:

View File

@ -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" ),

View File

@ -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" ),

View File

@ -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

View File

@ -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 );
}

View File

@ -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

View File

@ -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" ),

View File

@ -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" ),

View File

@ -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" ),

View File

@ -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" ),

View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -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" ),

View File

@ -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 );

View File

@ -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 );

View File

@ -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

View File

@ -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));

View File

@ -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 )

View File

@ -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

View File

@ -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,

View File

@ -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; \
} \