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
This commit is contained in:
John Cupitt 2018-04-10 15:18:18 +01:00
parent f3842dcc4b
commit d1dd41a21f
2 changed files with 31 additions and 5 deletions

View File

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

View File

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