From de981cd9ec80c12137ac156304d58011930a82a1 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 8 Jul 2016 12:18:07 +0100 Subject: [PATCH 01/15] docs tweak --- libvips/create/buildlut.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libvips/create/buildlut.c b/libvips/create/buildlut.c index 16ab774d..e6e41b17 100644 --- a/libvips/create/buildlut.c +++ b/libvips/create/buildlut.c @@ -278,7 +278,8 @@ vips_buildlut_init( VipsBuildlut *lut ) * * This operation builds a lookup table from a set of points. Intermediate * values are generated by piecewise linear interpolation. The lookup table is - * always of type %gdouble, use vips_cast() to change it to the type you need. + * always of type #VIPS_FORMAT_DOUBLE, use vips_cast() to change it to the + * type you need. * * For example, consider this 2 x 2 matrix of (x, y) coordinates: * @@ -331,7 +332,7 @@ vips_buildlut_init( VipsBuildlut *lut ) * several Ys, each becomes a band in the output LUT. You don't need to * start at zero, any integer will do, including negatives. * - * See also: vips_identity(), vips_invertlut(). + * See also: vips_identity(), vips_invertlut(), vips_cast(), vips_maplut(). * * Returns: 0 on success, -1 on error */ From 4c1e6dd02375a4736166caa8da98f26d07af0780 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 9 Jul 2016 18:49:22 +0100 Subject: [PATCH 02/15] turn off chroma subsample for Q > 90 see https://github.com/jcupitt/libvips/issues/482 --- libvips/foreign/jpegsave.c | 3 ++- libvips/foreign/vips2jpeg.c | 21 +++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/libvips/foreign/jpegsave.c b/libvips/foreign/jpegsave.c index f8b3b454..5289f176 100644 --- a/libvips/foreign/jpegsave.c +++ b/libvips/foreign/jpegsave.c @@ -95,7 +95,8 @@ typedef struct _VipsForeignSaveJpeg { */ gboolean trellis_quant; - /* 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. */ gboolean overshoot_deringing; diff --git a/libvips/foreign/vips2jpeg.c b/libvips/foreign/vips2jpeg.c index 91c703ed..dbf25447 100644 --- a/libvips/foreign/vips2jpeg.c +++ b/libvips/foreign/vips2jpeg.c @@ -78,6 +78,8 @@ * - add quant_table * 26/5/16 * - switch to new orientation tag + * 9/7/16 + * - turn off chroma subsample for Q > 90 */ /* @@ -1020,7 +1022,9 @@ write_vips( Write *write, int qfac, const char *profile, */ g_assert( in->BandFmt == VIPS_FORMAT_UCHAR ); g_assert( in->Coding == VIPS_CODING_NONE ); - g_assert( in->Bands == 1 || in->Bands == 3 || in->Bands == 4 ); + g_assert( in->Bands == 1 || + in->Bands == 3 || + in->Bands == 4 ); /* Check input image. */ @@ -1032,7 +1036,8 @@ write_vips( Write *write, int qfac, const char *profile, write->cinfo.image_width = in->Xsize; write->cinfo.image_height = in->Ysize; write->cinfo.input_components = in->Bands; - if( in->Bands == 4 && in->Type == VIPS_INTERPRETATION_CMYK ) { + if( in->Bands == 4 && + in->Type == VIPS_INTERPRETATION_CMYK ) { space = JCS_CMYK; /* IJG always sets an Adobe marker, so we should invert CMYK. */ @@ -1153,9 +1158,11 @@ write_vips( Write *write, int qfac, const char *profile, if( progressive ) jpeg_simple_progression( &write->cinfo ); - /* Turn off chroma subsampling. + /* Turn off chroma subsampling. Follow IM and do it automatically for + * high Q. */ - if( no_subsample ) { + if( no_subsample || + Q > 90 ) { int i; for( i = 0; i < in->Bands; i++ ) { @@ -1250,7 +1257,8 @@ vips__jpeg_write_file( VipsImage *in, */ if( write_vips( write, Q, profile, optimize_coding, progressive, strip, no_subsample, - trellis_quant, overshoot_deringing, optimize_scans, quant_table ) ) { + trellis_quant, overshoot_deringing, optimize_scans, + quant_table ) ) { write_destroy( write ); return( -1 ); } @@ -1535,7 +1543,8 @@ vips__jpeg_write_buffer( VipsImage *in, */ if( write_vips( write, Q, profile, optimize_coding, progressive, strip, no_subsample, - trellis_quant, overshoot_deringing, optimize_scans, quant_table ) ) { + trellis_quant, overshoot_deringing, optimize_scans, + quant_table ) ) { write_destroy( write ); return( -1 ); From f294ec5d9b7c6bf0cddfb69d91303d6e4e3c5c53 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 9 Jul 2016 19:10:17 +0100 Subject: [PATCH 03/15] oops --- libvips/foreign/vips2jpeg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libvips/foreign/vips2jpeg.c b/libvips/foreign/vips2jpeg.c index dbf25447..9b2d52a8 100644 --- a/libvips/foreign/vips2jpeg.c +++ b/libvips/foreign/vips2jpeg.c @@ -1162,7 +1162,7 @@ write_vips( Write *write, int qfac, const char *profile, * high Q. */ if( no_subsample || - Q > 90 ) { + qfac > 90 ) { int i; for( i = 0; i < in->Bands; i++ ) { From 828b36dfe58707eb797e9785e2b61f4129b24ff6 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 11 Jul 2016 10:07:41 +0100 Subject: [PATCH 04/15] fix --fail option to jpegload getting --fail on jpegload working means tilecache must terminate on tile calc error make openslideload not report tile calc errors, it might need a --fail option too see https://github.com/jcupitt/libvips/issues/474 --- libvips/conversion/tilecache.c | 19 ++++++++++------- libvips/foreign/jpeg2vips.c | 35 +++++++++++++++++++++++--------- libvips/foreign/openslide2vips.c | 14 ++++++++----- libvips/foreign/vips2jpeg.c | 1 - 4 files changed, 46 insertions(+), 23 deletions(-) diff --git a/libvips/conversion/tilecache.c b/libvips/conversion/tilecache.c index 75873485..39942b22 100644 --- a/libvips/conversion/tilecache.c +++ b/libvips/conversion/tilecache.c @@ -31,6 +31,8 @@ * - cache minimisation is optional, see "persistent" flag * 26/8/14 Lovell * - free the hash table in _dispose() + * 11/7/16 + * - terminate on tile calc error */ /* @@ -609,6 +611,9 @@ vips_tile_cache_gen( VipsRegion *or, VipsTile *tile; GSList *work; GSList *p; + int result; + + result = 0; VIPS_GATE_START( "vips_tile_cache_gen: wait1" ); @@ -658,8 +663,6 @@ vips_tile_cache_gen( VipsRegion *or, tile = (VipsTile *) p->data; if( tile->state == VIPS_TILE_STATE_PEND ) { - int result; - tile->state = VIPS_TILE_STATE_CALC; VIPS_DEBUG_MSG_RED( "vips_tile_cache_gen: " @@ -688,11 +691,11 @@ vips_tile_cache_gen( VipsRegion *or, } /* If there was an error calculating this - * tile, just warn and carry on. + * tile, black it out and terminate + * calculation. We have to stop so we can + * support things like --fail on jpegload. * - * This can happen with things like reading - * .scn files via openslide. We don't want the - * read to fail because of one broken tile. + * Don't return early, we'd deadlock. */ if( result ) { VIPS_DEBUG_MSG_RED( @@ -707,6 +710,8 @@ vips_tile_cache_gen( VipsRegion *or, vips_error_clear(); vips_region_black( tile->region ); + + *stop = TRUE; } tile->state = VIPS_TILE_STATE_DATA; @@ -749,7 +754,7 @@ vips_tile_cache_gen( VipsRegion *or, g_mutex_unlock( cache->lock ); - return( 0 ); + return( result ); } static int diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index afb57af5..1a1e42df 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -74,6 +74,8 @@ * memory for progressive jpg files * 26/5/16 * - switch to new orientation tag + * 11/7/16 + * - new --fail handling */ /* @@ -186,16 +188,10 @@ readjpeg_free( ReadJpeg *jpeg ) result = 0; if( jpeg->eman.pub.num_warnings != 0 ) { - if( jpeg->fail ) { - vips_error( "VipsJpeg", "%s", vips_error_buffer() ); - result = -1; - } - else { - vips_warn( "VipsJpeg", - _( "read gave %ld warnings" ), - jpeg->eman.pub.num_warnings ); - vips_warn( NULL, "%s", vips_error_buffer() ); - } + vips_warn( "VipsJpeg", + _( "read gave %ld warnings" ), + jpeg->eman.pub.num_warnings ); + vips_warn( NULL, "%s", vips_error_buffer() ); /* Make the message only appear once. */ @@ -991,6 +987,25 @@ read_jpeg_generate( VipsRegion *or, if( setjmp( jpeg->eman.jmp ) ) return( -1 ); + /* If --fail is set, we make read fail on any warnings. This will stop + * on any errors from the previous jpeg_read_scanlines(). + */ + if( jpeg->eman.pub.num_warnings > 0 && + jpeg->fail ) { + vips_error( "VipsJpeg", + _( "read gave %ld warnings" ), + jpeg->eman.pub.num_warnings ); + vips_error( NULL, "%s", vips_error_buffer() ); + + /* Make the message only appear once. + */ + jpeg->eman.pub.num_warnings = 0; + + *stop = TRUE; + + return( -1 ); + } + for( y = 0; y < r->height; y++ ) { JSAMPROW row_pointer[1]; diff --git a/libvips/foreign/openslide2vips.c b/libvips/foreign/openslide2vips.c index 4df9e2b1..93b05a6b 100644 --- a/libvips/foreign/openslide2vips.c +++ b/libvips/foreign/openslide2vips.c @@ -47,6 +47,8 @@ * - do argb -> rgba for associated as well * 27/1/15 * - unpremultiplication speedups for fully opaque/transparent pixels + * 11/7/16 + * - just warn on tile read error */ /* @@ -471,14 +473,16 @@ vips__openslide_generate( VipsRegion *out, rslide->level, r->width, r->height ); + /* Only warn on error: we don't want to make the whole image unreadable + * because of one broken tile. + * + * FIXME ... add a --fail option like jpegload + */ error = openslide_get_error( rslide->osr ); - if( error ) { - vips_error( "openslide2vips", + if( error ) + vips_warn( "openslide2vips", _( "reading region: %s" ), error ); - return( -1 ); - } - /* Since we are inside a cache, we know buf must be continuous. */ argb2rgba( buf, n, bg ); diff --git a/libvips/foreign/vips2jpeg.c b/libvips/foreign/vips2jpeg.c index 9b2d52a8..04ec6aa6 100644 --- a/libvips/foreign/vips2jpeg.c +++ b/libvips/foreign/vips2jpeg.c @@ -227,7 +227,6 @@ write_new( VipsImage *in ) write->cinfo.err = jpeg_std_error( &write->eman.pub ); write->eman.pub.error_exit = vips__new_error_exit; write->eman.pub.output_message = vips__new_output_message; - write->eman.pub.output_message = vips__new_output_message; write->eman.fp = NULL; write->profile_bytes = NULL; write->profile_length = 0; From 097ecd07c111cd380a3e371c528fb25ecdd82f6c Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 13 Jul 2016 07:06:43 +0100 Subject: [PATCH 05/15] doc improvements --- libvips/resample/affine.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libvips/resample/affine.c b/libvips/resample/affine.c index d67c184b..34ae4144 100644 --- a/libvips/resample/affine.c +++ b/libvips/resample/affine.c @@ -623,12 +623,12 @@ vips_affine_init( VipsAffine *affine ) * * Optional arguments: * - * * @interpolate: interpolate pixels with this - * * @oarea: output rectangle - * * @idx: input horizontal offset - * * @idy: input vertical offset - * * @odx: output horizontal offset - * * @ody: output vertical offset + * * @interpolate: #VipsInterpolate, interpolate pixels with this + * * @oarea: #VipsArrayInt, output rectangle + * * @idx: %gdouble, input horizontal offset + * * @idy: %gdouble, input vertical offset + * * @odx: %gdouble, output horizontal offset + * * @ody: %gdouble, output vertical offset * * This operator performs an affine transform on an image using @interpolate. * From 23d9bad5811e73ff8672b98bb95b685a81d8dfcf Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 13 Jul 2016 09:24:41 +0100 Subject: [PATCH 06/15] doc improvements --- configure.ac | 1 + libvips/Makefile.am | 4 +--- libvips/colour/Lab2XYZ.c | 4 ++-- libvips/colour/XYZ2Lab.c | 4 ++-- libvips/conversion/arrayjoin.c | 15 ++++++++------- libvips/conversion/autorot.c | 4 ++-- libvips/iofuncs/image.c | 2 ++ libvips/resample/reduce.c | 19 ++++++++++++++++--- 8 files changed, 34 insertions(+), 19 deletions(-) diff --git a/configure.ac b/configure.ac index 7adf1160..0c4fed37 100644 --- a/configure.ac +++ b/configure.ac @@ -112,6 +112,7 @@ headers="\ draw.h \ morphology.h \ type.h \ + rect.h \ memory.h \ region.h" diff --git a/libvips/Makefile.am b/libvips/Makefile.am index 4b378df1..b2178c5d 100644 --- a/libvips/Makefile.am +++ b/libvips/Makefile.am @@ -98,10 +98,8 @@ Vips_8_0_gir_LIBS = libvips.la Vips_8_0_gir_FILES = $(introspection_sources) INTROSPECTION_GIRS += Vips-8.0.gir -# don't use --warn-all --verbose -# we have an unusual markup with optional args and we don't want to see all -# those warnings Vips_8_0_gir_SCANNERFLAGS = \ + --warn-all --verbose \ --program=./introspect \ --identifier-prefix=Vips \ --identifier-prefix=vips \ diff --git a/libvips/colour/Lab2XYZ.c b/libvips/colour/Lab2XYZ.c index d24f0d52..417983e7 100644 --- a/libvips/colour/Lab2XYZ.c +++ b/libvips/colour/Lab2XYZ.c @@ -197,9 +197,9 @@ vips_Lab2XYZ_init( VipsLab2XYZ *Lab2XYZ ) * @out: output image * @...: %NULL-terminated list of optional named arguments * - * optional arguments: + * Optional arguments: * - * @temp: colour temperature + * * @temp: #VipsArrayDouble, colour temperature * * Turn Lab to XYZ. The colour temperature defaults to D65, but can be * specified with @temp. diff --git a/libvips/colour/XYZ2Lab.c b/libvips/colour/XYZ2Lab.c index 97d9af1d..6d96b46a 100644 --- a/libvips/colour/XYZ2Lab.c +++ b/libvips/colour/XYZ2Lab.c @@ -250,9 +250,9 @@ vips_XYZ2Lab_init( VipsXYZ2Lab *XYZ2Lab ) * @out: output image * @...: %NULL-terminated list of optional named arguments * - * optional arguments: + * Optional arguments: * - * @temp: colour temperature + * * @temp: #VipsArrayDouble, colour temperature * * Turn XYZ to Lab, optionally specifying the colour temperature. @temp * defaults to D65. diff --git a/libvips/conversion/arrayjoin.c b/libvips/conversion/arrayjoin.c index 227c28b4..a4536a43 100644 --- a/libvips/conversion/arrayjoin.c +++ b/libvips/conversion/arrayjoin.c @@ -394,16 +394,17 @@ vips_arrayjoinv( VipsImage **in, VipsImage **out, int n, va_list ap ) * @in: (array length=n) (transfer none): array of input images * @out: output image * @n: number of input images + * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * - * * @across: number of images per row - * * @shim: space between images, in pixels - * * @background: background ink colour - * * @halign: low, centre or high alignment - * * @valign: low, centre or high alignment - * * @hspacing: horizontal distance between images - * * @vspacing: vertical distance between images + * * @across: %gint, number of images per row + * * @shim: %gint, space between images, in pixels + * * @background: #VipsArrayDouble, background ink colour + * * @halign: #VipsAlign, low, centre or high alignment + * * @valign: #VipsAlign, low, centre or high alignment + * * @hspacing: %gint, horizontal distance between images + * * @vspacing: %gint, vertical distance between images * * Lay out the images in @in in a grid. The grid is @across images across and * however high is necessary to use up all of @in. Images are set down diff --git a/libvips/conversion/autorot.c b/libvips/conversion/autorot.c index 0a700010..c693dbeb 100644 --- a/libvips/conversion/autorot.c +++ b/libvips/conversion/autorot.c @@ -126,9 +126,9 @@ vips_autorot_remove_angle_sub( VipsImage *image, /** * vips_autorot_remove_angle: - * @im: image to remove orientation from + * @image: image to remove orientation from * - * Remove the orientation tag on @im. Also remove any exif orientation tags. + * Remove the orientation tag on @image. Also remove any exif orientation tags. * * See also: vips_autorot_get_angle(). */ diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index a7c441a1..a296f1e3 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -2997,6 +2997,8 @@ vips_image_rewind_output( VipsImage *image ) * have made it yourself), use vips_image_wio_input() instead. * * See also: vips_image_wio_input(). + * + * Returns: (transfer full): the new #VipsImage, or %NULL on error. */ VipsImage * vips_image_copy_memory( VipsImage *image ) diff --git a/libvips/resample/reduce.c b/libvips/resample/reduce.c index 1304e609..27e7661d 100644 --- a/libvips/resample/reduce.c +++ b/libvips/resample/reduce.c @@ -50,6 +50,19 @@ #include "presample.h" +/** + * VipsKernel: + * @VIPS_KERNEL_NEAREST: The nearest pixel to the point. + * @VIPS_KERNEL_LINEAR: Calculate a pixel value using linear interpolation. + * @VIPS_KERNEL_CUBIC: Calculate using a 4-element cubic kernel. + * @VIPS_KERNEL_LANCZOS2: Calculate with a two-lobe Lanczos kernel. + * @VIPS_KERNEL_LANCZOS3: Calculate with a three-lobe Lanczos kernel. + * + * The resampling kernels vips supports. See vips_reduce(), for example. + * + * The Lanczos kernels vary in size with the downsampling ratio. + */ + typedef struct _VipsReduce { VipsResample parent_instance; @@ -141,15 +154,15 @@ vips_reduce_init( VipsReduce *reduce ) * @in: input image * @out: output image * @xshrink: horizontal shrink - * @shrinke: vertical shrink + * @yshrink: vertical shrink * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @kernel: #VipsKernel to use to interpolate (default: lanczos3) * - * Reduce @in by a pair of factors with a pair of 1D interpolators. This - * will not work well for shrink factors greater than two. + * Reduce @in by a pair of factors with a pair of 1D kernels. This + * will not work well for shrink factors greater than three. * * This is a very low-level operation: see vips_resize() for a more * convenient way to resize images. From b814baa78c98af12f3741e47c1065e50dcaddfde Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 13 Jul 2016 17:07:26 +0100 Subject: [PATCH 07/15] fix various small doc problems --- libvips/include/vips/object.h | 8 ++------ libvips/iofuncs/object.c | 6 +++--- libvips/iofuncs/rect.c | 4 ++-- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/libvips/include/vips/object.h b/libvips/include/vips/object.h index 4123a526..53e16397 100644 --- a/libvips/include/vips/object.h +++ b/libvips/include/vips/object.h @@ -296,11 +296,6 @@ typedef struct _VipsArgumentInstance { */ typedef GHashTable VipsArgumentTable; -VipsArgumentInstance *vips__argument_get_instance( - VipsArgumentClass *argument_class, - VipsObject *object ); -VipsArgument *vips__argument_table_lookup( VipsArgumentTable *argument_class, - GParamSpec *pspec ); void vips__object_set_member( VipsObject *object, GParamSpec *pspec, GObject **member, GObject *argument ); typedef void *(*VipsArgumentMapFn)( VipsObject *object, GParamSpec *pspec, @@ -622,7 +617,8 @@ GType vips_type_find( const char *basename, const char *nickname ); const char *vips_nickname_find( GType type ); void *vips_class_map_all( GType type, VipsClassMapFn fn, void *a ); -VipsObjectClass *vips_class_find( const char *basename, const char *nickname ); +const VipsObjectClass *vips_class_find( const char *basename, + const char *nickname ); VipsObject **vips_object_local_array( VipsObject *parent, int n ); diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index 5e410bfa..77841dcc 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -2691,9 +2691,9 @@ test_name( VipsObjectClass *class, const char *nickname ) * * See also: vips_type_find() * - * Returns: the found class. + * Returns: (transfer none): the found class. */ -VipsObjectClass * +const VipsObjectClass * vips_class_find( const char *basename, const char *nickname ) { const char *classname = basename ? basename : "VipsObject"; @@ -2802,7 +2802,7 @@ vips_type_find( const char *basename, const char *nickname ) g_type_is_a( hit->type, base ) ) type = hit->type; else { - VipsObjectClass *class; + const VipsObjectClass *class; if( !(class = vips_class_find( basename, nickname )) ) return( 0 ); diff --git a/libvips/iofuncs/rect.c b/libvips/iofuncs/rect.c index 05eec762..a2f4ec15 100644 --- a/libvips/iofuncs/rect.c +++ b/libvips/iofuncs/rect.c @@ -203,12 +203,12 @@ vips_rect_unionrect( const VipsRect *r1, const VipsRect *r2, VipsRect *out ) } /** - * vips_rect_dup: + * vips_rect_dup: (skip) * @r: rectangle to duplicate * * Duplicate a rect to the heap. You need to free the result with vips_free(). * - * Returns: a pointer to copy of @r allocated on the heap. + * Returns: (transfer full): a pointer to copy of @r allocated on the heap. */ VipsRect * vips_rect_dup( const VipsRect *r ) From 9c18f1b4d54e1036c00a1032a9b62b6b86ac7753 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 13 Jul 2016 22:39:16 +0100 Subject: [PATCH 08/15] yet mmore doc fixes classes are working again in gtk-doc, yay --- libvips/foreign/foreign.c | 6 +++--- libvips/include/vips/foreign.h | 12 +++++++++--- libvips/include/vips/image.h | 6 ++++-- libvips/include/vips/interpolate.h | 4 +++- libvips/include/vips/object.h | 6 ++++-- libvips/include/vips/operation.h | 4 +++- libvips/include/vips/region.h | 4 +++- libvips/include/vips/threadpool.h | 5 ++++- libvips/include/vips/type.h | 16 ++++++++-------- 9 files changed, 41 insertions(+), 22 deletions(-) diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 04fd4551..707a1ed9 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -609,7 +609,7 @@ vips_foreign_find_load_buffer( const void *data, size_t size ) gboolean vips_foreign_is_a( const char *loader, const char *filename ) { - VipsObjectClass *class; + const VipsObjectClass *class; VipsForeignLoadClass *load_class; if( !(class = vips_class_find( "VipsForeignLoad", loader )) ) @@ -636,7 +636,7 @@ vips_foreign_is_a( const char *loader, const char *filename ) gboolean vips_foreign_is_a_buffer( const char *loader, const void *data, size_t size ) { - VipsObjectClass *class; + const VipsObjectClass *class; VipsForeignLoadClass *load_class; if( !(class = vips_class_find( "VipsForeignLoad", loader )) ) @@ -662,7 +662,7 @@ vips_foreign_is_a_buffer( const char *loader, const void *data, size_t size ) VipsForeignFlags vips_foreign_flags( const char *loader, const char *filename ) { - VipsObjectClass *class; + const VipsObjectClass *class; if( (class = vips_class_find( "VipsForeignLoad", loader )) ) { VipsForeignLoadClass *load_class = diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index 14d9a824..2245167b 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -77,7 +77,9 @@ typedef struct _VipsForeignClass { } VipsForeignClass; -GType vips_foreign_get_type( void ); +/* Don't put spaces around void here, it breaks gtk-doc. + */ +GType vips_foreign_get_type(void); /* Map over and find formats. This uses type introspection to loop over * subclasses of VipsForeign. @@ -211,7 +213,9 @@ typedef struct _VipsForeignLoadClass { int (*load)( VipsForeignLoad *load ); } VipsForeignLoadClass; -GType vips_foreign_load_get_type( void ); +/* Don't put spaces around void here, it breaks gtk-doc. + */ +GType vips_foreign_load_get_type(void); const char *vips_foreign_find_load( const char *filename ); const char *vips_foreign_find_load_buffer( const void *data, size_t size ); @@ -313,7 +317,9 @@ typedef struct _VipsForeignSaveClass { gboolean coding[VIPS_CODING_LAST]; } VipsForeignSaveClass; -GType vips_foreign_save_get_type( void ); +/* Don't put spaces around void here, it breaks gtk-doc. + */ +GType vips_foreign_save_get_type(void); const char *vips_foreign_find_save( const char *filename ); const char *vips_foreign_find_save_buffer( const char *suffix ); diff --git a/libvips/include/vips/image.h b/libvips/include/vips/image.h index ee8d7a6b..3fedba71 100644 --- a/libvips/include/vips/image.h +++ b/libvips/include/vips/image.h @@ -173,7 +173,7 @@ typedef struct _VipsProgress { VIPS_TYPE_IMAGE, VipsImageClass )) typedef struct _VipsImage { - VipsObject parent_object; + VipsObject parent_instance; /*< private >*/ @@ -356,7 +356,9 @@ typedef struct _VipsImageClass { } VipsImageClass; -GType vips_image_get_type( void ); +/* Don't put spaces around void here, it breaks gtk-doc. + */ +GType vips_image_get_type(void); /* Has to be guint64 and not size_t/off_t since we have to be able to address * huge images on platforms with 32-bit files. diff --git a/libvips/include/vips/interpolate.h b/libvips/include/vips/interpolate.h index a872fb89..930e400e 100644 --- a/libvips/include/vips/interpolate.h +++ b/libvips/include/vips/interpolate.h @@ -87,7 +87,9 @@ typedef struct _VipsInterpolateClass { int window_offset; } VipsInterpolateClass; -GType vips_interpolate_get_type( void ); +/* Don't put spaces around void here, it breaks gtk-doc. + */ +GType vips_interpolate_get_type(void); void vips_interpolate( VipsInterpolate *interpolate, void *out, VipsRegion *in, double x, double y ); VipsInterpolateMethod vips_interpolate_get_method( VipsInterpolate *interpolate ); diff --git a/libvips/include/vips/object.h b/libvips/include/vips/object.h index 53e16397..1da024fd 100644 --- a/libvips/include/vips/object.h +++ b/libvips/include/vips/object.h @@ -406,7 +406,7 @@ int vips_object_get_argument_priority( VipsObject *object, const char *name ); (G_TYPE_INSTANCE_GET_CLASS( (obj), VIPS_TYPE_OBJECT, VipsObjectClass )) struct _VipsObject { - GObject parent_object; + GObject parent_instance; /* Set after ->build() has run succesfully: construct is fully done * and checked. @@ -579,7 +579,9 @@ void vips_object_print_name( VipsObject *object ); gboolean vips_object_sanity( VipsObject *object ); -GType vips_object_get_type( void ); +/* Don't put spaces around void here, it breaks gtk-doc. + */ +GType vips_object_get_type(void); void vips_object_class_install_argument( VipsObjectClass *cls, GParamSpec *pspec, VipsArgumentFlags flags, diff --git a/libvips/include/vips/operation.h b/libvips/include/vips/operation.h index 3b732f6e..d76abdaa 100644 --- a/libvips/include/vips/operation.h +++ b/libvips/include/vips/operation.h @@ -92,7 +92,9 @@ typedef struct _VipsOperationClass { void (*invalidate)( VipsOperation *operation ); } VipsOperationClass; -GType vips_operation_get_type( void ); +/* Don't put spaces around void here, it breaks gtk-doc. + */ +GType vips_operation_get_type(void); VipsOperationFlags vips_operation_get_flags( VipsOperation *operation ); void vips_operation_class_print_usage( VipsOperationClass *operation_class ); diff --git a/libvips/include/vips/region.h b/libvips/include/vips/region.h index 5c9439d4..ce171a39 100644 --- a/libvips/include/vips/region.h +++ b/libvips/include/vips/region.h @@ -98,7 +98,9 @@ typedef struct _VipsRegionClass { } VipsRegionClass; -GType vips_region_get_type( void ); +/* Don't put spaces around void here, it breaks gtk-doc. + */ +GType vips_region_get_type(void); VipsRegion *vips_region_new( VipsImage *image ); diff --git a/libvips/include/vips/threadpool.h b/libvips/include/vips/threadpool.h index 2352bc44..d1e49ce4 100644 --- a/libvips/include/vips/threadpool.h +++ b/libvips/include/vips/threadpool.h @@ -97,7 +97,10 @@ typedef struct _VipsThreadStateClass { } VipsThreadStateClass; void *vips_thread_state_set( VipsObject *object, void *a, void *b ); -GType vips_thread_state_get_type( void ); + +/* Don't put spaces around void here, it breaks gtk-doc. + */ +GType vips_thread_state_get_type(void); VipsThreadState *vips_thread_state_new( VipsImage *im, void *a ); diff --git a/libvips/include/vips/type.h b/libvips/include/vips/type.h index 37ed6b3c..88b44c88 100644 --- a/libvips/include/vips/type.h +++ b/libvips/include/vips/type.h @@ -50,7 +50,7 @@ typedef struct _VipsThing { * The #GType for a #VipsThing. */ #define VIPS_TYPE_THING (vips_thing_get_type()) -GType vips_thing_get_type( void ); +GType vips_thing_get_type(void); VipsThing *vips_thing_new( int i ); /* A ref-counted area of memory. Can hold arrays of things as well. @@ -119,7 +119,7 @@ void *vips_area_get_data( VipsArea *area, */ #define VIPS_TYPE_AREA (vips_area_get_type()) #define VIPS_AREA( X ) ((VipsArea *) (X)) -GType vips_area_get_type( void ); +GType vips_area_get_type(void); /** * VIPS_TYPE_SAVE_STRING: @@ -127,7 +127,7 @@ GType vips_area_get_type( void ); * The #GType for a #VipsSaveString. */ #define VIPS_TYPE_SAVE_STRING (vips_save_string_get_type()) -GType vips_save_string_get_type( void ); +GType vips_save_string_get_type(void); /** * VIPS_TYPE_REF_STRING: @@ -142,7 +142,7 @@ typedef struct _VipsRefString { VipsRefString *vips_ref_string_new( const char *str ); const char *vips_ref_string_get( VipsRefString *refstr, size_t *length ); -GType vips_ref_string_get_type( void ); +GType vips_ref_string_get_type(void); /** * VIPS_TYPE_BLOB: @@ -159,7 +159,7 @@ VipsBlob *vips_blob_new( VipsCallbackFn free_fn, const void *data, size_t size ); VipsBlob *vips_blob_copy( const void *data, size_t size ); const void *vips_blob_get( VipsBlob *blob, size_t *size ); -GType vips_blob_get_type( void ); +GType vips_blob_get_type(void); /** * VIPS_TYPE_ARRAY_DOUBLE: @@ -175,7 +175,7 @@ typedef struct _VipsArrayDouble { VipsArrayDouble *vips_array_double_new( const double *array, int n ); VipsArrayDouble *vips_array_double_newv( int n, ... ); double *vips_array_double_get( VipsArrayDouble *array, int *n ); -GType vips_array_double_get_type( void ); +GType vips_array_double_get_type(void); /** * VIPS_TYPE_ARRAY_INT: @@ -191,7 +191,7 @@ typedef struct _VipsArrayInt { VipsArrayInt *vips_array_int_new( const int *array, int n ); VipsArrayInt *vips_array_int_newv( int n, ... ); int *vips_array_int_get( VipsArrayInt *array, int *n ); -GType vips_array_int_get_type( void ); +GType vips_array_int_get_type(void); /** * VIPS_TYPE_ARRAY_IMAGE: @@ -207,7 +207,7 @@ typedef struct _VipsArrayImage { /* See image.h for vips_array_image_new() etc., they need to be declared after * VipsImage. */ -GType vips_array_image_get_type( void ); +GType vips_array_image_get_type(void); void vips_value_set_area( GValue *value, VipsCallbackFn free_fn, void *data ); void *vips_value_get_area( const GValue *value, size_t *length ); From dac671439c99ca5d776c713792f4922a1cb736d0 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 19 Jul 2016 10:57:21 +0100 Subject: [PATCH 09/15] kind-of working --- ChangeLog | 1 + TODO | 2 + libvips/Makefile.am | 12 +- libvips/create/Makefile.am | 1 + libvips/create/create.c | 2 + libvips/create/worley.c | 383 ++++++++++++++++++++++++++++++++++ libvips/include/vips/create.h | 3 + 7 files changed, 399 insertions(+), 5 deletions(-) create mode 100644 libvips/create/worley.c diff --git a/ChangeLog b/ChangeLog index 3c0a33a7..55cfc0c3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -27,6 +27,7 @@ - better quality for vips_resize() with linear/cubic kernels - pyvips8 can create new metadata - better upsizing with vips_resize() +- added vips_worley(), generate worley noise 18/5/16 started 8.3.2 - more robust vips image reading diff --git a/TODO b/TODO index 4ead5e91..85383097 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ +- add perlin and worley noise generators + - add more webp tests to py suite - try moving some more of the CLI tests to py diff --git a/libvips/Makefile.am b/libvips/Makefile.am index b2178c5d..015ac722 100644 --- a/libvips/Makefile.am +++ b/libvips/Makefile.am @@ -98,12 +98,14 @@ Vips_8_0_gir_LIBS = libvips.la Vips_8_0_gir_FILES = $(introspection_sources) INTROSPECTION_GIRS += Vips-8.0.gir +# don't use +# --warn-all --verbose +# too annoying Vips_8_0_gir_SCANNERFLAGS = \ - --warn-all --verbose \ - --program=./introspect \ - --identifier-prefix=Vips \ - --identifier-prefix=vips \ - --symbol-prefix=vips + --program=./introspect \ + --identifier-prefix=Vips \ + --identifier-prefix=vips \ + --symbol-prefix=vips girdir = $(datadir)/gir-1.0 gir_DATA = $(INTROSPECTION_GIRS) diff --git a/libvips/create/Makefile.am b/libvips/create/Makefile.am index 7f27218e..e56a9566 100644 --- a/libvips/create/Makefile.am +++ b/libvips/create/Makefile.am @@ -1,6 +1,7 @@ noinst_LTLIBRARIES = libcreate.la libcreate_la_SOURCES = \ + worley.c \ create.c \ pcreate.h \ gaussmat.c \ diff --git a/libvips/create/create.c b/libvips/create/create.c index 77ef0ccc..cc2613a7 100644 --- a/libvips/create/create.c +++ b/libvips/create/create.c @@ -139,6 +139,7 @@ vips_create_operation_init( void ) extern GType vips_mask_ideal_band_get_type( void ); extern GType vips_mask_fractal_get_type( void ); extern GType vips_fractsurf_get_type( void ); + extern GType vips_worley_get_type( void ); vips_black_get_type(); vips_gaussmat_get_type(); @@ -167,5 +168,6 @@ vips_create_operation_init( void ) vips_mask_gaussian_band_get_type(); vips_mask_fractal_get_type(); vips_fractsurf_get_type(); + vips_worley_get_type(); } diff --git a/libvips/create/worley.c b/libvips/create/worley.c new file mode 100644 index 00000000..2360bba7 --- /dev/null +++ b/libvips/create/worley.c @@ -0,0 +1,383 @@ +/* Worley noise generator + * + * 13/6/13 + */ + +/* + + 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 + + */ + +/* +#define VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#include "pcreate.h" + +typedef struct _VipsWorley { + VipsCreate parent_instance; + + int width; + int height; + int cell_size; + + int cells_across; + int cells_down; + + /* Use this to seed this call of our rng. + */ + int seed; +} VipsWorley; + +typedef struct _VipsWorleyClass { + VipsCreateClass parent_class; + +} VipsWorleyClass; + +G_DEFINE_TYPE( VipsWorley, vips_worley, VIPS_TYPE_CREATE ); + +#define MAX_FEATURES (10) + +/* Round N down to P boundary. + */ +#define ROUND_DOWN( N, P ) ((N) - ((N) % P)) + +/* Round N up to P boundary. + */ +#define ROUND_UP( N, P ) (ROUND_DOWN( (N) + (P) - 1, (P) )) + +typedef struct _Cell { + /* Cell position, in number of cells. Scale by cell_size to get + * absolute image cods. + */ + int cell_x; + int cell_y; + + /* A cell contains 1 to n features. + */ + int n_features; + + /* Feature coordinates, in absolute image space. + */ + int feature_x[MAX_FEATURES]; + int feature_y[MAX_FEATURES]; +} Cell; + +typedef struct _Sequence { + VipsWorley *worley; + + /* The position of the last cell we were in. Use this to avoid + * regenerating cells on every pixel lookup. + */ + int cell_x; + int cell_y; + + /* The 3 x 3 grid of cells around the current point. + */ + Cell cells[9]; + +} Sequence; + +/* A very simple random number generator. + */ +static int +vips_worley_random( int previous ) +{ + return( ((guint64) 1103515245 * previous + 12345) & 0xffffff ); +} + +/* Generate a 3 x 3 grid of cells around a point. + */ +static void +vips_worley_create_cells( VipsWorley *worley, + Cell cells[9], int cell_x, int cell_y ) +{ + int x, y; + + for( y = 0; y < 3; y++ ) + for( x = 0; x < 3; x++ ) { + Cell *cell = &cells[x + y * 3]; + + int seed; + int j; + + /* We wrap cells around, so that out output will + * tesselate. + */ + cell->cell_x = cell_x + x - 1; + if( cell->cell_x >= worley->cells_across ) + cell->cell_x = 0; + if( cell->cell_x < 0 ) + cell->cell_x = worley->cells_across - 1; + cell->cell_y = cell_y + y - 1; + if( cell->cell_y >= worley->cells_down ) + cell->cell_y = 0; + if( cell->cell_y < 0 ) + cell->cell_y = worley->cells_down - 1; + + /* Seed our rng. + */ + seed = worley->seed + + (cell->cell_x << 16) + cell->cell_y; + + /* [1, MAX_FEATURES) + */ + seed = vips_worley_random( seed ); + cell->n_features = (seed % (MAX_FEATURES - 1)) + 1; + + for( j = 0; j < cell->n_features; j++ ) { + seed = vips_worley_random( seed ); + cell->feature_x[j] = + cell->cell_y * worley->cell_size + + seed % worley->cell_size; + seed = vips_worley_random( seed ); + cell->feature_y[j] = + cell->cell_y * worley->cell_size + + seed % worley->cell_size; + } + } +} + +static int +vips_worley_stop( void *vseq, void *a, void *b ) +{ + Sequence *seq = (Sequence *) vseq; + + VIPS_FREE( seq ); + + return( 0 ); +} + +static void * +vips_worley_start( VipsImage *out, void *a, void *b ) +{ + VipsWorley *worley = (VipsWorley *) b; + + Sequence *seq; + + if( !(seq = VIPS_NEW( NULL, Sequence )) ) + return( NULL ); + + seq->worley = worley; + seq->cell_x = -1; + seq->cell_y = -1; + + return( seq ); +} + +static int +vips_hypot( int x, int y ) +{ + int result; + +#ifdef HAVE_HYPOT + result = hypot( x, y ); +#else + result = sqrt( x * x + y * y ); +#endif + + return( result ); +} + +static int +vips_worley_distance( VipsWorley *worley, Cell cells[9], int x, int y ) +{ + int distance; + + int i, j; + + distance = worley->cell_size * 1.5; + + for( i = 0; i < 9; i++ ) { + Cell *cell = &cells[i]; + + for( j = 0; j < cell->n_features; j++ ) { + int d = vips_hypot( + x - cell->feature_x[j], + y - cell->feature_y[j] ); + + distance = VIPS_MIN( d, distance ); + } + } + + return( distance ); +} + +static int +vips_worley_gen( VipsRegion *or, void *vseq, void *a, void *b, + gboolean *stop ) +{ + VipsWorley *worley = (VipsWorley *) a; + VipsRect *r = &or->valid; + Sequence *seq = (Sequence *) vseq; + + int x, y; + + for( y = 0; y < r->height; y++ ) { + int *q = (int *) VIPS_REGION_ADDR( or, r->left, r->top + y ); + + for( x = 0; x < r->width; x++ ) { + int cell_x = (r->left + x) % worley->cell_size; + int cell_y = (r->top + y) % worley->cell_size; + + if( cell_x != seq->cell_x || + cell_y != seq->cell_y ) { + vips_worley_create_cells( worley, + seq->cells, cell_x, cell_y ); + seq->cell_x = cell_x; + seq->cell_y = cell_y; + } + + q[x] = vips_worley_distance( worley, seq->cells, + r->left + x, r->top + y ); + } + } + + return( 0 ); +} + +static int +vips_worley_build( VipsObject *object ) +{ + VipsCreate *create = VIPS_CREATE( object ); + VipsWorley *worley = (VipsWorley *) object; + + if( VIPS_OBJECT_CLASS( vips_worley_parent_class )->build( object ) ) + return( -1 ); + + /* Be careful if width is a multiple of cell_size. + */ + worley->cells_across = ROUND_UP( worley->width, worley->cell_size ) / + worley->cell_size; + worley->cells_down = ROUND_UP( worley->height, worley->cell_size ) / + worley->cell_size; + + worley->seed = g_random_double() * 0xffffff; + + vips_image_init_fields( create->out, + worley->width, worley->height, 1, + VIPS_FORMAT_INT, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, + 1.0, 1.0 ); + vips_image_pipelinev( create->out, + VIPS_DEMAND_STYLE_ANY, NULL ); + if( vips_image_generate( create->out, + vips_worley_start, vips_worley_gen, vips_worley_stop, + worley, NULL ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_worley_class_init( VipsWorleyClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "worley"; + vobject_class->description = _( "make a worley image" ); + vobject_class->build = vips_worley_build; + + VIPS_ARG_INT( class, "width", 2, + _( "Width" ), + _( "Image width in pixels" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsWorley, width ), + 1, VIPS_MAX_COORD, 1 ); + + VIPS_ARG_INT( class, "height", 3, + _( "Height" ), + _( "Image height in pixels" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsWorley, height ), + 1, VIPS_MAX_COORD, 1 ); + + VIPS_ARG_INT( class, "cell_size", 3, + _( "Cell size" ), + _( "Size of Worley cells" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsWorley, cell_size ), + 1, VIPS_MAX_COORD, 256 ); + +} + +static void +vips_worley_init( VipsWorley *worley ) +{ + worley->cell_size = 256; +} + +/** + * vips_worley: + * @out: output image + * @width: horizontal size + * @height: vertical size + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * * @cell_size: %gint, size of Worley cells + * + * Create a one-band int image of Worley noise. See: + * + * https://en.wikipedia.org/wiki/Worley_noise + * + * Use @cell_size to set the size of the cells from which the image is + * constructed. The default is 256 x 256. + * + * If @width and @height are mutiples of @cell_size, the image will tessellate. + * + * See also: vips_gaussnoise(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_worley( VipsImage **out, int width, int height, ... ) +{ + va_list ap; + int result; + + va_start( ap, height ); + result = vips_call_split( "worley", ap, out, width, height ); + va_end( ap ); + + return( result ); +} + diff --git a/libvips/include/vips/create.h b/libvips/include/vips/create.h index b7f96c4f..8567fb82 100644 --- a/libvips/include/vips/create.h +++ b/libvips/include/vips/create.h @@ -114,6 +114,9 @@ int vips_fractsurf( VipsImage **out, int width, int height, double fractal_dimension, ... ) __attribute__((sentinel)); +int vips_worley( VipsImage **out, int width, int height, ... ) + __attribute__((sentinel)); + #ifdef __cplusplus } #endif /*__cplusplus*/ From e14c97359ab03142a7a2b7404e3c412ea2cf7db4 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 19 Jul 2016 11:53:56 +0100 Subject: [PATCH 10/15] done! perlin next I guess --- libvips/create/worley.c | 60 ++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/libvips/create/worley.c b/libvips/create/worley.c index 2360bba7..8d364001 100644 --- a/libvips/create/worley.c +++ b/libvips/create/worley.c @@ -1,6 +1,6 @@ -/* Worley noise generator +/* Worley noise generator. * - * 13/6/13 + * 19/7/16 */ /* @@ -120,7 +120,7 @@ vips_worley_random( int previous ) return( ((guint64) 1103515245 * previous + 12345) & 0xffffff ); } -/* Generate a 3 x 3 grid of cells around a point. +/* Generate a 3 x 3 grid of cells around a point. */ static void vips_worley_create_cells( VipsWorley *worley, @@ -135,24 +135,29 @@ vips_worley_create_cells( VipsWorley *worley, int seed; int j; - /* We wrap cells around, so that out output will - * tesselate. + /* Can go <0 and >width for edges. */ cell->cell_x = cell_x + x - 1; - if( cell->cell_x >= worley->cells_across ) - cell->cell_x = 0; - if( cell->cell_x < 0 ) - cell->cell_x = worley->cells_across - 1; cell->cell_y = cell_y + y - 1; - if( cell->cell_y >= worley->cells_down ) - cell->cell_y = 0; - if( cell->cell_y < 0 ) - cell->cell_y = worley->cells_down - 1; - /* Seed our rng. + seed = worley->seed; + + /* When we calculate the seed for this cell, we wrap + * around so that our output will tesselate. */ - seed = worley->seed + - (cell->cell_x << 16) + cell->cell_y; + if( cell->cell_x >= worley->cells_across ) + seed ^= 0; + else if( cell->cell_x < 0 ) + seed ^= worley->cells_across - 1; + else + seed ^= cell->cell_x; + + if( cell->cell_y >= worley->cells_down ) + seed ^= 0; + else if( cell->cell_y < 0 ) + seed ^= worley->cells_down - 1; + else + seed ^= cell->cell_y; /* [1, MAX_FEATURES) */ @@ -162,8 +167,9 @@ vips_worley_create_cells( VipsWorley *worley, for( j = 0; j < cell->n_features; j++ ) { seed = vips_worley_random( seed ); cell->feature_x[j] = - cell->cell_y * worley->cell_size + + cell->cell_x * worley->cell_size + seed % worley->cell_size; + seed = vips_worley_random( seed ); cell->feature_y[j] = cell->cell_y * worley->cell_size + @@ -202,15 +208,9 @@ vips_worley_start( VipsImage *out, void *a, void *b ) static int vips_hypot( int x, int y ) { - int result; - -#ifdef HAVE_HYPOT - result = hypot( x, y ); -#else - result = sqrt( x * x + y * y ); -#endif - - return( result ); + /* Faster than hypot() for int args. + */ + return( sqrt( x * x + y * y ) ); } static int @@ -230,7 +230,7 @@ vips_worley_distance( VipsWorley *worley, Cell cells[9], int x, int y ) x - cell->feature_x[j], y - cell->feature_y[j] ); - distance = VIPS_MIN( d, distance ); + distance = VIPS_MIN( distance, d ); } } @@ -251,8 +251,8 @@ vips_worley_gen( VipsRegion *or, void *vseq, void *a, void *b, int *q = (int *) VIPS_REGION_ADDR( or, r->left, r->top + y ); for( x = 0; x < r->width; x++ ) { - int cell_x = (r->left + x) % worley->cell_size; - int cell_y = (r->top + y) % worley->cell_size; + int cell_x = (r->left + x) / worley->cell_size; + int cell_y = (r->top + y) / worley->cell_size; if( cell_x != seq->cell_x || cell_y != seq->cell_y ) { @@ -362,7 +362,7 @@ vips_worley_init( VipsWorley *worley ) * Use @cell_size to set the size of the cells from which the image is * constructed. The default is 256 x 256. * - * If @width and @height are mutiples of @cell_size, the image will tessellate. + * If @width and @height are multiples of @cell_size, the image will tessellate. * * See also: vips_gaussnoise(). * From 99766dbd830b7d9099d60cb2ed3d90c3c8306784 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 19 Jul 2016 12:29:36 +0100 Subject: [PATCH 11/15] better hash for worley --- libvips/create/worley.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/libvips/create/worley.c b/libvips/create/worley.c index 8d364001..2187a7be 100644 --- a/libvips/create/worley.c +++ b/libvips/create/worley.c @@ -60,7 +60,7 @@ typedef struct _VipsWorley { /* Use this to seed this call of our rng. */ - int seed; + guint32 seed; } VipsWorley; typedef struct _VipsWorleyClass { @@ -112,12 +112,19 @@ typedef struct _Sequence { } Sequence; -/* A very simple random number generator. +/* A very simple random number generator. See: + * http://isthe.com/chongo/tech/comp/fnv/#FNV-source */ -static int -vips_worley_random( int previous ) +static guint32 +vips_worley_random( guint32 seed ) { - return( ((guint64) 1103515245 * previous + 12345) & 0xffffff ); + return( 1103515245u * seed + 12345 ); +} + +static guint32 +vips_worley_seed_add( guint32 seed, int value ) +{ + return( ((2166136261u ^ seed) * 16777619u) ^ value ); } /* Generate a 3 x 3 grid of cells around a point. @@ -132,7 +139,8 @@ vips_worley_create_cells( VipsWorley *worley, for( x = 0; x < 3; x++ ) { Cell *cell = &cells[x + y * 3]; - int seed; + guint32 seed; + int value; int j; /* Can go <0 and >width for edges. @@ -146,18 +154,20 @@ vips_worley_create_cells( VipsWorley *worley, * around so that our output will tesselate. */ if( cell->cell_x >= worley->cells_across ) - seed ^= 0; + value = 0; else if( cell->cell_x < 0 ) - seed ^= worley->cells_across - 1; + value = worley->cells_across - 1; else - seed ^= cell->cell_x; + value = cell->cell_x; + seed = vips_worley_seed_add( seed, value ); if( cell->cell_y >= worley->cells_down ) - seed ^= 0; + value = 0; else if( cell->cell_y < 0 ) - seed ^= worley->cells_down - 1; + value = worley->cells_down - 1; else - seed ^= cell->cell_y; + value = cell->cell_y; + seed = vips_worley_seed_add( seed, value ); /* [1, MAX_FEATURES) */ @@ -286,7 +296,7 @@ vips_worley_build( VipsObject *object ) worley->cells_down = ROUND_UP( worley->height, worley->cell_size ) / worley->cell_size; - worley->seed = g_random_double() * 0xffffff; + worley->seed = g_random_double() * 0xffffffffu; vips_image_init_fields( create->out, worley->width, worley->height, 1, From 3efee94e19cdcb5c3b7a3ec399f8e10408bfb3c7 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 21 Jul 2016 07:40:33 +0100 Subject: [PATCH 12/15] fix possible out of bounds read in tiff2vips reading a malformed tiff file from a buffer could trigger out of bounds read thanks Matt Richards --- ChangeLog | 1 + libvips/foreign/tiff2vips.c | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 600f20f9..d7981499 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ 18/5/16 started 8.3.2 - more robust vips image reading +- more robust tiff read [Matt Richards] 15/4/16 started 8.3.1 - rename vips wrapper script, it was still vips-8.2, thanks Benjamin diff --git a/libvips/foreign/tiff2vips.c b/libvips/foreign/tiff2vips.c index 1c58bd61..2c2bf39c 100644 --- a/libvips/foreign/tiff2vips.c +++ b/libvips/foreign/tiff2vips.c @@ -1832,9 +1832,17 @@ my_tiff_read( thandle_t st, tdata_t buffer, tsize_t size ) { ReadTiff *rtiff = (ReadTiff *) st; - size_t available = rtiff->len - rtiff->pos; - size_t copy = VIPS_MIN( size, available ); + size_t available; + size_t copy; + if( rtiff->pos > rtiff->len ) { + vips_error( "tiff2vips", + "%s", _( "read beyond end of buffer" ) ); + return( 0 ); + } + + available = rtiff->len - rtiff->pos; + copy = VIPS_MIN( size, available ); memcpy( buffer, (unsigned char *) rtiff->buf + rtiff->pos, copy ); rtiff->pos += copy; @@ -1855,6 +1863,9 @@ my_tiff_close( thandle_t st ) return 0; } +/* After calling this, ->pos is not bound by the size of the buffer, it can + * have any positive value. + */ static toff_t my_tiff_seek( thandle_t st, toff_t pos, int whence ) { From c469bb398394bc98edede29c4fb2168a2733cf46 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 24 Jul 2016 11:46:42 +0100 Subject: [PATCH 13/15] perlin sort-of works --- ChangeLog | 3 +- TODO | 4 +- libvips/create/Makefile.am | 1 + libvips/create/create.c | 2 + libvips/create/perlin.c | 360 ++++++++++++++++++++++++++++++++++ libvips/create/worley.c | 4 +- libvips/include/vips/create.h | 2 + 7 files changed, 372 insertions(+), 4 deletions(-) create mode 100644 libvips/create/perlin.c diff --git a/ChangeLog b/ChangeLog index 55cfc0c3..86a9c497 100644 --- a/ChangeLog +++ b/ChangeLog @@ -27,7 +27,8 @@ - better quality for vips_resize() with linear/cubic kernels - pyvips8 can create new metadata - better upsizing with vips_resize() -- added vips_worley(), generate worley noise +- added vips_worley(), generate Worley noise +- added vips_perlin(), generate Perlin noise 18/5/16 started 8.3.2 - more robust vips image reading diff --git a/TODO b/TODO index 85383097..eed2e9f1 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,6 @@ -- add perlin and worley noise generators +- need better interpolation in the perlin generator? + +- add tests for perlin and worley noise generators - add more webp tests to py suite diff --git a/libvips/create/Makefile.am b/libvips/create/Makefile.am index e56a9566..8b5c11e4 100644 --- a/libvips/create/Makefile.am +++ b/libvips/create/Makefile.am @@ -1,6 +1,7 @@ noinst_LTLIBRARIES = libcreate.la libcreate_la_SOURCES = \ + perlin.c \ worley.c \ create.c \ pcreate.h \ diff --git a/libvips/create/create.c b/libvips/create/create.c index cc2613a7..93c35796 100644 --- a/libvips/create/create.c +++ b/libvips/create/create.c @@ -140,6 +140,7 @@ vips_create_operation_init( void ) extern GType vips_mask_fractal_get_type( void ); extern GType vips_fractsurf_get_type( void ); extern GType vips_worley_get_type( void ); + extern GType vips_perlin_get_type( void ); vips_black_get_type(); vips_gaussmat_get_type(); @@ -169,5 +170,6 @@ vips_create_operation_init( void ) vips_mask_fractal_get_type(); vips_fractsurf_get_type(); vips_worley_get_type(); + vips_perlin_get_type(); } diff --git a/libvips/create/perlin.c b/libvips/create/perlin.c new file mode 100644 index 00000000..fdb9ae57 --- /dev/null +++ b/libvips/create/perlin.c @@ -0,0 +1,360 @@ +/* Perlin noise generator. + * + * 24/7/16 + */ + +/* + + 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 + + */ + +/* +#define VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#include "pcreate.h" + +typedef struct _VipsPerlin { + VipsCreate parent_instance; + + int width; + int height; + int cell_size; + + int cells_across; + int cells_down; + + /* Use this to seed this call of our rng. + */ + guint32 seed; +} VipsPerlin; + +typedef struct _VipsPerlinClass { + VipsCreateClass parent_class; + +} VipsPerlinClass; + +G_DEFINE_TYPE( VipsPerlin, vips_perlin, VIPS_TYPE_CREATE ); + +/* Round N down to P boundary. + */ +#define ROUND_DOWN( N, P ) ((N) - ((N) % P)) + +/* Round N up to P boundary. + */ +#define ROUND_UP( N, P ) (ROUND_DOWN( (N) + (P) - 1, (P) )) + +/* cos and sin from an angle in 0 - 255. + */ +float vips_perlin_cos[256]; +float vips_perlin_sin[256]; + +typedef struct _Sequence { + VipsPerlin *perlin; + + /* The position of the last cell we were in. Use this to avoid + * regenerating vectors on every pixel lookup. + */ + int cell_x; + int cell_y; + + /* The 2 x 2 grid of unit vectors, with cell_x/cell_y as the top left. + */ + float gx[4]; + float gy[4]; + +} Sequence; + +/* A very simple random number generator. See: + * http://isthe.com/chongo/tech/comp/fnv/#FNV-source + */ +static guint32 +vips_perlin_random( guint32 seed ) +{ + return( 1103515245u * seed + 12345 ); +} + +static guint32 +vips_perlin_seed_add( guint32 seed, int value ) +{ + return( ((2166136261u ^ seed) * 16777619u) ^ value ); +} + +/* Generate a 3 x 3 grid of cells around a point. + */ +static void +vips_perlin_create_cells( VipsPerlin *perlin, + float gx[4], float gy[4], int cell_x, int cell_y ) +{ + int x, y; + + for( y = 0; y < 2; y++ ) + for( x = 0; x < 2; x++ ) { + int ci = x + y * 2; + + guint32 seed; + int cx; + int cy; + int angle; + + seed = perlin->seed; + + cx = cell_x + x; + cy = cell_y + y; + + /* When we calculate the seed for this cell, we wrap + * around so that our output will tesselate. + */ + if( cx >= perlin->cells_across ) + cx = 0; + seed = vips_perlin_seed_add( seed, cx ); + + if( cy >= perlin->cells_down ) + cy = 0; + seed = vips_perlin_seed_add( seed, cy ); + + seed = vips_perlin_random( seed ); + angle = seed & 0xff; + + gx[ci] = vips_perlin_cos[angle]; + gy[ci] = vips_perlin_sin[angle]; + } +} + +static int +vips_perlin_stop( void *vseq, void *a, void *b ) +{ + Sequence *seq = (Sequence *) vseq; + + VIPS_FREE( seq ); + + return( 0 ); +} + +static void * +vips_perlin_start( VipsImage *out, void *a, void *b ) +{ + VipsPerlin *perlin = (VipsPerlin *) b; + + Sequence *seq; + + if( !(seq = VIPS_NEW( NULL, Sequence )) ) + return( NULL ); + + seq->perlin = perlin; + seq->cell_x = -1; + seq->cell_y = -1; + + return( seq ); +} + +static int +vips_perlin_gen( VipsRegion *or, void *vseq, void *a, void *b, + gboolean *stop ) +{ + VipsPerlin *perlin = (VipsPerlin *) a; + VipsRect *r = &or->valid; + Sequence *seq = (Sequence *) vseq; + + int x, y; + + for( y = 0; y < r->height; y++ ) { + float *q = (float *) VIPS_REGION_ADDR( or, r->left, r->top + y ); + + for( x = 0; x < r->width; x++ ) { + int cs = perlin->cell_size; + int cell_x = (r->left + x) / cs; + int cell_y = (r->top + y) / cs; + float dx = (x + r->left - cell_x * cs) / (float) cs; + float dy = (y + r->top - cell_y * cs) / (float) cs; + + float n0, n1; + float ix0, ix1; + + if( cell_x != seq->cell_x || + cell_y != seq->cell_y ) { + vips_perlin_create_cells( perlin, + seq->gx, seq->gy, cell_x, cell_y ); + seq->cell_x = cell_x; + seq->cell_y = cell_y; + } + + n0 = -dx * seq->gx[0] + -dy * seq->gy[0]; + n1 = (1 - dx) * seq->gx[1] + -dy * seq->gy[1]; + ix0 = (1 - dx) * n0 + dx * n1; + + n0 = -dx * seq->gx[2] + (1 - dy) * seq->gy[2]; + n1 = (1 - dx) * seq->gx[3] + (1 - dy) * seq->gy[3]; + ix1 = (1 - dx) * n0 + dx * n1; + + q[x] = (1 - dy) * ix0 + dy * ix1; + } + } + + return( 0 ); +} + +static int +vips_perlin_build( VipsObject *object ) +{ + VipsCreate *create = VIPS_CREATE( object ); + VipsPerlin *perlin = (VipsPerlin *) object; + + if( VIPS_OBJECT_CLASS( vips_perlin_parent_class )->build( object ) ) + return( -1 ); + + /* Be careful if width is a multiple of cell_size. + */ + perlin->cells_across = ROUND_UP( perlin->width, perlin->cell_size ) / + perlin->cell_size; + perlin->cells_down = ROUND_UP( perlin->height, perlin->cell_size ) / + perlin->cell_size; + + perlin->seed = g_random_double() * 0xffffffffu; + + vips_image_init_fields( create->out, + perlin->width, perlin->height, 1, + VIPS_FORMAT_FLOAT, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, + 1.0, 1.0 ); + vips_image_pipelinev( create->out, + VIPS_DEMAND_STYLE_ANY, NULL ); + if( vips_image_generate( create->out, + vips_perlin_start, vips_perlin_gen, vips_perlin_stop, + perlin, NULL ) ) + return( -1 ); + + return( 0 ); +} + +static void * +vips_perlin_make_tables( void *client ) +{ + int i; + + for( i = 0; i < 256; i++ ) { + double angle = 2 * M_PI * i / 256.0; + + vips_perlin_cos[i] = cos( angle ); + vips_perlin_sin[i] = sin( angle ); + } + + return( NULL ); +} + +static void +vips_perlin_class_init( VipsPerlinClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + + static GOnce once = G_ONCE_INIT; + + (void) g_once( &once, vips_perlin_make_tables, NULL ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "perlin"; + vobject_class->description = _( "make a perlin noise image" ); + vobject_class->build = vips_perlin_build; + + VIPS_ARG_INT( class, "width", 2, + _( "Width" ), + _( "Image width in pixels" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsPerlin, width ), + 1, VIPS_MAX_COORD, 1 ); + + VIPS_ARG_INT( class, "height", 3, + _( "Height" ), + _( "Image height in pixels" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsPerlin, height ), + 1, VIPS_MAX_COORD, 1 ); + + VIPS_ARG_INT( class, "cell_size", 3, + _( "Cell size" ), + _( "Size of Perlin cells" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsPerlin, cell_size ), + 1, VIPS_MAX_COORD, 256 ); + +} + +static void +vips_perlin_init( VipsPerlin *perlin ) +{ + perlin->cell_size = 256; +} + +/** + * vips_perlin: + * @out: output image + * @width: horizontal size + * @height: vertical size + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * * @cell_size: %gint, size of Perlin cells + * + * Create a one-band float image of Perlin noise. See: + * + * https://en.wikipedia.org/wiki/Perlin_noise + * + * Use @cell_size to set the size of the cells from which the image is + * constructed. The default is 256 x 256. + * + * If @width and @height are multiples of @cell_size, the image will tessellate. + * + * See also: vips_worley(), vips_fractsurf(), vips_gaussnoise(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_perlin( VipsImage **out, int width, int height, ... ) +{ + va_list ap; + int result; + + va_start( ap, height ); + result = vips_call_split( "perlin", ap, out, width, height ); + va_end( ap ); + + return( result ); +} + diff --git a/libvips/create/worley.c b/libvips/create/worley.c index 2187a7be..dc509731 100644 --- a/libvips/create/worley.c +++ b/libvips/create/worley.c @@ -322,7 +322,7 @@ vips_worley_class_init( VipsWorleyClass *class ) gobject_class->get_property = vips_object_get_property; vobject_class->nickname = "worley"; - vobject_class->description = _( "make a worley image" ); + vobject_class->description = _( "make a worley noise image" ); vobject_class->build = vips_worley_build; VIPS_ARG_INT( class, "width", 2, @@ -374,7 +374,7 @@ vips_worley_init( VipsWorley *worley ) * * If @width and @height are multiples of @cell_size, the image will tessellate. * - * See also: vips_gaussnoise(). + * See also: vips_perlin(), vips_fractsurf(), vips_gaussnoise(). * * Returns: 0 on success, -1 on error */ diff --git a/libvips/include/vips/create.h b/libvips/include/vips/create.h index 8567fb82..f04752d4 100644 --- a/libvips/include/vips/create.h +++ b/libvips/include/vips/create.h @@ -116,6 +116,8 @@ int vips_fractsurf( VipsImage **out, int vips_worley( VipsImage **out, int width, int height, ... ) __attribute__((sentinel)); +int vips_perlin( VipsImage **out, int width, int height, ... ) + __attribute__((sentinel)); #ifdef __cplusplus } From 6db9a2fdf9e7c11d13349322dbb8c59051794bbe Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 24 Jul 2016 12:58:45 +0100 Subject: [PATCH 14/15] better smoothing for perlin --- TODO | 2 -- libvips/create/perlin.c | 43 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/TODO b/TODO index eed2e9f1..a173d5b7 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,3 @@ -- need better interpolation in the perlin generator? - - add tests for perlin and worley noise generators - add more webp tests to py suite diff --git a/libvips/create/perlin.c b/libvips/create/perlin.c index fdb9ae57..d01e3a79 100644 --- a/libvips/create/perlin.c +++ b/libvips/create/perlin.c @@ -54,6 +54,7 @@ typedef struct _VipsPerlin { int width; int height; int cell_size; + gboolean uchar; int cells_across; int cells_down; @@ -182,6 +183,16 @@ vips_perlin_start( VipsImage *out, void *a, void *b ) return( seq ); } +/* Smooth linear interpolation, 0 <= x <= 1. + * + * https://en.wikipedia.org/wiki/Smoothstep + */ +static float +smootherstep( float x ) +{ + return( x * x * x * (x * (x * 6 - 15) + 10) ); +} + static int vips_perlin_gen( VipsRegion *or, void *vseq, void *a, void *b, gboolean *stop ) @@ -193,7 +204,9 @@ vips_perlin_gen( VipsRegion *or, void *vseq, void *a, void *b, int x, y; for( y = 0; y < r->height; y++ ) { - float *q = (float *) VIPS_REGION_ADDR( or, r->left, r->top + y ); + float *fq = (float *) + VIPS_REGION_ADDR( or, r->left, r->top + y ); + VipsPel *q = (VipsPel *) fq; for( x = 0; x < r->width; x++ ) { int cs = perlin->cell_size; @@ -201,9 +214,12 @@ vips_perlin_gen( VipsRegion *or, void *vseq, void *a, void *b, int cell_y = (r->top + y) / cs; float dx = (x + r->left - cell_x * cs) / (float) cs; float dy = (y + r->top - cell_y * cs) / (float) cs; + float sx = smootherstep( dx ); + float sy = smootherstep( dy ); float n0, n1; float ix0, ix1; + float p; if( cell_x != seq->cell_x || cell_y != seq->cell_y ) { @@ -215,13 +231,18 @@ vips_perlin_gen( VipsRegion *or, void *vseq, void *a, void *b, n0 = -dx * seq->gx[0] + -dy * seq->gy[0]; n1 = (1 - dx) * seq->gx[1] + -dy * seq->gy[1]; - ix0 = (1 - dx) * n0 + dx * n1; + ix0 = n0 + sx * (n1 - n0); n0 = -dx * seq->gx[2] + (1 - dy) * seq->gy[2]; n1 = (1 - dx) * seq->gx[3] + (1 - dy) * seq->gy[3]; - ix1 = (1 - dx) * n0 + dx * n1; + ix1 = n0 + sx * (n1 - n0); - q[x] = (1 - dy) * ix0 + dy * ix1; + p = ix0 + sy * (ix1 - ix0); + + if( perlin->uchar ) + q[x] = 128 * p + 128; + else + fq[x] = p; } } @@ -248,7 +269,8 @@ vips_perlin_build( VipsObject *object ) vips_image_init_fields( create->out, perlin->width, perlin->height, 1, - VIPS_FORMAT_FLOAT, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, + perlin->uchar ? VIPS_FORMAT_UCHAR : VIPS_FORMAT_FLOAT, + VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); @@ -313,6 +335,13 @@ vips_perlin_class_init( VipsPerlinClass *class ) G_STRUCT_OFFSET( VipsPerlin, cell_size ), 1, VIPS_MAX_COORD, 256 ); + VIPS_ARG_BOOL( class, "uchar", 4, + _( "Uchar" ), + _( "Output an unsigned char image" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsPerlin, uchar ), + FALSE ); + } static void @@ -331,6 +360,7 @@ vips_perlin_init( VipsPerlin *perlin ) * Optional arguments: * * * @cell_size: %gint, size of Perlin cells + * * @uchar: output a uchar image * * Create a one-band float image of Perlin noise. See: * @@ -341,6 +371,9 @@ vips_perlin_init( VipsPerlin *perlin ) * * If @width and @height are multiples of @cell_size, the image will tessellate. * + * Normally, output pixels are #VIPS_FORMAT_FLOAT in the range [-1, +1]. Set + * @uchar to output a uchar image with pixels in [0, 255]. + * * See also: vips_worley(), vips_fractsurf(), vips_gaussnoise(). * * Returns: 0 on success, -1 on error From 2dc43198e49e36fa62800eda9acf3f1f4e91624b Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 24 Jul 2016 14:47:22 +0100 Subject: [PATCH 15/15] final fixups --- TODO | 2 - cplusplus/gen-operators.py | 2 + cplusplus/include/vips/gen-operators-h.py | 2 + cplusplus/include/vips/vips-operators.h | 18 ++-- cplusplus/vips-operators.cpp | 126 +++++++++++++--------- libvips/create/perlin.c | 9 +- python/find_class_methods.py | 2 + python/packages/gi/overrides/Vips.py | 2 + test/test_create.py | 14 +++ 9 files changed, 109 insertions(+), 68 deletions(-) diff --git a/TODO b/TODO index a173d5b7..4ead5e91 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,3 @@ -- add tests for perlin and worley noise generators - - add more webp tests to py suite - try moving some more of the CLI tests to py diff --git a/cplusplus/gen-operators.py b/cplusplus/gen-operators.py index c64d1bf3..9be011fb 100755 --- a/cplusplus/gen-operators.py +++ b/cplusplus/gen-operators.py @@ -22,6 +22,8 @@ import re import logging #logging.basicConfig(level = logging.DEBUG) +import gi +gi.require_version('Vips', '8.0') from gi.repository import Vips, GObject vips_type_image = GObject.GType.from_name("VipsImage") diff --git a/cplusplus/include/vips/gen-operators-h.py b/cplusplus/include/vips/gen-operators-h.py index 6c76c257..aa0a96d0 100755 --- a/cplusplus/include/vips/gen-operators-h.py +++ b/cplusplus/include/vips/gen-operators-h.py @@ -12,6 +12,8 @@ import re import logging #logging.basicConfig(level = logging.DEBUG) +import gi +gi.require_version('Vips', '8.0') from gi.repository import Vips, GObject vips_type_image = GObject.GType.from_name("VipsImage") diff --git a/cplusplus/include/vips/vips-operators.h b/cplusplus/include/vips/vips-operators.h index 92b30b1f..620465a8 100644 --- a/cplusplus/include/vips/vips-operators.h +++ b/cplusplus/include/vips/vips-operators.h @@ -1,7 +1,3 @@ -// headers for vips operations -// Fri Feb 12 20:04:03 GMT 2016 -// this file is generated automatically, do not edit! - static void system( char * cmd_format , VOption *options = 0 ); VImage add( VImage right , VOption *options = 0 ); VImage subtract( VImage right , VOption *options = 0 ); @@ -103,13 +99,15 @@ VImage invertlut( VOption *options = 0 ); static VImage tonelut( VOption *options = 0 ); static VImage identity( VOption *options = 0 ); static VImage fractsurf( int width , int height , double fractal_dimension , VOption *options = 0 ); -static VImage radload( char * filename , VOption *options = 0 ); -static VImage ppmload( char * filename , VOption *options = 0 ); +static VImage worley( int width , int height , VOption *options = 0 ); +static VImage perlin( int width , int height , VOption *options = 0 ); static VImage csvload( char * filename , VOption *options = 0 ); static VImage matrixload( char * filename , VOption *options = 0 ); -static VImage analyzeload( char * filename , VOption *options = 0 ); static VImage rawload( char * filename , int width , int height , int bands , VOption *options = 0 ); static VImage vipsload( char * filename , VOption *options = 0 ); +static VImage analyzeload( char * filename , VOption *options = 0 ); +static VImage ppmload( char * filename , VOption *options = 0 ); +static VImage radload( char * filename , VOption *options = 0 ); static VImage pdfload( char * filename , VOption *options = 0 ); static VImage pdfload_buffer( VipsBlob * buffer , VOption *options = 0 ); static VImage svgload( char * filename , VOption *options = 0 ); @@ -130,15 +128,15 @@ static VImage magickload( char * filename , VOption *options = 0 ); static VImage magickload_buffer( VipsBlob * buffer , VOption *options = 0 ); static VImage fitsload( char * filename , VOption *options = 0 ); static VImage openexrload( char * filename , VOption *options = 0 ); -void radsave( char * filename , VOption *options = 0 ); -VipsBlob * radsave_buffer( VOption *options = 0 ); -void ppmsave( char * filename , VOption *options = 0 ); void csvsave( char * filename , VOption *options = 0 ); void matrixsave( char * filename , VOption *options = 0 ); void matrixprint( VOption *options = 0 ); void rawsave( char * filename , VOption *options = 0 ); void rawsave_fd( int fd , VOption *options = 0 ); void vipssave( char * filename , VOption *options = 0 ); +void ppmsave( char * filename , VOption *options = 0 ); +void radsave( char * filename , VOption *options = 0 ); +VipsBlob * radsave_buffer( VOption *options = 0 ); void dzsave( char * filename , VOption *options = 0 ); void pngsave( char * filename , VOption *options = 0 ); VipsBlob * pngsave_buffer( VOption *options = 0 ); diff --git a/cplusplus/vips-operators.cpp b/cplusplus/vips-operators.cpp index ddbef979..1cb98e9f 100644 --- a/cplusplus/vips-operators.cpp +++ b/cplusplus/vips-operators.cpp @@ -1,7 +1,3 @@ -// bodies for vips operations -// Fri Feb 12 20:03:53 GMT 2016 -// this file is generated automatically, do not edit! - void VImage::system( char * cmd_format , VOption *options ) { call( "system" , @@ -1321,26 +1317,28 @@ VImage VImage::fractsurf( int width , int height , double fractal_dimension , VO return( out ); } -VImage VImage::radload( char * filename , VOption *options ) +VImage VImage::worley( int width , int height , VOption *options ) { VImage out; - call( "radload" , + call( "worley" , (options ? options : VImage::option()) -> - set( "filename", filename ) -> - set( "out", &out ) ); + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) ); return( out ); } -VImage VImage::ppmload( char * filename , VOption *options ) +VImage VImage::perlin( int width , int height , VOption *options ) { VImage out; - call( "ppmload" , + call( "perlin" , (options ? options : VImage::option()) -> - set( "filename", filename ) -> - set( "out", &out ) ); + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) ); return( out ); } @@ -1369,18 +1367,6 @@ VImage VImage::matrixload( char * filename , VOption *options ) return( out ); } -VImage VImage::analyzeload( char * filename , VOption *options ) -{ - VImage out; - - call( "analyzeload" , - (options ? options : VImage::option()) -> - set( "filename", filename ) -> - set( "out", &out ) ); - - return( out ); -} - VImage VImage::rawload( char * filename , int width , int height , int bands , VOption *options ) { VImage out; @@ -1408,6 +1394,42 @@ VImage VImage::vipsload( char * filename , VOption *options ) return( out ); } +VImage VImage::analyzeload( char * filename , VOption *options ) +{ + VImage out; + + call( "analyzeload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::ppmload( char * filename , VOption *options ) +{ + VImage out; + + call( "ppmload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::radload( char * filename , VOption *options ) +{ + VImage out; + + call( "radload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + VImage VImage::pdfload( char * filename , VOption *options ) { VImage out; @@ -1648,34 +1670,6 @@ VImage VImage::openexrload( char * filename , VOption *options ) return( out ); } -void VImage::radsave( char * filename , VOption *options ) -{ - call( "radsave" , - (options ? options : VImage::option()) -> - set( "in", *this ) -> - set( "filename", filename ) ); -} - -VipsBlob * VImage::radsave_buffer( VOption *options ) -{ - VipsBlob * buffer; - - call( "radsave_buffer" , - (options ? options : VImage::option()) -> - set( "in", *this ) -> - set( "buffer", &buffer ) ); - - return( buffer ); -} - -void VImage::ppmsave( char * filename , VOption *options ) -{ - call( "ppmsave" , - (options ? options : VImage::option()) -> - set( "in", *this ) -> - set( "filename", filename ) ); -} - void VImage::csvsave( char * filename , VOption *options ) { call( "csvsave" , @@ -1723,6 +1717,34 @@ void VImage::vipssave( char * filename , VOption *options ) set( "filename", filename ) ); } +void VImage::ppmsave( char * filename , VOption *options ) +{ + call( "ppmsave" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "filename", filename ) ); +} + +void VImage::radsave( char * filename , VOption *options ) +{ + call( "radsave" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "filename", filename ) ); +} + +VipsBlob * VImage::radsave_buffer( VOption *options ) +{ + VipsBlob * buffer; + + call( "radsave_buffer" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "buffer", &buffer ) ); + + return( buffer ); +} + void VImage::dzsave( char * filename , VOption *options ) { call( "dzsave" , diff --git a/libvips/create/perlin.c b/libvips/create/perlin.c index d01e3a79..e006a5a6 100644 --- a/libvips/create/perlin.c +++ b/libvips/create/perlin.c @@ -140,16 +140,17 @@ vips_perlin_create_cells( VipsPerlin *perlin, /* When we calculate the seed for this cell, we wrap * around so that our output will tesselate. */ - if( cx >= perlin->cells_across ) - cx = 0; - seed = vips_perlin_seed_add( seed, cx ); if( cy >= perlin->cells_down ) cy = 0; seed = vips_perlin_seed_add( seed, cy ); + if( cx >= perlin->cells_across ) + cx = 0; + seed = vips_perlin_seed_add( seed, cx ); + seed = vips_perlin_random( seed ); - angle = seed & 0xff; + angle = (seed ^ (seed >> 8) ^ (seed >> 16)) & 0xff; gx[ci] = vips_perlin_cos[angle]; gy[ci] = vips_perlin_sin[angle]; diff --git a/python/find_class_methods.py b/python/find_class_methods.py index 9fc31e3f..93c55159 100755 --- a/python/find_class_methods.py +++ b/python/find_class_methods.py @@ -5,6 +5,8 @@ import sys import logging #logging.basicConfig(level = logging.DEBUG) +import gi +gi.require_version('Vips', '8.0') from gi.repository import Vips, GObject # Search for all VipsOperation which don't have an input image object ... these diff --git a/python/packages/gi/overrides/Vips.py b/python/packages/gi/overrides/Vips.py index 0a78fbda..b015779e 100644 --- a/python/packages/gi/overrides/Vips.py +++ b/python/packages/gi/overrides/Vips.py @@ -1093,6 +1093,8 @@ class_methods = [ "mask_fractal", "tonelut", "identity", + "perlin", + "worley", "fractsurf", "radload", "ppmload", diff --git a/test/test_create.py b/test/test_create.py index d0048625..861ee07d 100755 --- a/test/test_create.py +++ b/test/test_create.py @@ -441,5 +441,19 @@ class TestCreate(unittest.TestCase): self.assertEqual(im.bands, 1) self.assertEqual(im.format, Vips.BandFormat.FLOAT) + def test_worley(self): + im = Vips.Image.worley(512, 512) + self.assertEqual(im.width, 512) + self.assertEqual(im.height, 512) + self.assertEqual(im.bands, 1) + self.assertEqual(im.format, Vips.BandFormat.INT) + + def test_perlin(self): + im = Vips.Image.perlin(512, 512) + self.assertEqual(im.width, 512) + self.assertEqual(im.height, 512) + self.assertEqual(im.bands, 1) + self.assertEqual(im.format, Vips.BandFormat.FLOAT) + if __name__ == '__main__': unittest.main()