diff --git a/ChangeLog b/ChangeLog index 41d6dd9d..29436f5c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,8 +16,9 @@ - set "interlaced=1" for interlaced JPG and PNG images 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] +- 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/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: diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index a3320606..04c59f36 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -96,6 +96,8 @@ * that * 9/4/18 * - set interlaced=1 for interlaced images + * 10/4/18 + * - strict round down on shrink-on-load */ /* @@ -153,8 +155,6 @@ /* Stuff we track during a read. */ typedef struct _ReadJpeg { - VipsImage *out; - /* Shrink by this much during load. 1, 2, 4, 8. */ int shrink; @@ -179,6 +179,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. @@ -230,7 +237,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; @@ -410,6 +416,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,15 +709,20 @@ 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_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[1]; + im = t[2]; if( jpeg->autorotate ) im = read_jpeg_rotate( VIPS_OBJECT( out ), im ); @@ -738,6 +761,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 ) {