From f3842dcc4bcaa69208b3807f6ab11a43d003e922 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 8 Apr 2018 11:44:15 +0100 Subject: [PATCH 1/3] update cpp example thanks fangqiao see https://github.com/jcupitt/libvips/issues/932 --- ChangeLog | 4 +-- doc/using-cpp.xml | 83 +++++++++++++++++++---------------------------- 2 files changed, 35 insertions(+), 52 deletions(-) diff --git a/ChangeLog b/ChangeLog index 65a91921..627d8e64 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ 12/3/18 started 8.6.4 -- better fitting of fonts with overhanging edges, thanks Adrià -- lower stack use in radsave to help musl [Jacob Thrane Lund] +- better fitting of fonts with overhanging edges [Adrià] +- revise C++ example [fangqiao] 12/2/18 started 8.6.3 - use pkg-config to find libjpeg, if we can diff --git a/doc/using-cpp.xml b/doc/using-cpp.xml index a5fb9d39..be1cbeae 100644 --- a/doc/using-cpp.xml +++ b/doc/using-cpp.xml @@ -26,7 +26,7 @@ /* compile with: - * g++ -g -Wall try.cc `pkg-config vips-cpp --cflags --libs` + * g++ -g -Wall example.cc `pkg-config vips-cpp --cflags --libs` */ #include <vips/vips8> @@ -34,64 +34,47 @@ using namespace vips; int -main( int argc, char **argv ) -{ - GOptionContext *context; - GOptionGroup *main_group; - GError *error = NULL; +main (int argc, char **argv) +{ + if (VIPS_INIT (argv[0])) + vips_error_exit (NULL); + + if (argc != 3) + vips_error_exit ("usage: %s input-file output-file", argv[0]); - if( VIPS_INIT( argv[0] ) ) - vips_error_exit( NULL ); - - context = g_option_context_new( "" ); - - main_group = g_option_group_new( NULL, NULL, NULL, NULL, NULL ); - g_option_context_set_main_group( context, main_group ); - g_option_context_add_group( context, vips_get_option_group() ); - - if( !g_option_context_parse( context, &argc, &argv, &error ) ) { - if( error ) { - fprintf( stderr, "%s\n", error->message ); - g_error_free( error ); - } - - vips_error_exit( NULL ); - } - - VImage in = VImage::new_from_file( argv[1], - VImage::option()-> - set( "access", VIPS_ACCESS_SEQUENTIAL ) ); - - double avg = in.avg(); - - printf( "avg = %g\n", avg ); - printf( "width = %d\n", in.width() ); - - VImage in = VImage::new_from_file( argv[1], - VImage::option()-> - set( "access", VIPS_ACCESS_SEQUENTIAL ) ); - - VImage out = in.embed( 10, 10, 1000, 1000, - VImage::option()-> - set( "extend", "background" )-> - set( "background", 128 ) ); - - out.write_to_file( argv[2] ); - - vips_shutdown(); - - return( 0 ); + VImage in = VImage::new_from_file (argv[1], + VImage::option ()->set ("access", VIPS_ACCESS_SEQUENTIAL)); + + double avg = in.avg (); + + printf ("avg = %g\n", avg); + printf ("width = %d\n", in.width ()); + + in = VImage::new_from_file (argv[1], + VImage::option ()->set ("access", VIPS_ACCESS_SEQUENTIAL)); + + VImage out = in.embed (10, 10, 1000, 1000, + VImage::option ()-> + set ("extend", "background")-> + set ("background", 128)); + + out.write_to_file (argv[2]); + + vips_shutdown (); + + return (0); } Everything before VImage in = VImage::.. is exactly - as the C API. This boilerplate gives the example a set of standard - command-line flags. + as the C API. vips_error_exit() just prints the arguments plus the + libvips error log and exits with an error code. - This line is the C++ equivalent of vips_image_new_from_file(). It works + VImage in = VImage::.. is the C++ equivalent of + vips_image_new_from_file(). It works in the same way, the differences being: From d1dd41a21fe8251dd57d9069ae388e1b7ae30764 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 10 Apr 2018 15:18:18 +0100 Subject: [PATCH 2/3] strict round down on jpeg shrink libjpeg rounds up on shrink-on-load. In some cases this can leave a dark line along the right and bottom edge, since it only contains (for example) 1/4 of a pixel of data. This change adds a crop after jpeg load so that only complete pixels are output. See https://github.com/lovell/sharp/issues/1185 --- ChangeLog | 1 + libvips/foreign/jpeg2vips.c | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 627d8e64..c0c417a3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ 12/3/18 started 8.6.4 - better fitting of fonts with overhanging edges [Adrià] - revise C++ example [fangqiao] +- strict round down on jpeg shrink on load [davidwood] 12/2/18 started 8.6.3 - use pkg-config to find libjpeg, if we can diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index eb59427e..cf4b39d1 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -94,6 +94,8 @@ * - revert previous warning change: libvips reports serious corruption, * like a truncated file, as a warning and we need to be able to catch * that + * 10/4/18 + * - strict round down on shrink-on-load */ /* @@ -151,8 +153,6 @@ /* Stuff we track during a read. */ typedef struct _ReadJpeg { - VipsImage *out; - /* Shrink by this much during load. 1, 2, 4, 8. */ int shrink; @@ -177,6 +177,13 @@ typedef struct _ReadJpeg { * during load. */ gboolean autorotate; + + /* cinfo->output_width and height can be larger than we want since + * libjpeg rounds up on shrink-on-load. This is the real size we will + * output, as opposed to the size we decompress to. + */ + int output_width; + int output_height; } ReadJpeg; /* This can be called many times. @@ -228,7 +235,6 @@ readjpeg_new( VipsImage *out, int shrink, gboolean fail, gboolean autorotate ) if( !(jpeg = VIPS_NEW( out, ReadJpeg )) ) return( NULL ); - jpeg->out = out; jpeg->shrink = shrink; jpeg->fail = fail; jpeg->filename = NULL; @@ -408,6 +414,18 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out ) vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL ); + /* cinfo->output_width and cinfo->output_height round up with + * shrink-on-load. For example, if the image is 1801 pixels across and + * we shrink by 4, the output will be 450.25 pixels across, + * cinfo->output_width with be 451, and libjpeg will write a black + * column of pixels down the right. + * + * We must strictly round down, since we don't want fractional pixels + * along the bottom and right. + */ + jpeg->output_width = cinfo->image_width / jpeg->shrink; + jpeg->output_height = cinfo->image_height / jpeg->shrink; + /* Interlaced jpegs need lots of memory to read, so our caller needs * to know. */ @@ -691,12 +709,14 @@ read_jpeg_image( ReadJpeg *jpeg, VipsImage *out ) if( vips_image_generate( t[0], NULL, read_jpeg_generate, NULL, jpeg, NULL ) || - vips_sequential( t[0], &t[1], + vips_extract_area( t[0], &t[1], + 0, 0, jpeg->output_width, jpeg->output_height, NULL ) || + vips_sequential( t[1], &t[2], "tile_height", 8, NULL ) ) return( -1 ); - im = t[1]; + im = t[2]; if( jpeg->autorotate ) im = read_jpeg_rotate( VIPS_OBJECT( out ), im ); @@ -735,6 +755,11 @@ vips__jpeg_read( ReadJpeg *jpeg, VipsImage *out, gboolean header_only ) if( read_jpeg_header( jpeg, out ) ) return( -1 ); + /* Patch in the correct size. + */ + out->Xsize = jpeg->output_width; + out->Ysize = jpeg->output_height; + /* Swap width and height if we're going to rotate this image. */ if( jpeg->autorotate ) { From 24b146790f0628c65500e66effd29855c3fb1d14 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 10 Apr 2018 15:39:51 +0100 Subject: [PATCH 3/3] oop reorder crop to come after cache or we'll write beyond the buffer end --- libvips/foreign/jpeg2vips.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index cf4b39d1..08265baa 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -706,14 +706,17 @@ read_jpeg_image( ReadJpeg *jpeg, VipsImage *out ) printf( "read_jpeg_image: starting decompress\n" ); #endif /*DEBUG*/ + /* We must crop after the seq, or our generate may not be asked for + * full lines of pixels and will attempt to write beyond the buffer. + */ if( vips_image_generate( t[0], NULL, read_jpeg_generate, NULL, jpeg, NULL ) || - vips_extract_area( t[0], &t[1], - 0, 0, jpeg->output_width, jpeg->output_height, NULL ) || - vips_sequential( t[1], &t[2], + vips_sequential( t[0], &t[1], "tile_height", 8, - NULL ) ) + NULL ) || + vips_extract_area( t[1], &t[2], + 0, 0, jpeg->output_width, jpeg->output_height, NULL ) ) return( -1 ); im = t[2];