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