From 48a2551957351ed8bb2712044071fb481c2dcea3 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 14 Sep 2020 17:26:19 +0100 Subject: [PATCH 1/2] fix TIFF thumbnail of buffer and source We had dropped a couple of patches. see https://github.com/libvips/libvips/issues/1815 --- ChangeLog | 1 + libvips/resample/thumbnail.c | 61 +++++++++++++++++++++++++------- test/test-suite/test_resample.py | 9 ++++- 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index eb41e6d2..49a1e20e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ 6/9/20 started 8.10.2 - update magicksave/load profile handling [kelilevi] - better demand hint rules [kaas3000] +- fix tiff thumbnail from buffer and source [vansante] 9/8/20 started 8.10.1 - fix markdown -> xml conversion in doc generation diff --git a/libvips/resample/thumbnail.c b/libvips/resample/thumbnail.c index 1b6a7862..b6ffe672 100644 --- a/libvips/resample/thumbnail.c +++ b/libvips/resample/thumbnail.c @@ -1057,6 +1057,7 @@ vips_thumbnail_file_open( VipsThumbnail *thumbnail, double factor ) "scale", 1.0 / factor, NULL ) ); } + else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ) { /* We support three modes: subifd pyramids, page-based * pyramids, and simple multi-page TIFFs (no pyramid). @@ -1075,8 +1076,8 @@ vips_thumbnail_file_open( VipsThumbnail *thumbnail, double factor ) return( vips_image_new_from_file( file->filename, "access", VIPS_ACCESS_SEQUENTIAL, NULL ) ); - } + else if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) ) { return( vips_image_new_from_file( file->filename, "access", VIPS_ACCESS_SEQUENTIAL, @@ -1270,12 +1271,29 @@ vips_thumbnail_buffer_open( VipsThumbnail *thumbnail, double factor ) NULL ) ); } else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ) { - return( vips_image_new_from_buffer( - buffer->buf->data, buffer->buf->length, - buffer->option_string, - "access", VIPS_ACCESS_SEQUENTIAL, - "page", (int) factor, - NULL ) ); + /* We support three modes: subifd pyramids, page-based + * pyramids, and simple multi-page TIFFs (no pyramid). + */ + if( thumbnail->subifd_pyramid ) + return( vips_image_new_from_buffer( + buffer->buf->data, buffer->buf->length, + buffer->option_string, + "access", VIPS_ACCESS_SEQUENTIAL, + "subifd", (int) factor, + NULL ) ); + else if( thumbnail->page_pyramid ) + return( vips_image_new_from_buffer( + buffer->buf->data, buffer->buf->length, + buffer->option_string, + "access", VIPS_ACCESS_SEQUENTIAL, + "page", (int) factor, + NULL ) ); + else + return( vips_image_new_from_buffer( + buffer->buf->data, buffer->buf->length, + buffer->option_string, + "access", VIPS_ACCESS_SEQUENTIAL, + NULL ) ); } else if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) ) { return( vips_image_new_from_buffer( @@ -1449,12 +1467,29 @@ vips_thumbnail_source_open( VipsThumbnail *thumbnail, double factor ) NULL ) ); } else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ) { - return( vips_image_new_from_source( - source->source, - source->option_string, - "access", VIPS_ACCESS_SEQUENTIAL, - "page", (int) factor, - NULL ) ); + /* We support three modes: subifd pyramids, page-based + * pyramids, and simple multi-page TIFFs (no pyramid). + */ + if( thumbnail->subifd_pyramid ) + return( vips_image_new_from_source( + source->source, + source->option_string, + "access", VIPS_ACCESS_SEQUENTIAL, + "subifd", (int) factor, + NULL ) ); + else if( thumbnail->page_pyramid ) + return( vips_image_new_from_source( + source->source, + source->option_string, + "access", VIPS_ACCESS_SEQUENTIAL, + "page", (int) factor, + NULL ) ); + else + return( vips_image_new_from_source( + source->source, + source->option_string, + "access", VIPS_ACCESS_SEQUENTIAL, + NULL ) ); } else if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) ) { return( vips_image_new_from_source( diff --git a/test/test-suite/test_resample.py b/test/test-suite/test_resample.py index ceb2eecf..afd21422 100644 --- a/test/test-suite/test_resample.py +++ b/test/test-suite/test_resample.py @@ -2,7 +2,7 @@ import pytest import pyvips -from helpers import JPEG_FILE, OME_FILE, HEIC_FILE, all_formats, have +from helpers import JPEG_FILE, OME_FILE, HEIC_FILE, TIF_FILE, all_formats, have # Run a function expecting a complex image on a two-band image @@ -186,6 +186,13 @@ class TestResample: assert im.width == 100 assert im.height == 570 + # should be able to thumbnail a single-page tiff in a buffer + im1 = pyvips.Image.thumbnail(TIF_FILE, 100) + with open(TIF_FILE, 'rb') as f: + buf = f.read() + im2 = pyvips.Image.thumbnail_buffer(buf, 100) + assert abs(im1.avg() - im2.avg()) < 1 + if have("heifload"): # this image is orientation 6 ... thumbnail should flip it im = pyvips.Image.new_from_file(HEIC_FILE) From 4227606f5a6dbba8d562332f4f2f495135d71fa2 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 14 Sep 2020 17:38:20 +0100 Subject: [PATCH 2/2] fix tiff thumbnail from buffer and source We were missing the new tiff thumbnail logic on the source and buffer paths. see https://github.com/libvips/libvips/issues/1815 --- ChangeLog | 1 + libvips/foreign/jpeg2vips.c | 19 +++++++--- libvips/foreign/vips2jpeg.c | 73 ++++++++++++++++++++++++++++++++++--- 3 files changed, 81 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index 49a1e20e..ff6ab5b6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ - update magicksave/load profile handling [kelilevi] - better demand hint rules [kaas3000] - fix tiff thumbnail from buffer and source [vansante] +- in jpegsave, don't set JFIF resolution if we set EXIF resolution 9/8/20 started 8.10.1 - fix markdown -> xml conversion in doc generation diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index 80964ecc..08d8e7c3 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -108,6 +108,8 @@ * - revise for source IO * 5/5/20 angelmixu * - better handling of JFIF res unit 0 + * 13/9/20 + * - set resolution unit from JFIF */ /* @@ -506,6 +508,13 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out ) break; } +#ifdef DEBUG + if( cinfo->saw_JFIF_marker ) + printf( "read_jpeg_header: jfif _density %d, %d, unit %d\n", + cinfo->X_density, cinfo->Y_density, + cinfo->density_unit ); +#endif /*DEBUG*/ + /* Get the jfif resolution. exif may overwrite this later. Default to * 72dpi (as EXIF does). */ @@ -514,12 +523,6 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out ) if( cinfo->saw_JFIF_marker && cinfo->X_density != 1U && cinfo->Y_density != 1U ) { -#ifdef DEBUG - printf( "read_jpeg_header: jfif _density %d, %d, unit %d\n", - cinfo->X_density, cinfo->Y_density, - cinfo->density_unit ); -#endif /*DEBUG*/ - switch( cinfo->density_unit ) { case 0: /* X_density / Y_density gives the pixel aspect ratio. @@ -535,6 +538,8 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out ) */ xres = cinfo->X_density / 25.4; yres = cinfo->Y_density / 25.4; + vips_image_set_string( out, + VIPS_META_RESOLUTION_UNIT, "in" ); break; case 2: @@ -542,6 +547,8 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out ) */ xres = cinfo->X_density / 10.0; yres = cinfo->Y_density / 10.0; + vips_image_set_string( out, + VIPS_META_RESOLUTION_UNIT, "cm" ); break; default: diff --git a/libvips/foreign/vips2jpeg.c b/libvips/foreign/vips2jpeg.c index c748fad8..83495072 100644 --- a/libvips/foreign/vips2jpeg.c +++ b/libvips/foreign/vips2jpeg.c @@ -94,6 +94,8 @@ * - revise for target IO * 18/2/20 Elad-Laufer * - add subsample_mode, deprecate no_subsample + * 13/9/20 + * - only write JFIF resolution if we don't have EXIF */ /* @@ -412,6 +414,59 @@ write_profile_data (j_compress_ptr cinfo, } } +#ifndef HAVE_EXIF +/* Set the JFIF resolution from the vips xres/yres tags. + */ +static void +vips_jfif_resolution_from_image( struct jpeg_compress_struct *cinfo, + VipsImage *image ) +{ + int xres, yres; + const char *p; + int unit; + + /* Default to inches, more progs support it. + */ + unit = 1; + if( vips_image_get_typeof( image, VIPS_META_RESOLUTION_UNIT ) && + !vips_image_get_string( image, + VIPS_META_RESOLUTION_UNIT, &p ) ) { + if( vips_isprefix( "cm", p ) ) + unit = 2; + else if( vips_isprefix( "none", p ) ) + unit = 0; + } + + switch( unit ) { + case 0: + xres = VIPS_RINT( image->Xres ); + yres = VIPS_RINT( image->Yres ); + break; + + case 1: + xres = VIPS_RINT( image->Xres * 25.4 ); + yres = VIPS_RINT( image->Yres * 25.4 ); + break; + + case 2: + xres = VIPS_RINT( image->Xres * 10.0 ); + yres = VIPS_RINT( image->Yres * 10.0 ); + break; + + default: + g_assert_not_reached(); + break; + } + + VIPS_DEBUG_MSG( "vips_jfif_resolution_from_image: " + "setting xres = %d, yres = %d, unit = %d\n", xres, yres, unit ); + + cinfo->density_unit = unit; + cinfo->X_density = xres; + cinfo->Y_density = yres; +} +#endif /*HAVE_EXIF*/ + /* Write an ICC Profile from a file into the JPEG stream. */ static int @@ -643,18 +698,24 @@ write_vips( Write *write, int qfac, const char *profile, } } - /* Don't write the APP0 JFIF headers if we are stripping. + /* Only write the JFIF headers if we are not stripping and we have no + * EXIF. Some readers get confused if you set both. */ - if( strip ) - write->cinfo.write_JFIF_header = FALSE; + write->cinfo.write_JFIF_header = FALSE; +#ifndef HAVE_EXIF + if( !strip ) { + vips_jfif_resolution_from_image( &write->cinfo, write->in ); + write->cinfo.write_JFIF_header = TRUE; + } +#endif /*HAVE_EXIF*/ - /* Build compress tables. + /* Write app0 and build compress tables. */ jpeg_start_compress( &write->cinfo, TRUE ); - /* Write any APP markers we need. + /* All the other APP chunks come next. */ - if( !strip ) { + if( !strip ) { /* We need to rebuild the exif data block from any exif tags * on the image. */