Merge branch 'master' into libspng-experiment

This commit is contained in:
John Cupitt 2020-06-07 11:56:16 +01:00
commit a44814018d
38 changed files with 1281 additions and 937 deletions

View File

@ -15,12 +15,18 @@
- add subsample_mode, deprecate no_subsample in jpegsave [Elad-Laufer]
- add vips_isdirf()
- add PAGENUMBER support to tiff write [jclavoie-jive]
- add all to smartcrop
- add "all" mode to smartcrop
- flood fill could stop half-way for some very complex shapes
- better handling of unaligned reads in multipage tiffs [petoor]
- add experimental libspng reader
- mark old --delete option to vipsthumbnail as deprecated [UweOhse]
- png save with a bad ICC profile just gives a warning
- add "premultipled" option to vips_affine(), clarified vips_resize()
behaviour with alpha channels
- improve bioformats support with read and write of tiff subifd pyramids
- thumbnail exploits subifd pyramids
- handle all EXIF orientation cases, deprecate
vips_autorot_get_angle() [Elad-Laufer]
24/4/20 started 8.9.3
- better iiif tile naming [IllyaMoskvin]

View File

@ -855,14 +855,16 @@ if test x"$with_heif" != x"no"; then
PKG_CHECK_MODULES(HEIF, libheif,
[with_heif=yes
have_h265_decoder=`$PKG_CONFIG libheif --variable builtin_h265_decoder`
have_avif_decoder=`$PKG_CONFIG libheif --variable builtin_avif_decoder`
# test for !=no so that we work for older libheif which does not have
# this variable
if test x"$have_h265_decoder" != x"no"; then
if test x"$have_h265_decoder" != x"no" -o x"$have_avif_decoder" = x"yes"; then
AC_DEFINE(HAVE_HEIF_DECODER,1,
[define if your libheif has decode support.])
fi
have_h265_encoder=`$PKG_CONFIG libheif --variable builtin_h265_encoder`
if test x"$have_h265_encoder" != x"no"; then
have_avif_encoder=`$PKG_CONFIG libheif --variable builtin_avif_encoder`
if test x"$have_h265_encoder" != x"no" -o x"$have_avif_encoder" = x"yes"; then
AC_DEFINE(HAVE_HEIF_ENCODER,1,
[define if your libheif has encode support.])
fi
@ -872,6 +874,8 @@ if test x"$with_heif" != x"no"; then
with_heif=no
have_h265_decoder=
have_h265_encoder=
have_avif_decoder=
have_avif_encoder=
]
)
fi

View File

@ -260,10 +260,10 @@ $ ls -l tn_shark.jpg
-rw-rr 1 john john 7295 Nov  9 14:33 tn_shark.jpg
</programlisting>
<para>
Now encode with sRGB and delete any embedded profile:
Now transform to sRGB and dont attach a profile (you can also use <literal>strip</literal>, though that will remove <emphasis>all</emphasis> metadata from the image):
</para>
<programlisting>
$ vipsthumbnail shark.jpg --eprofile srgb --delete
$ vipsthumbnail shark.jpg --eprofile srgb -o tn_shark.jpg[profile=none]
$ ls -l tn_shark.jpg
-rw-rr 1 john john 4229 Nov  9 14:33 tn_shark.jpg
</programlisting>
@ -299,6 +299,7 @@ $ vipsthumbnail kgdev.jpg --iprofile cmyk
<programlisting>
$ vipsthumbnail fred.jpg \
--size 128 \
--eprofile srgb \
-o tn_%s.jpg[optimize_coding,strip] \
--eprofile srgb
</programlisting>

View File

@ -6,6 +6,9 @@
* - test and remove orientation from every ifd
* 6/10/18
* - don't remove orientation if it's one of the cases we don't handle
* 10/5/20
* - handle mirrored images
* - deprecate vips_autorot_get_angle()
*/
/*
@ -54,6 +57,7 @@ typedef struct _VipsAutorot {
VipsImage *in;
VipsAngle angle;
gboolean flip;
} VipsAutorot;
@ -61,79 +65,6 @@ typedef VipsConversionClass VipsAutorotClass;
G_DEFINE_TYPE( VipsAutorot, vips_autorot, VIPS_TYPE_CONVERSION );
/**
* vips_autorot_get_angle:
* @image: image to fetch orientation from
*
* Examine the metadata on @im and return the #VipsAngle to rotate by to turn
* the image upright.
*
* See also: vips_autorot().
*
* Returns: the #VipsAngle to rotate by to make the image upright.
*/
VipsAngle
vips_autorot_get_angle( VipsImage *im )
{
int orientation;
VipsAngle angle;
if( !vips_image_get_typeof( im, VIPS_META_ORIENTATION ) ||
vips_image_get_int( im, VIPS_META_ORIENTATION, &orientation ) )
orientation = 1;
switch( orientation ) {
case 6:
angle = VIPS_ANGLE_D90;
break;
case 8:
angle = VIPS_ANGLE_D270;
break;
case 3:
angle = VIPS_ANGLE_D180;
break;
default:
/* Other values do rotate + mirror, don't bother handling them
* though, how common can mirroring be.
*/
angle = VIPS_ANGLE_D0;
break;
}
return( angle );
}
/* TRUE if this is one of the cases we handle.
*/
static gboolean
vips_autorot_handled( VipsImage *im )
{
int orientation;
gboolean handled;
if( !vips_image_get_typeof( im, VIPS_META_ORIENTATION ) ||
vips_image_get_int( im, VIPS_META_ORIENTATION, &orientation ) )
orientation = 1;
switch( orientation ) {
case 1:
case 3:
case 6:
case 8:
handled = TRUE;
break;
default:
handled = FALSE;
break;
}
return( handled );
}
static void *
vips_autorot_remove_angle_sub( VipsImage *image,
const char *field, GValue *value, void *my_data )
@ -154,14 +85,14 @@ vips_autorot_remove_angle_sub( VipsImage *image,
* vips_autorot_remove_angle: (method)
* @image: image to remove orientation from
*
* Remove the orientation tag on @image. Also remove any exif orientation tags.
*
* See also: vips_autorot_get_angle().
* Remove the orientation tag on @image. Also remove any exif orientation tags.
* You must vips_copy() the image before calling this function since it
* modifies metadata.
*/
void
vips_autorot_remove_angle( VipsImage *image )
{
(void) vips_image_remove( image, VIPS_META_ORIENTATION );
(void) vips_image_remove( image, VIPS_META_ORIENTATION );
(void) vips_image_map( image, vips_autorot_remove_angle_sub, NULL );
}
@ -170,27 +101,87 @@ vips_autorot_build( VipsObject *object )
{
VipsConversion *conversion = VIPS_CONVERSION( object );
VipsAutorot *autorot = (VipsAutorot *) object;
VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 );
VipsImage **t = (VipsImage **) vips_object_local_array( object, 3 );
if( VIPS_OBJECT_CLASS( vips_autorot_parent_class )->build( object ) )
return( -1 );
VipsAngle angle;
gboolean flip;
VipsImage *in;
g_object_set( object,
"angle", vips_autorot_get_angle( autorot->in ),
NULL );
in = autorot->in;
switch( vips_image_get_orientation( in ) ) {
case 2:
angle = VIPS_ANGLE_D0;
flip = TRUE;
break;
case 3:
angle = VIPS_ANGLE_D180;
flip = FALSE;
break;
case 4:
angle = VIPS_ANGLE_D180;
flip = TRUE;
break;
case 5:
angle = VIPS_ANGLE_D90;
flip = TRUE;
break;
case 6:
angle = VIPS_ANGLE_D90;
flip = FALSE;
break;
case 7:
angle = VIPS_ANGLE_D270;
flip = TRUE;
break;
case 8:
angle = VIPS_ANGLE_D270;
flip = FALSE;
break;
case 1:
default:
angle = VIPS_ANGLE_D0;
flip = FALSE;
break;
if( vips_autorot_handled( autorot->in ) ) {
if( vips_rot( autorot->in, &t[0], autorot->angle, NULL ) ||
vips_copy( t[0], &t[1], NULL ) )
return( -1 );
vips_autorot_remove_angle( t[1] );
}
else {
if( vips_copy( autorot->in, &t[1], NULL ) )
return( -1 );
}
if( vips_image_write( t[1], conversion->out ) )
g_object_set( object,
"angle", angle,
"flip", flip,
NULL );
if( angle != VIPS_ANGLE_D0 ) {
if( vips_rot( in, &t[0], angle, NULL ) )
return( -1 );
in = t[0];
}
if( flip ) {
if( vips_flip( in, &t[1], VIPS_DIRECTION_HORIZONTAL, NULL ) )
return( -1 );
in = t[1];
}
/* We must copy before modifying metadata.
*/
if( vips_copy( in, &t[2], NULL ) )
return( -1 );
in = t[2];
vips_autorot_remove_angle( in );
if( vips_image_write( in, conversion->out ) )
return( -1 );
return( 0 );
@ -220,13 +211,21 @@ vips_autorot_class_init( VipsAutorotClass *class )
_( "Angle image was rotated by" ),
VIPS_ARGUMENT_OPTIONAL_OUTPUT,
G_STRUCT_OFFSET( VipsAutorot, angle ),
VIPS_TYPE_ANGLE, VIPS_ANGLE_D0 );
VIPS_TYPE_ANGLE, VIPS_ANGLE_D0 );
VIPS_ARG_BOOL( class, "flip", 7,
_( "Flip" ),
_( "Whether the image was flipped or not" ),
VIPS_ARGUMENT_OPTIONAL_OUTPUT,
G_STRUCT_OFFSET( VipsAutorot, flip ),
FALSE );
}
static void
vips_autorot_init( VipsAutorot *autorot )
{
autorot->angle = VIPS_ANGLE_D0;
autorot->flip = FALSE;
}
/**
@ -238,18 +237,14 @@ vips_autorot_init( VipsAutorot *autorot )
* Optional arguments:
*
* * @angle: output #VipsAngle the image was rotated by
* * @flip: output %gboolean whether the image was flipped
*
* Look at the image metadata and rotate the image to make it upright. The
* #VIPS_META_ORIENTATION tag is removed from @out to prevent accidental
* double rotation.
* Look at the image metadata and rotate and flip the image to make it
* upright. The #VIPS_META_ORIENTATION tag is removed from @out to prevent
* accidental double rotation.
*
* Read @angle to find the amount the image was rotated by.
*
* vips only supports the four simple rotations, it does not support the
* various mirror modes. If the image is using one of these mirror modes, the
* image is not rotated and the #VIPS_META_ORIENTATION tag is not removed.
*
* See also: vips_autorot_get_angle(), vips_autorot_remove_angle(), vips_rot().
* Read @angle to find the amount the image was rotated by. Read @flip to
* see if the image was also flipped.
*
* Returns: 0 on success, -1 on error
*/
@ -265,4 +260,3 @@ vips_autorot( VipsImage *in, VipsImage **out, ... )
return( result );
}

View File

@ -30,6 +30,8 @@
* - set Xoffset/Yoffset to ink left/top
* 27/6/19
* - fitting could occasionally terminate early [levmorozov]
* 16/5/20 [keiviv]
* - don't add fontfiles repeatedly
*/
/*
@ -103,6 +105,10 @@ typedef VipsCreateClass VipsTextClass;
G_DEFINE_TYPE( VipsText, vips_text, VIPS_TYPE_CREATE );
/* ... single-thread the body of vips_text() with this.
*/
static GMutex *vips_text_lock = NULL;
/* Just have one of these and reuse it.
*
* This does not unref cleanly on many platforms, so we will leak horribly
@ -111,9 +117,10 @@ G_DEFINE_TYPE( VipsText, vips_text, VIPS_TYPE_CREATE );
*/
static PangoFontMap *vips_text_fontmap = NULL;
/* ... single-thread the body of vips_text() with this.
/* All the fontfiles we've loaded. fontconfig lets you add a fontfile
* repeatedly, and we obviously don't want that.
*/
static GMutex *vips_text_lock = NULL;
static GHashTable *vips_text_fontfiles = NULL;
static void
vips_text_dispose( GObject *gobject )
@ -350,17 +357,26 @@ vips_text_build( VipsObject *object )
if( !vips_text_fontmap )
vips_text_fontmap = pango_ft2_font_map_new();
if( !vips_text_fontfiles )
vips_text_fontfiles =
g_hash_table_new( g_str_hash, g_str_equal );
text->context = pango_font_map_create_context(
PANGO_FONT_MAP( vips_text_fontmap ) );
if( text->fontfile &&
!FcConfigAppFontAddFile( NULL,
!g_hash_table_lookup( vips_text_fontfiles, text->fontfile ) ) {
if( !FcConfigAppFontAddFile( NULL,
(const FcChar8 *) text->fontfile ) ) {
vips_error( class->nickname,
_( "unable to load font \"%s\"" ), text->fontfile );
g_mutex_unlock( vips_text_lock );
return( -1 );
vips_error( class->nickname,
_( "unable to load font \"%s\"" ),
text->fontfile );
g_mutex_unlock( vips_text_lock );
return( -1 );
}
g_hash_table_insert( vips_text_fontfiles,
text->fontfile,
g_strdup( text->fontfile ) );
}
/* If our caller set height and not dpi, we adjust dpi until
@ -543,7 +559,7 @@ vips_text_init( VipsText *text )
* * @fontfile: %gchararray, load this font file
* * @width: %gint, image should be no wider than this many pixels
* * @height: %gint, image should be no higher than this many pixels
* * @align: #VipsAlign, left/centre/right alignment
* * @align: #VipsAlign, set justification alignment
* * @justify: %gboolean, justify lines
* * @dpi: %gint, render at this resolution
* * @autofit_dpi: %gint, read out auto-fitted DPI
@ -564,11 +580,12 @@ vips_text_init( VipsText *text )
*
* @width is the number of pixels to word-wrap at. Lines of text wider than
* this will be broken at word boundaries.
* @align can be used to set the alignment style for multi-line
* text. Note that the output image can be wider than @width if there are no
* word breaks, or narrower if the lines don't break exactly at @width.
*
* Set @justify to turn on line justification.
* @align can be used to set the alignment style for multi-line
* text to the low (left) edge centre, or high (right) edge. Note that the
* output image can be wider than @width if there are no
* word breaks, or narrower if the lines don't break exactly at @width.
*
* @height is the maximum number of pixels high the generated text can be. This
* only takes effect when @dpi is not set, and @width is set, making a box.

View File

@ -76,7 +76,7 @@ im_tiff_read_header( const char *filename, VipsImage *out,
if( !(source = vips_source_new_from_file( filename )) )
return( -1 );
if( vips__tiff_read_header_source( source,
out, page, n, autorotate ) ) {
out, page, n, autorotate, -1 ) ) {
VIPS_UNREF( source );
return( -1 );
}
@ -93,7 +93,7 @@ im_tiff_read( const char *filename, VipsImage *out,
if( !(source = vips_source_new_from_file( filename )) )
return( -1 );
if( vips__tiff_read_source( source, out, page, n, autorotate ) ) {
if( vips__tiff_read_source( source, out, page, n, autorotate, -1 ) ) {
VIPS_UNREF( source );
return( -1 );
}

View File

@ -801,3 +801,15 @@ vips_warn( const char *domain, const char *fmt, ... )
va_end( ap );
}
/**
* vips_autorot_get_angle:
* @image: image to fetch orientation from
*
* This function is deprecated. Use vips_autorot() instead.
*/
VipsAngle
vips_autorot_get_angle( VipsImage *im )
{
return( VIPS_ANGLE_D0 );
}

View File

@ -69,7 +69,7 @@
* to be a slow way to do it. This is where the draw operations come in.
*
* To use these operations, use vips_copy() to make a copy of the image you
* want to modify, to ensure that no one else is using it, then call a
* want to modify to ensure that no one else is using it, then call a
* series of draw operations.
* Once you are done drawing, return to normal use of vips operations. Any time
* you want to start drawing again, you'll need to copy again.

View File

@ -413,6 +413,10 @@ vips_foreign_load_csv_load( VipsForeignLoad *load )
for( y = 0; y < load->real->Ysize; y++ ) {
csv->colno = 0;
/* Not needed, but stops a used-before-set compiler warning.
*/
ch = EOF;
for( x = 0; x < load->real->Xsize; x++ ) {
double value;

View File

@ -523,7 +523,8 @@ vips__exif_parse( VipsImage *image )
int orientation;
orientation = atoi( str );
if( orientation < 1 || orientation > 8 )
if( orientation < 1 ||
orientation > 8 )
orientation = 1;
vips_image_set_int( image, VIPS_META_ORIENTATION, orientation );
}

View File

@ -237,7 +237,8 @@ static const char *heif_magic[] = {
"ftyphevm", /* Multiview sequence */
"ftyphevs", /* Scaleable sequence */
"ftypmif1", /* Nokia alpha_ image */
"ftypmsf1" /* Nokia animation image */
"ftypmsf1", /* Nokia animation image */
"ftypavif" /* AV1 image format */
};
/* THe API has:

View File

@ -830,40 +830,6 @@ read_jpeg_generate( VipsRegion *or,
return( 0 );
}
/* Auto-rotate, if rotate_image is set.
*/
static VipsImage *
read_jpeg_rotate( VipsObject *process, VipsImage *im )
{
VipsImage **t = (VipsImage **) vips_object_local_array( process, 3 );
VipsAngle angle = vips_autorot_get_angle( im );
if( angle != VIPS_ANGLE_D0 ) {
/* Need to copy to memory or disc, we have to stay seq.
*/
const guint64 image_size = VIPS_IMAGE_SIZEOF_IMAGE( im );
const guint64 disc_threshold = vips_get_disc_threshold();
if( image_size > disc_threshold )
t[0] = vips_image_new_temp_file( "%s.v" );
else
t[0] = vips_image_new_memory();
if( vips_image_write( im, t[0] ) ||
vips_rot( t[0], &t[1], angle, NULL ) )
return( NULL );
im = t[1];
if( vips_copy( im, &t[2], NULL ) )
return( NULL );
im = t[2];
vips_autorot_remove_angle( im );
}
return( im );
}
/* Read a cinfo to a VIPS image.
*/
static int
@ -871,7 +837,7 @@ read_jpeg_image( ReadJpeg *jpeg, VipsImage *out )
{
struct jpeg_decompress_struct *cinfo = &jpeg->cinfo;
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( out ), 3 );
vips_object_local_array( VIPS_OBJECT( out ), 4 );
VipsImage *im;
@ -902,10 +868,16 @@ read_jpeg_image( ReadJpeg *jpeg, VipsImage *out )
vips_extract_area( t[1], &t[2],
0, 0, jpeg->output_width, jpeg->output_height, NULL ) )
return( -1 );
im = t[2];
if( jpeg->autorotate )
im = read_jpeg_rotate( VIPS_OBJECT( out ), im );
if( jpeg->autorotate &&
vips_image_get_orientation( im ) != 1 ) {
/* This will go via a huge memory buffer :-(
*/
if( vips_autorot( im, &t[3], NULL ) )
return( -1 );
im = t[3];
}
if( vips_image_write( im, out ) )
return( -1 );
@ -948,15 +920,9 @@ vips__jpeg_read( ReadJpeg *jpeg, VipsImage *out, gboolean header_only )
/* Swap width and height if we're going to rotate this image.
*/
if( jpeg->autorotate ) {
VipsAngle angle = vips_autorot_get_angle( out );
if( angle == VIPS_ANGLE_D90 ||
angle == VIPS_ANGLE_D270 )
VIPS_SWAP( int, out->Xsize, out->Ysize );
/* We won't be returning an orientation tag.
*/
if( jpeg->autorotate &&
vips_image_get_orientation_swap( out ) ) {
VIPS_SWAP( int, out->Xsize, out->Ysize );
vips_autorot_remove_angle( out );
}
}

View File

@ -63,7 +63,8 @@ int vips__tiff_write( VipsImage *in, const char *filename,
VipsRegionShrink region_shrink,
int level,
gboolean lossless,
VipsForeignDzDepth depth );
VipsForeignDzDepth depth,
gboolean subifd );
int vips__tiff_write_buf( VipsImage *in,
void **obuf, size_t *olen,
@ -81,14 +82,15 @@ int vips__tiff_write_buf( VipsImage *in,
VipsRegionShrink region_shrink,
int level,
gboolean lossless,
VipsForeignDzDepth depth );
VipsForeignDzDepth depth,
gboolean subifd );
gboolean vips__istiff_source( VipsSource *source );
gboolean vips__istifftiled_source( VipsSource *source );
int vips__tiff_read_header_source( VipsSource *source, VipsImage *out,
int page, int n, gboolean autorotate );
int page, int n, gboolean autorotate, int subifd );
int vips__tiff_read_source( VipsSource *source, VipsImage *out,
int page, int n, gboolean autorotate );
int page, int n, gboolean autorotate, int subifd );
extern const char *vips__foreign_tiff_suffs[];

View File

@ -197,6 +197,8 @@
* - read logluv images as XYZ
* 11/4/20 petoor
* - better handling of aligned reads in multipage tiffs
* 28/5/20
* - add subifd
*/
/*
@ -269,7 +271,7 @@ typedef struct _RtiffHeader {
int alpha_band;
uint16 compression;
/* Result of TIFFIsTiled().
/* Is this directory tiled.
*/
gboolean tiled;
@ -302,6 +304,15 @@ typedef struct _RtiffHeader {
/* Scale factor to get absolute cd/m2 from XYZ.
*/
double stonits;
/* Number of subifds, if any.
*/
int subifd_count;
/* Optional IMAGEDESCRIPTION.
*/
char *image_description;
} RtiffHeader;
/* Scanline-type process function.
@ -320,6 +331,7 @@ typedef struct _Rtiff {
int page;
int n;
gboolean autorotate;
int subifd;
/* The TIFF we read.
*/
@ -527,7 +539,7 @@ rtiff_minimise_cb( VipsImage *image, Rtiff *rtiff )
static Rtiff *
rtiff_new( VipsSource *source, VipsImage *out,
int page, int n, gboolean autorotate )
int page, int n, gboolean autorotate, int subifd )
{
Rtiff *rtiff;
@ -540,6 +552,7 @@ rtiff_new( VipsSource *source, VipsImage *out,
rtiff->page = page;
rtiff->n = n;
rtiff->autorotate = autorotate;
rtiff->subifd = subifd;
rtiff->tiff = NULL;
rtiff->n_pages = 0;
rtiff->current_page = -1;
@ -608,7 +621,8 @@ rtiff_set_page( Rtiff *rtiff, int page )
{
if( rtiff->current_page != page ) {
#ifdef DEBUG
printf( "rtiff_set_page: selecting page %d\n", page );
printf( "rtiff_set_page: selecting page %d, subifd %d\n",
page, rtiff->subifd );
#endif /*DEBUG*/
if( !TIFFSetDirectory( rtiff->tiff, page ) ) {
@ -617,7 +631,43 @@ rtiff_set_page( Rtiff *rtiff, int page )
return( -1 );
}
if( rtiff->subifd >= 0 ) {
int subifd_count;
toff_t *subifd_offsets;
if( !TIFFGetField( rtiff->tiff, TIFFTAG_SUBIFD,
&subifd_count, &subifd_offsets ) ) {
vips_error( "tiff2vips",
"%s", _( "no SUBIFD tag" ) );
return( -1 );
}
if( subifd_count <= 0 ||
rtiff->subifd >= subifd_count ) {
vips_error( "tiff2vips",
_( "subifd %d out of range, "
"only 0-%d available" ),
rtiff->subifd,
subifd_count - 1 );
return( -1 );
}
if( !TIFFSetSubDirectory( rtiff->tiff,
subifd_offsets[rtiff->subifd] ) ) {
vips_error( "tiff2vips",
"%s", _( "subdirectory unreadable" ) );
return( -1 );
}
}
rtiff->current_page = page;
/* This can get unset when we change directories. Make sure
* it's set again.
*/
if( rtiff->header.compression == COMPRESSION_JPEG )
TIFFSetField( rtiff->tiff,
TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB );
}
return( 0 );
@ -633,7 +683,9 @@ rtiff_n_pages( Rtiff *rtiff )
for( n = 1; TIFFReadDirectory( rtiff->tiff ); n++ )
;
(void) TIFFSetDirectory( rtiff->tiff, rtiff->current_page );
/* Make sure the nest set_page() will set the directory.
*/
rtiff->current_page = -1;
#ifdef DEBUG
printf( "rtiff_n_pages: found %d pages\n", n );
@ -1512,6 +1564,10 @@ rtiff_set_header( Rtiff *rtiff, VipsImage *out )
vips_image_set_int( out,
VIPS_META_PAGE_HEIGHT, rtiff->header.height );
if( rtiff->header.subifd_count > 0 )
vips_image_set_int( out,
VIPS_META_N_SUBIFDS, rtiff->header.subifd_count );
vips_image_set_int( out, VIPS_META_N_PAGES, rtiff->n_pages );
/* Even though we could end up serving tiled data, always hint
@ -1520,17 +1576,6 @@ rtiff_set_header( Rtiff *rtiff, VipsImage *out )
*/
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
#ifdef DEBUG
printf( "rtiff_set_header: header.samples_per_pixel = %d\n",
rtiff->header.samples_per_pixel );
printf( "rtiff_set_header: header.bits_per_sample = %d\n",
rtiff->header.bits_per_sample );
printf( "rtiff_set_header: header.sample_format = %d\n",
rtiff->header.sample_format );
printf( "rtiff_set_header: header.orientation = %d\n",
rtiff->header.orientation );
#endif /*DEBUG*/
/* We have a range of output paths. Look at the tiff header and try to
* route the input image to the best output path.
*/
@ -1540,18 +1585,16 @@ rtiff_set_header( Rtiff *rtiff, VipsImage *out )
/* Read any ICC profile.
*/
if( TIFFGetField( rtiff->tiff,
TIFFTAG_ICCPROFILE, &data_length, &data ) ) {
TIFFTAG_ICCPROFILE, &data_length, &data ) )
vips_image_set_blob_copy( out,
VIPS_META_ICC_NAME, data, data_length );
}
/* Read any XMP metadata.
*/
if( TIFFGetField( rtiff->tiff,
TIFFTAG_XMLPACKET, &data_length, &data ) ) {
TIFFTAG_XMLPACKET, &data_length, &data ) )
vips_image_set_blob_copy( out,
VIPS_META_XMP_NAME, data, data_length );
}
/* Read any IPTC metadata.
*/
@ -1569,20 +1612,13 @@ rtiff_set_header( Rtiff *rtiff, VipsImage *out )
/* Read any photoshop metadata.
*/
if( TIFFGetField( rtiff->tiff,
TIFFTAG_PHOTOSHOP, &data_length, &data ) ) {
TIFFTAG_PHOTOSHOP, &data_length, &data ) )
vips_image_set_blob_copy( out,
VIPS_META_PHOTOSHOP_NAME, data, data_length );
}
/* IMAGEDESCRIPTION often has useful metadata.
*/
if( TIFFGetField( rtiff->tiff, TIFFTAG_IMAGEDESCRIPTION, &data ) ) {
/* libtiff makes sure that data is null-terminated and contains
* no embedded null characters.
*/
vips_image_set_string( out,
VIPS_META_IMAGEDESCRIPTION, (char *) data );
}
if( rtiff->header.image_description )
vips_image_set_string( out, VIPS_META_IMAGEDESCRIPTION,
rtiff->header.image_description );
if( get_resolution( rtiff->tiff, out ) )
return( -1 );
@ -1689,7 +1725,7 @@ rtiff_fill_region_unaligned( VipsRegion *out,
/* Not necessary, but it stops static analyzers complaining
* about a used-before-set.
*/
tile.height = 0;
hit.height = 0;
x = 0;
while( x < r->width ) {
@ -1753,10 +1789,13 @@ rtiff_fill_region_unaligned( VipsRegion *out,
q, p, hit.width, rtiff->client );
}
x += tile.width;
x += hit.width;
}
y += tile.height;
/* This will be the same for all tiles in the row we've just
* done.
*/
y += hit.height;
}
return( 0 );
@ -1823,59 +1862,6 @@ rtiff_seq_stop( void *seq, void *a, void *b )
return( 0 );
}
/* Auto-rotate handling.
*/
static int
rtiff_autorotate( Rtiff *rtiff, VipsImage *in, VipsImage **out )
{
VipsAngle angle = vips_autorot_get_angle( in );
if( rtiff->autorotate &&
angle != VIPS_ANGLE_D0 ) {
/* Need to copy to memory or disc, we have to stay seq.
*/
const guint64 image_size = VIPS_IMAGE_SIZEOF_IMAGE( in );
const guint64 disc_threshold = vips_get_disc_threshold();
VipsImage *im;
VipsImage *x;
if( image_size > disc_threshold )
im = vips_image_new_temp_file( "%s.v" );
else
im = vips_image_new_memory();
if( vips_image_write( in, im ) ) {
g_object_unref( im );
return( -1 );
}
if( vips_rot( im, &x, angle, NULL ) ) {
g_object_unref( im );
return( -1 );
}
g_object_unref( im );
im = x;
if( vips_copy( im, out, NULL ) ) {
g_object_unref( im );
return( -1 );
}
g_object_unref( im );
/* We must remove the tag to prevent accidental
* double rotations.
*/
vips_autorot_remove_angle( *out );
}
else {
*out = in;
g_object_ref( in );
}
return( 0 );
}
/* Unpremultiply associative alpha, if any.
*/
static int
@ -1913,6 +1899,8 @@ rtiff_read_tilewise( Rtiff *rtiff, VipsImage *out )
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( out ), 4 );
VipsImage *in;
#ifdef DEBUG
printf( "tiff2vips: rtiff_read_tilewise\n" );
#endif /*DEBUG*/
@ -1952,21 +1940,31 @@ rtiff_read_tilewise( Rtiff *rtiff, VipsImage *out )
*/
vips_image_pipelinev( t[0], VIPS_DEMAND_STYLE_THINSTRIP, NULL );
if( vips_image_generate( t[0],
rtiff_seq_start, rtiff_fill_region, rtiff_seq_stop,
rtiff, NULL ) )
return( -1 );
/* Copy to out, adding a cache. Enough tiles for two complete rows.
/* Generate to out, adding a cache. Enough tiles for two complete rows.
*/
if( vips_tilecache( t[0], &t[1],
"tile_width", tile_width,
"tile_height", tile_height,
"max_tiles", 2 * (1 + t[0]->Xsize / tile_width),
NULL ) ||
rtiff_autorotate( rtiff, t[1], &t[2] ) ||
rtiff_unpremultiply( rtiff, t[2], &t[3] ) ||
vips_image_write( t[3], out ) )
if(
vips_image_generate( t[0],
rtiff_seq_start, rtiff_fill_region, rtiff_seq_stop,
rtiff, NULL ) ||
vips_tilecache( t[0], &t[1],
"tile_width", tile_width,
"tile_height", tile_height,
"max_tiles", 2 * (1 + t[0]->Xsize / tile_width),
NULL ) ||
rtiff_unpremultiply( rtiff, t[1], &t[2] ) )
return( -1 );
in = t[2];
/* Only do this if we have to.
*/
if( rtiff->autorotate &&
vips_image_get_orientation( in ) != 1 ) {
if( vips_autorot( in, &t[3], NULL ) )
return( -1 );
in = t[3];
}
if( vips_image_write( in, out ) )
return( -1 );
return( 0 );
@ -2187,6 +2185,8 @@ rtiff_read_stripwise( Rtiff *rtiff, VipsImage *out )
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( out ), 4 );
VipsImage *in;
#ifdef DEBUG
printf( "tiff2vips: rtiff_read_stripwise\n" );
#endif /*DEBUG*/
@ -2197,19 +2197,6 @@ rtiff_read_stripwise( Rtiff *rtiff, VipsImage *out )
vips_image_pipelinev( t[0], VIPS_DEMAND_STYLE_THINSTRIP, NULL );
#ifdef DEBUG
printf( "rtiff_read_stripwise: header.rows_per_strip = %u\n",
rtiff->header.rows_per_strip );
printf( "rtiff_read_stripwise: header.strip_size = %zd\n",
rtiff->header.strip_size );
printf( "rtiff_read_stripwise: header.number_of_strips = %d\n",
rtiff->header.number_of_strips );
printf( "rtiff_read_stripwise: header.read_height = %u\n",
rtiff->header.read_height );
printf( "rtiff_read_stripwise: header.read_size = %zd\n",
rtiff->header.read_size );
#endif /*DEBUG*/
/* Double check: in memcpy mode, the vips linesize should exactly
* match the tiff line size.
*/
@ -2275,9 +2262,20 @@ rtiff_read_stripwise( Rtiff *rtiff, VipsImage *out )
vips_sequential( t[0], &t[1],
"tile_height", rtiff->header.read_height,
NULL ) ||
rtiff_autorotate( rtiff, t[1], &t[2] ) ||
rtiff_unpremultiply( rtiff, t[2], &t[3] ) ||
vips_image_write( t[3], out ) )
rtiff_unpremultiply( rtiff, t[1], &t[2] ) )
return( -1 );
in = t[2];
/* Only do this if we have to.
*/
if( rtiff->autorotate &&
vips_image_get_orientation( in ) != 1 ) {
if( vips_autorot( in, &t[3], NULL ) )
return( -1 );
in = t[3];
}
if( vips_image_write( in, out ) )
return( -1 );
return( 0 );
@ -2290,6 +2288,8 @@ rtiff_header_read( Rtiff *rtiff, RtiffHeader *header )
{
uint16 extra_samples_count;
uint16 *extra_samples_types;
toff_t *subifd_offsets;
char *image_description;
if( !tfget32( rtiff->tiff, TIFFTAG_IMAGEWIDTH,
&header->width ) ||
@ -2387,10 +2387,42 @@ rtiff_header_read( Rtiff *rtiff, RtiffHeader *header )
header->separate = TRUE;
}
/* Stays zero if there's no SUBIFD.
*/
TIFFGetField( rtiff->tiff, TIFFTAG_SUBIFD,
&header->subifd_count, &subifd_offsets );
/* IMAGEDESCRIPTION often has useful metadata. libtiff makes sure
* that data is null-terminated and contains no embedded null
* characters.
*/
if( TIFFGetField( rtiff->tiff,
TIFFTAG_IMAGEDESCRIPTION, &image_description ) )
header->image_description =
vips_strdup( VIPS_OBJECT( rtiff->out ),
image_description );
/* Tiles and strip images have slightly different fields.
*/
header->tiled = TIFFIsTiled( rtiff->tiff );
#ifdef DEBUG
printf( "rtiff_header_read: header.width = %d\n",
header->width );
printf( "rtiff_header_read: header.height = %d\n",
header->height );
printf( "rtiff_header_read: header.samples_per_pixel = %d\n",
header->samples_per_pixel );
printf( "rtiff_header_read: header.bits_per_sample = %d\n",
header->bits_per_sample );
printf( "rtiff_header_read: header.sample_format = %d\n",
header->sample_format );
printf( "rtiff_header_read: header.orientation = %d\n",
header->orientation );
printf( "rtiff_header_read: header.tiled = %d\n",
header->tiled );
#endif /*DEBUG*/
if( header->tiled ) {
if( !tfget32( rtiff->tiff,
TIFFTAG_TILEWIDTH, &header->tile_width ) ||
@ -2398,6 +2430,13 @@ rtiff_header_read( Rtiff *rtiff, RtiffHeader *header )
TIFFTAG_TILELENGTH, &header->tile_height ) )
return( -1 );
#ifdef DEBUG
printf( "rtiff_header_read: header.tile_width = %d\n",
header->tile_width );
printf( "rtiff_header_read: header.tile_height = %d\n",
header->tile_height );
#endif /*DEBUG*/
/* Arbitrary sanity-checking limits.
*/
if( header->tile_width <= 0 ||
@ -2412,6 +2451,13 @@ rtiff_header_read( Rtiff *rtiff, RtiffHeader *header )
header->tile_size = TIFFTileSize( rtiff->tiff );
header->tile_row_size = TIFFTileRowSize( rtiff->tiff );
#ifdef DEBUG
printf( "rtiff_header_read: header.tile_size = %zd\n",
header->tile_size );
printf( "rtiff_header_read: header.tile_row_size = %zd\n",
header->tile_row_size );
#endif /*DEBUG*/
/* Fuzzed TIFFs can give crazy values for tile_size. Sanity
* check at 100mb per tile.
*/
@ -2440,6 +2486,17 @@ rtiff_header_read( Rtiff *rtiff, RtiffHeader *header )
header->scanline_size = TIFFScanlineSize( rtiff->tiff );
header->number_of_strips = TIFFNumberOfStrips( rtiff->tiff );
#ifdef DEBUG
printf( "rtiff_header_read: header.rows_per_strip = %d\n",
header->rows_per_strip );
printf( "rtiff_header_read: header.strip_size = %zd\n",
header->strip_size );
printf( "rtiff_header_read: header.scanline_size = %zd\n",
header->scanline_size );
printf( "rtiff_header_read: header.number_of_strips = %d\n",
header->number_of_strips );
#endif /*DEBUG*/
/* libtiff has two strip-wise readers. TIFFReadEncodedStrip()
* decompresses an entire strip to memory. It's fast, but it
* will need a lot of ram if the strip is large.
@ -2478,6 +2535,15 @@ rtiff_header_read( Rtiff *rtiff, RtiffHeader *header )
header->read_size = header->strip_size;
}
#ifdef DEBUG
printf( "rtiff_header_read: header.read_scanlinewise = %d\n",
header->read_scanlinewise );
printf( "rtiff_header_read: header.read_height = %d\n",
header->read_height );
printf( "rtiff_header_read: header.read_size = %zd\n",
header->read_size );
#endif /*DEBUG*/
/* Stop some compiler warnings.
*/
header->tile_width = 0;
@ -2545,7 +2611,8 @@ static int
rtiff_header_read_all( Rtiff *rtiff )
{
#ifdef DEBUG
printf( "tiff2vips: reading header for page %d ...\n", rtiff->page );
printf( "rtiff_header_read_all: "
"reading header for page %d ...\n", rtiff->page );
#endif /*DEBUG*/
if( rtiff_set_page( rtiff, rtiff->page ) ||
@ -2567,7 +2634,8 @@ rtiff_header_read_all( Rtiff *rtiff )
RtiffHeader header;
#ifdef DEBUG
printf( "tiff2vips: verifying header for page %d ...\n",
printf( "rtiff_header_read_all: "
"verifying header for page %d ...\n",
rtiff->page + i );
#endif /*DEBUG*/
@ -2587,29 +2655,6 @@ rtiff_header_read_all( Rtiff *rtiff )
return( 0 );
}
/* On a header-only read, we can just swap width/height if orientation is 6 or
* 8.
*/
static void
vips__tiff_read_header_orientation( Rtiff *rtiff, VipsImage *out )
{
int orientation;
if( rtiff->autorotate &&
vips_image_get_typeof( out, VIPS_META_ORIENTATION ) &&
!vips_image_get_int( out,
VIPS_META_ORIENTATION, &orientation ) ) {
if( orientation == 3 ||
orientation == 6 )
VIPS_SWAP( int, out->Xsize, out->Ysize );
/* We must remove VIPS_META_ORIENTATION to prevent accidental
* double rotations.
*/
vips_image_remove( out, VIPS_META_ORIENTATION );
}
}
typedef gboolean (*TiffPropertyFn)( TIFF *tif );
static gboolean
@ -2646,20 +2691,24 @@ vips__istifftiled_source( VipsSource *source )
int
vips__tiff_read_header_source( VipsSource *source, VipsImage *out,
int page, int n, gboolean autorotate )
int page, int n, gboolean autorotate, int subifd )
{
Rtiff *rtiff;
vips__tiff_init();
if( !(rtiff = rtiff_new( source, out, page, n, autorotate )) ||
if( !(rtiff = rtiff_new( source, out, page, n, autorotate, subifd )) ||
rtiff_header_read_all( rtiff ) )
return( -1 );
if( rtiff_set_header( rtiff, out ) )
return( -1 );
vips__tiff_read_header_orientation( rtiff, out );
if( rtiff->autorotate &&
vips_image_get_orientation_swap( out ) ) {
VIPS_SWAP( int, out->Xsize, out->Ysize );
vips_autorot_remove_angle( out );
}
/* We never call vips_source_decode() since we need to be able to
* seek() the whole way through the file. Just minimise instead,
@ -2671,7 +2720,7 @@ vips__tiff_read_header_source( VipsSource *source, VipsImage *out,
int
vips__tiff_read_source( VipsSource *source, VipsImage *out,
int page, int n, gboolean autorotate )
int page, int n, gboolean autorotate, int subifd )
{
Rtiff *rtiff;
@ -2681,7 +2730,7 @@ vips__tiff_read_source( VipsSource *source, VipsImage *out,
vips__tiff_init();
if( !(rtiff = rtiff_new( source, out, page, n, autorotate )) ||
if( !(rtiff = rtiff_new( source, out, page, n, autorotate, subifd )) ||
rtiff_header_read_all( rtiff ) )
return( -1 );

View File

@ -69,6 +69,10 @@ typedef struct _VipsForeignLoadTiff {
*/
int n;
/* Select subifd index. -1 for main image.
*/
int subifd;
/* Autorotate using orientation tag.
*/
gboolean autorotate;
@ -133,7 +137,7 @@ vips_foreign_load_tiff_header( VipsForeignLoad *load )
VipsForeignLoadTiff *tiff = (VipsForeignLoadTiff *) load;
if( vips__tiff_read_header_source( tiff->source, load->out,
tiff->page, tiff->n, tiff->autorotate ) )
tiff->page, tiff->n, tiff->autorotate, tiff->subifd ) )
return( -1 );
return( 0 );
@ -145,7 +149,7 @@ vips_foreign_load_tiff_load( VipsForeignLoad *load )
VipsForeignLoadTiff *tiff = (VipsForeignLoadTiff *) load;
if( vips__tiff_read_source( tiff->source, load->real,
tiff->page, tiff->n, tiff->autorotate ) )
tiff->page, tiff->n, tiff->autorotate, tiff->subifd ) )
return( -1 );
return( 0 );
@ -203,6 +207,14 @@ vips_foreign_load_tiff_class_init( VipsForeignLoadTiffClass *class )
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadTiff, autorotate ),
FALSE );
VIPS_ARG_INT( class, "subifd", 21,
_( "subifd" ),
_( "Select subifd index" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadTiff, subifd ),
-1, 100000, -1 );
}
static void
@ -210,6 +222,7 @@ vips_foreign_load_tiff_init( VipsForeignLoadTiff *tiff )
{
tiff->page = 0;
tiff->n = 1;
tiff->subifd = -1;
}
typedef struct _VipsForeignLoadTiffSource {
@ -454,6 +467,7 @@ vips_foreign_load_tiff_buffer_init( VipsForeignLoadTiffBuffer *buffer )
* * @n: %gint, load this many pages
* * @autorotate: %gboolean, use orientation tag to rotate the image
* during load
* * @subifd: %gint, select this subifd index
*
* Read a TIFF file into a VIPS image. It is a full baseline TIFF 6 reader,
* with extensions for tiled images, multipage images, XYZ and LAB colour
@ -478,6 +492,14 @@ vips_foreign_load_tiff_buffer_init( VipsForeignLoadTiffBuffer *buffer )
* operations will use #VIPS_META_ORIENTATION, if present, to set the
* orientation of output images.
*
* If @autorotate is TRUE, the image will be rotated upright during load and
* no metadata attached. This can be very slow.
*
* If @subifd is -1 (the default), the main image is selected for each page.
* If it is 0 or greater and there is a SUBIFD tag, the indexed SUBIFD is
* selected. This can be used to read lower resolution layers from
* bioformats-style image pyramids.
*
* Any ICC profile is read and attached to the VIPS image as
* #VIPS_META_ICC_NAME. Any XMP metadata is read and attached to the image
* as #VIPS_META_XMP_NAME. Any IPTC is attached as #VIPS_META_IPTC_NAME. The
@ -515,6 +537,7 @@ vips_tiffload( const char *filename, VipsImage **out, ... )
* * @n: %gint, load this many pages
* * @autorotate: %gboolean, use orientation tag to rotate the image
* during load
* * @subifd: %gint, select this subifd index
*
* Read a TIFF-formatted memory block into a VIPS image. Exactly as
* vips_tiffload(), but read from a memory source.
@ -558,6 +581,7 @@ vips_tiffload_buffer( void *buf, size_t len, VipsImage **out, ... )
* * @n: %gint, load this many pages
* * @autorotate: %gboolean, use orientation tag to rotate the image
* during load
* * @subifd: %gint, select this subifd index
*
* Exactly as vips_tiffload(), but read from a source.
*

View File

@ -21,6 +21,8 @@
* - xres/yres params were in pixels/cm
* 26/1/20
* - add "depth" to set pyr depth
* 12/5/20
* - add "subifd" to create pyr layers as sub-directories
*/
/*
@ -98,6 +100,8 @@ typedef struct _VipsForeignSaveTiff {
int level;
gboolean lossless;
VipsForeignDzDepth depth;
gboolean subifd;
} VipsForeignSaveTiff;
typedef VipsForeignSaveClass VipsForeignSaveTiffClass;
@ -327,7 +331,7 @@ vips_foreign_save_tiff_class_init( VipsForeignSaveTiffClass *class )
1, 22, 10 );
VIPS_ARG_BOOL( class, "lossless", 24,
_( "lossless" ),
_( "Lossless" ),
_( "Enable WEBP lossless mode" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveTiff, lossless ),
@ -340,6 +344,13 @@ vips_foreign_save_tiff_class_init( VipsForeignSaveTiffClass *class )
G_STRUCT_OFFSET( VipsForeignSaveTiff, depth ),
VIPS_TYPE_FOREIGN_DZ_DEPTH, VIPS_FOREIGN_DZ_DEPTH_ONETILE );
VIPS_ARG_BOOL( class, "subifd", 24,
_( "Sub-IFD" ),
_( "Save pyr layers as sub-IFDs" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveTiff, subifd ),
FALSE );
}
static void
@ -396,7 +407,8 @@ vips_foreign_save_tiff_file_build( VipsObject *object )
tiff->region_shrink,
tiff->level,
tiff->lossless,
tiff->depth ) )
tiff->depth,
tiff->subifd ) )
return( -1 );
return( 0 );
@ -468,7 +480,8 @@ vips_foreign_save_tiff_buffer_build( VipsObject *object )
tiff->region_shrink,
tiff->level,
tiff->lossless,
tiff->depth ) )
tiff->depth,
tiff->subifd ) )
return( -1 );
/* vips__tiff_write_buf() makes a buffer that needs g_free(), not
@ -537,6 +550,7 @@ vips_foreign_save_tiff_buffer_init( VipsForeignSaveTiffBuffer *buffer )
* * @level: %gint, Zstd compression level
* * @lossless: %gboolean, WebP losssless mode
* * @depth: #VipsForeignDzDepth how deep to make the pyramid
* * @subifd: %gboolean write pyr layers as sub-ifds
*
* Write a VIPS image to a file as TIFF.
*
@ -620,6 +634,10 @@ vips_foreign_save_tiff_buffer_init( VipsForeignSaveTiffBuffer *buffer )
* #VIPS_META_PHOTOSHOP_NAME (if set) is used to set the value of the PHOTOSHOP
* tag.
*
* By default, pyramid layers are saved as consecutive pages.
* Set @subifd to save pyramid layers as sub-directories of the main image.
* Setting this option can improve compatibility with formats like OME.
*
* See also: vips_tiffload(), vips_image_write_to_file().
*
* Returns: 0 on success, -1 on error.
@ -665,6 +683,7 @@ vips_tiffsave( VipsImage *in, const char *filename, ... )
* * @level: %gint, Zstd compression level
* * @lossless: %gboolean, WebP losssless mode
* * @depth: #VipsForeignDzDepth how deep to make the pyramid
* * @subifd: %gboolean write pyr layers as sub-ifds
*
* As vips_tiffsave(), but save to a memory buffer.
*

View File

@ -193,6 +193,8 @@
* - write XYZ images as logluv
* 7/2/20 [jclavoie-jive]
* - add PAGENUMBER support
* 23/5/20
* - add support for subifd pyramid layers
*/
/*
@ -247,6 +249,21 @@
#include "pforeign.h"
#include "tiff.h"
/* TODO:
*
* - add a flag for plane-separate write
*
* At the moment, we write bioformats-style TIFFs by splitting bands up,
* making a toilet-roll image and writing out in pages. The TIFFs we make
* are not tagged as plane-separate and do not have (eg.) RGB photometric
* interpretation. Moreover, when working from an RGB source, we'll end
* up reading the input three times.
*
* A write-plane-separate flag to the TIFF writer could let us set the
* photometric interpretation correctly, and save all planes in a single
* pass before doing a final gather sweep.
*/
/* Max number of alpha channels we allow.
*/
#define MAX_ALPHA (64)
@ -334,6 +351,7 @@ struct _Wtiff {
int level; /* zstd compression level */
gboolean lossless; /* webp lossless mode */
VipsForeignDzDepth depth; /* Pyr depth */
gboolean subifd; /* Write pyr layers into subifds */
/* True if we've detected a toilet-roll image, plus the page height,
* which has been checked to be a factor of im->Ysize. page_number
@ -395,40 +413,62 @@ embed_profile_meta( TIFF *tif, VipsImage *im )
return( 0 );
}
static Layer *
wtiff_layer_new( Wtiff *wtiff, Layer *above, int width, int height )
static void
wtiff_layer_init( Wtiff *wtiff, Layer **layer, Layer *above,
int width, int height )
{
Layer *layer;
if( !*layer ) {
*layer = VIPS_NEW( wtiff->ready, Layer );
(*layer)->wtiff = wtiff;
(*layer)->width = width;
(*layer)->height = height;
layer = VIPS_NEW( wtiff->ready, Layer );
layer->wtiff = wtiff;
layer->width = width;
layer->height = height;
if( !above )
/* Top of pyramid.
*/
(*layer)->sub = 1;
else
(*layer)->sub = above->sub * 2;
if( !above )
/* Top of pyramid.
(*layer)->lname = NULL;
(*layer)->buf = NULL;
(*layer)->len = 0;
(*layer)->tif = NULL;
(*layer)->image = NULL;
(*layer)->write_y = 0;
(*layer)->y = 0;
(*layer)->strip = NULL;
(*layer)->copy = NULL;
(*layer)->below = NULL;
(*layer)->above = above;
/* The name for the top layer is the output filename.
*
* We need lname to be freed automatically: it has to stay
* alive until after wtiff_gather().
*/
layer->sub = 1;
else
layer->sub = above->sub * 2;
if( wtiff->filename ) {
if( !above )
(*layer)->lname = vips_strdup(
VIPS_OBJECT( wtiff->ready ),
wtiff->filename );
else {
char *lname;
layer->lname = NULL;
layer->buf = NULL;
layer->len = 0;
layer->tif = NULL;
layer->image = NULL;
layer->write_y = 0;
layer->y = 0;
layer->strip = NULL;
layer->copy = NULL;
lname = vips__temp_name( "%s.tif" );
(*layer)->lname = vips_strdup(
VIPS_OBJECT( wtiff->ready ),
lname );
g_free( lname );
}
}
layer->below = NULL;
layer->above = above;
/*
printf( "wtiff_layer_new: sub = %d, width = %d, height = %d\n",
layer->sub, width, height );
*/
/*
printf( "wtiff_layer_init: sub = %d, width = %d, height = %d\n",
(*layer)->sub, width, height );
*/
}
if( wtiff->pyramid ) {
int limitw, limith;
@ -458,34 +498,13 @@ wtiff_layer_new( Wtiff *wtiff, Layer *above, int width, int height )
* Very tall or wide images might end up with a smallest layer
* larger than one tile.
*/
if( (layer->width > limitw ||
layer->height > limith) &&
layer->width > 1 &&
layer->height > 1 )
layer->below = wtiff_layer_new( wtiff, layer,
if( ((*layer)->width > limitw ||
(*layer)->height > limith) &&
(*layer)->width > 1 &&
(*layer)->height > 1 )
wtiff_layer_init( wtiff, &(*layer)->below, *layer,
width / 2, height / 2 );
}
/* The name for the top layer is the output filename.
*
* We need lname to be freed automatically: it has to stay
* alive until after wtiff_gather().
*/
if( wtiff->filename ) {
if( !above )
layer->lname = vips_strdup( VIPS_OBJECT( wtiff->ready ),
wtiff->filename );
else {
char *lname;
lname = vips__temp_name( "%s.tif" );
layer->lname = vips_strdup( VIPS_OBJECT( wtiff->ready ),
lname );
g_free( lname );
}
}
return( layer );
}
static int
@ -617,6 +636,11 @@ wtiff_write_header( Wtiff *wtiff, Layer *layer )
int orientation;
#ifdef DEBUG
printf( "wtiff_write_header: sub %d, width %d, height %d\n",
layer->sub, layer->width, layer->height );
#endif /*DEBUG*/
/* Output base header fields.
*/
TIFFSetField( tif, TIFFTAG_IMAGEWIDTH, layer->width );
@ -845,40 +869,56 @@ wtiff_allocate_layers( Wtiff *wtiff )
{
Layer *layer;
g_assert( wtiff->layer );
for( layer = wtiff->layer; layer; layer = layer->below ) {
layer->image = vips_image_new();
if( vips_image_pipelinev( layer->image,
VIPS_DEMAND_STYLE_ANY, wtiff->ready, NULL ) )
return( -1 );
layer->image->Xsize = layer->width;
layer->image->Ysize = layer->height;
if( !layer->image ) {
layer->image = vips_image_new();
if( vips_image_pipelinev( layer->image,
VIPS_DEMAND_STYLE_ANY, wtiff->ready, NULL ) )
return( -1 );
layer->image->Xsize = layer->width;
layer->image->Ysize = layer->height;
layer->strip = vips_region_new( layer->image );
layer->copy = vips_region_new( layer->image );
layer->strip = vips_region_new( layer->image );
layer->copy = vips_region_new( layer->image );
/* The regions will get used in the bg thread callback, so
* make sure we don't own them.
*/
vips__region_no_ownership( layer->strip );
vips__region_no_ownership( layer->copy );
/* The regions will get used in the bg thread callback,
* so make sure we don't own them.
*/
vips__region_no_ownership( layer->strip );
vips__region_no_ownership( layer->copy );
if( layer->lname )
layer->tif = vips__tiff_openout(
layer->lname, wtiff->bigtiff );
else {
layer->tif = vips__tiff_openout_buffer(
wtiff->ready, wtiff->bigtiff,
&layer->buf, &layer->len );
}
if( !layer->tif )
return( -1 );
}
if( wtiff_layer_rewind( wtiff, layer ) )
return( -1 );
if( layer->lname )
layer->tif = vips__tiff_openout(
layer->lname, wtiff->bigtiff );
else {
layer->tif = vips__tiff_openout_buffer( wtiff->ready,
wtiff->bigtiff, &layer->buf, &layer->len );
}
if( !layer->tif )
return( -1 );
if( wtiff_write_header( wtiff, layer ) )
return( -1 );
}
if( !wtiff->tbuf ) {
if( wtiff->tile )
wtiff->tbuf = vips_malloc( NULL,
TIFFTileSize( wtiff->layer->tif ) );
else
wtiff->tbuf = vips_malloc( NULL,
TIFFScanlineSize( wtiff->layer->tif ) );
if( !wtiff->tbuf )
return( -1 );
}
return( 0 );
}
@ -1032,7 +1072,8 @@ wtiff_new( VipsImage *input, const char *filename,
VipsRegionShrink region_shrink,
int level,
gboolean lossless,
VipsForeignDzDepth depth )
VipsForeignDzDepth depth,
gboolean subifd )
{
Wtiff *wtiff;
@ -1064,6 +1105,7 @@ wtiff_new( VipsImage *input, const char *filename,
wtiff->level = level;
wtiff->lossless = lossless;
wtiff->depth = depth;
wtiff->subifd = subifd;
wtiff->toilet_roll = FALSE;
wtiff->page_height = vips_image_get_page_height( input );
wtiff->page_number = 0;
@ -1096,28 +1138,6 @@ wtiff_new( VipsImage *input, const char *filename,
wtiff->toilet_roll = TRUE;
wtiff->image_height = wtiff->page_height;
wtiff->n_pages = wtiff->ready->Ysize / wtiff->page_height;
/* We can't pyramid toilet roll images.
*/
if( wtiff->pyramid ) {
g_warning( "%s",
_( "can't pyramid multi page images --- "
"disabling pyramid" ) );
wtiff->pyramid = FALSE;
}
}
/* In strip mode we use tileh to set rowsperstrip, and that does not
* have the multiple-of-16 restriction.
*/
if( tile ) {
if( (wtiff->tilew & 0xf) != 0 ||
(wtiff->tileh & 0xf) != 0 ) {
wtiff_free( wtiff );
vips_error( "vips2tiff",
"%s", _( "tile size not a multiple of 16" ) );
return( NULL );
}
}
/* We can only pyramid LABQ and non-complex images.
@ -1133,6 +1153,41 @@ wtiff_new( VipsImage *input, const char *filename,
}
}
/* Pyramid images must be tiled.
*/
if( wtiff->pyramid &&
!wtiff->tile )
wtiff->tile = TRUE;
/* Multi-page pyramids must be in subifd mode.
*/
if( wtiff->pyramid &&
wtiff->toilet_roll )
wtiff->subifd = TRUE;
/* If compression is off and we're writing a >4gb image, automatically
* enable bigtiff.
*
* This won't always work. If the image data is just under 4gb but
* there's a lot of metadata, we could be pushed over the 4gb limit.
*/
if( wtiff->compression == COMPRESSION_NONE &&
VIPS_IMAGE_SIZEOF_IMAGE( wtiff->ready ) > UINT_MAX )
wtiff->bigtiff = TRUE;
/* In strip mode we use tileh to set rowsperstrip, and that does not
* have the multiple-of-16 restriction.
*/
if( wtiff->tile ) {
if( (wtiff->tilew & 0xf) != 0 ||
(wtiff->tileh & 0xf) != 0 ) {
wtiff_free( wtiff );
vips_error( "vips2tiff",
"%s", _( "tile size not a multiple of 16" ) );
return( NULL );
}
}
/* Can only squash 8 bit mono. 3-band float should have been squashed
* above.
*/
@ -1190,42 +1245,6 @@ wtiff_new( VipsImage *input, const char *filename,
wtiff->tls = VIPS_IMAGE_SIZEOF_PEL( wtiff->ready ) *
wtiff->tilew;
/* If compression is off and we're writing a >4gb image, automatically
* enable bigtiff.
*
* This won't always work. If the image data is just under 4gb but
* there's a lot of metadata, we could be pushed over the 4gb limit.
*/
if( wtiff->compression == COMPRESSION_NONE &&
VIPS_IMAGE_SIZEOF_IMAGE( wtiff->ready ) > UINT_MAX &&
!wtiff->bigtiff ) {
g_warning( "%s", _( "image over 4gb, enabling bigtiff" ) );
wtiff->bigtiff = TRUE;
}
/* Build the pyramid framework.
*/
wtiff->layer = wtiff_layer_new( wtiff, NULL,
wtiff->ready->Xsize, wtiff->image_height );
/* Fill all the layers.
*/
if( wtiff_allocate_layers( wtiff ) ) {
wtiff_free( wtiff );
return( NULL );
}
if( tile )
wtiff->tbuf = vips_malloc( NULL,
TIFFTileSize( wtiff->layer->tif ) );
else
wtiff->tbuf = vips_malloc( NULL,
TIFFScanlineSize( wtiff->layer->tif ) );
if( !wtiff->tbuf ) {
wtiff_free( wtiff );
return( NULL );
}
return( wtiff );
}
@ -1693,10 +1712,10 @@ write_strip( VipsRegion *region, VipsRect *area, void *a )
Wtiff *wtiff = (Wtiff *) a;
Layer *layer = wtiff->layer;
#ifdef DEBUG
#ifdef DEBUG_VERBOSE
printf( "write_strip: strip at %d, height %d\n",
area->top, area->height );
#endif/*DEBUG*/
#endif/*DEBUG_VERBOSE*/
for(;;) {
VipsRect *to = &layer->strip->valid;
@ -1858,7 +1877,7 @@ wtiff_copy_tiff( Wtiff *wtiff, TIFF *out, TIFF *in )
return( 0 );
}
/* Append all of the lower layers we wrote to the output.
/* Append all of the layers we wrote to the output.
*/
static int
wtiff_gather( Wtiff *wtiff )
@ -1873,13 +1892,12 @@ wtiff_gather( Wtiff *wtiff )
TIFF *in;
#ifdef DEBUG
printf( "Appending layer %s ...\n", layer->lname );
printf( "appending layer %s ...\n", layer->lname );
#endif /*DEBUG*/
if( layer->lname ) {
if( !(source =
vips_source_new_from_file(
layer->lname )) )
if( !(source = vips_source_new_from_file(
layer->lname )) )
return( -1 );
}
else {
@ -1892,12 +1910,14 @@ wtiff_gather( Wtiff *wtiff )
VIPS_UNREF( source );
return( -1 );
}
VIPS_UNREF( source );
if( wtiff_copy_tiff( wtiff, wtiff->layer->tif, in ) ) {
TIFFClose( in );
return( -1 );
}
TIFFClose( in );
if( !TIFFWriteDirectory( wtiff->layer->tif ) )
@ -1907,77 +1927,109 @@ wtiff_gather( Wtiff *wtiff )
return( 0 );
}
/* Three types of write: single image, multipage and pyramid.
/* Write one page from our input image, optionally pyramiding it.
*/
static int
wtiff_write_page( Wtiff *wtiff, VipsImage *page )
{
#ifdef DEBUG
printf( "wtiff_write_page:\n" );
#endif /*DEBUG*/
/* Init the pyramid framework for this page. This will just make a
* single layer if we're not pyramiding.
*/
wtiff_layer_init( wtiff, &wtiff->layer, NULL,
page->Xsize, page->Ysize );
/* Fill all the layers and write the TIFF headers.
*/
if( wtiff_allocate_layers( wtiff ) )
return( -1 );
/* In ifd mode, we write the pyramid layers as subdirectories of this
* page.
*/
if( wtiff->subifd ) {
int n_layers;
toff_t *subifd_offsets;
Layer *p;
#ifdef DEBUG
printf( "wtiff_write_page: OME pyr mode\n" );
#endif /*DEBUG*/
/* This magic tag makes the n_layers directories we write
* after this one into subdirectories. We set the offsets to 0
* and libtiff will fill them in automatically.
*/
for( n_layers = 0, p = wtiff->layer->below; p; p = p->below )
n_layers += 1;
subifd_offsets = VIPS_ARRAY( NULL, n_layers, toff_t );
memset( subifd_offsets, 0, n_layers * sizeof( toff_t ) );
TIFFSetField( wtiff->layer->tif, TIFFTAG_SUBIFD,
n_layers, subifd_offsets );
g_free( subifd_offsets );
}
if( vips_sink_disc( page, write_strip, wtiff ) )
return( -1 );
if( !TIFFWriteDirectory( wtiff->layer->tif ) )
return( -1 );
/* Append any pyr layers, if necessary.
*/
if( wtiff->layer->below ) {
/* Free any lower pyramid resources ... this will
* TIFFClose() (but not delete) the smaller layers
* ready for us to read from them again.
*/
layer_free_all( wtiff->layer->below );
/* Append smaller layers to the main file.
*/
if( wtiff_gather( wtiff ) )
return( -1 );
/* We can delete any temps now ready for the next page.
*/
wtiff_delete_temps( wtiff );
/* And free all lower pyr layers ready to be rebuilt for the
* next page.
*/
VIPS_FREEF( layer_free_all, wtiff->layer->below );
}
return( 0 );
}
/* Write all pages.
*/
static int
wtiff_write_image( Wtiff *wtiff )
{
if( wtiff->toilet_roll ) {
int y;
int y;
for( y = 0; y < wtiff->ready->Ysize; y += wtiff->page_height ) {
VipsImage *page;
#ifdef DEBUG
printf( "wtiff_write_image: toilet-roll mode\n" );
printf( "writing page %d ...\n", wtiff->page_number );
#endif /*DEBUG*/
y = 0;
for(;;) {
VipsImage *page;
if( vips_crop( wtiff->ready, &page,
0, y, wtiff->ready->Xsize, wtiff->page_height,
NULL ) )
return( -1 );
if( vips_sink_disc( page, write_strip, wtiff ) ) {
g_object_unref( page );
return( -1 );
}
if( vips_crop( wtiff->ready, &page,
0, y, wtiff->ready->Xsize, wtiff->page_height,
NULL ) )
return( -1 );
if( wtiff_write_page( wtiff, page ) ) {
g_object_unref( page );
wtiff->page_number += 1;
y += wtiff->page_height;
if( y >= wtiff->ready->Ysize )
break;
if( !TIFFWriteDirectory( wtiff->layer->tif ) ||
wtiff_layer_rewind( wtiff,
wtiff->layer ) ||
wtiff_write_header( wtiff,
wtiff->layer ) )
return( -1 );
return( -1 );
}
}
else if( wtiff->pyramid ) {
#ifdef DEBUG
printf( "wtiff_write_image: pyramid mode\n" );
#endif /*DEBUG*/
g_object_unref( page );
if( vips_sink_disc( wtiff->ready, write_strip, wtiff ) )
return( -1 );
if( !TIFFWriteDirectory( wtiff->layer->tif ) )
return( -1 );
if( wtiff->pyramid ) {
/* Free lower pyramid resources ... this will
* TIFFClose() (but not delete) the smaller layers
* ready for us to read from them again.
*/
if( wtiff->layer->below )
layer_free_all( wtiff->layer->below );
/* Append smaller layers to the main file.
*/
if( wtiff_gather( wtiff ) )
return( -1 );
}
}
else {
#ifdef DEBUG
printf( "wtiff_write_image: single-image mode\n" );
#endif /*DEBUG*/
if( vips_sink_disc( wtiff->ready, write_strip, wtiff ) )
return( -1 );
wtiff->page_number += 1;
}
return( 0 );
@ -1999,7 +2051,8 @@ vips__tiff_write( VipsImage *input, const char *filename,
VipsRegionShrink region_shrink,
int level,
gboolean lossless,
VipsForeignDzDepth depth )
VipsForeignDzDepth depth,
gboolean subifd )
{
Wtiff *wtiff;
@ -2013,7 +2066,8 @@ vips__tiff_write( VipsImage *input, const char *filename,
compression, Q, predictor, profile,
tile, tile_width, tile_height, pyramid, squash,
miniswhite, resunit, xres, yres, bigtiff, rgbjpeg,
properties, strip, region_shrink, level, lossless, depth )) )
properties, strip, region_shrink, level, lossless, depth,
subifd )) )
return( -1 );
if( wtiff_write_image( wtiff ) ) {
@ -2043,7 +2097,8 @@ vips__tiff_write_buf( VipsImage *input,
VipsRegionShrink region_shrink,
int level,
gboolean lossless,
VipsForeignDzDepth depth )
VipsForeignDzDepth depth,
gboolean subifd )
{
Wtiff *wtiff;
@ -2053,7 +2108,8 @@ vips__tiff_write_buf( VipsImage *input,
compression, Q, predictor, profile,
tile, tile_width, tile_height, pyramid, squash,
miniswhite, resunit, xres, yres, bigtiff, rgbjpeg,
properties, strip, region_shrink, level, lossless, depth )) )
properties, strip, region_shrink, level, lossless, depth,
subifd )) )
return( -1 );
wtiff->obuf = obuf;

View File

@ -292,6 +292,8 @@ void vips_info( const char *domain, const char *fmt, ... )
__attribute__((format(printf, 2, 3)));
void vips_vinfo( const char *domain, const char *fmt, va_list ap );
VipsAngle vips_autorot_get_angle( VipsImage *image );
#ifdef __cplusplus
}
#endif /*__cplusplus*/

View File

@ -192,7 +192,6 @@ int vips_rot270( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel));
int vips_rot45( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel));
VipsAngle vips_autorot_get_angle( VipsImage *image );
void vips_autorot_remove_angle( VipsImage *image );
int vips_autorot( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel));

View File

@ -150,6 +150,13 @@ extern "C" {
*/
#define VIPS_META_N_PAGES "n-pages"
/**
* VIPS_META_N_SUBIFDS:
*
* If set, the number of subifds in the first page of the file.
*/
#define VIPS_META_N_SUBIFDS "n-subifds"
guint64 vips_format_sizeof( VipsBandFormat format );
guint64 vips_format_sizeof_unsafe( VipsBandFormat format );
@ -171,6 +178,9 @@ double vips_image_get_scale( const VipsImage *image );
double vips_image_get_offset( const VipsImage *image );
int vips_image_get_page_height( VipsImage *image );
int vips_image_get_n_pages( VipsImage *image );
int vips_image_get_n_subifds( VipsImage *image );
int vips_image_get_orientation( VipsImage *image );
gboolean vips_image_get_orientation_swap( VipsImage *image );
const void *vips_image_get_data( VipsImage *image );
void vips_image_init_fields( VipsImage *image,

View File

@ -818,7 +818,7 @@ vips_image_get_page_height( VipsImage *image )
* vips_image_get_n_pages: (method)
* @image: image to get from
*
* Fetch and sanity-check VIPS_META_N_PAGES. Default to 1 if not present or
* Fetch and sanity-check #VIPS_META_N_PAGES. Default to 1 if not present or
* crazy.
*
* This is the number of pages in the image file, not the number of pages that
@ -840,6 +840,70 @@ vips_image_get_n_pages( VipsImage *image )
return( 1 );
}
/**
* vips_image_get_n_subifds: (method)
* @image: image to get from
*
* Fetch and sanity-check #VIPS_META_N_SUBIFDS. Default to 0 if not present or
* crazy.
*
* Returns: the number of subifds in the image file
*/
int
vips_image_get_n_subifds( VipsImage *image )
{
int n_subifds;
if( vips_image_get_typeof( image, VIPS_META_N_SUBIFDS ) &&
!vips_image_get_int( image, VIPS_META_N_SUBIFDS, &n_subifds ) &&
n_subifds > 1 &&
n_subifds < 1000 )
return( n_subifds );
return( 0 );
}
/**
* vips_image_get_orientation: (method)
* @image: image to get from
*
* Fetch and sanity-check #VIPS_META_ORIENTATION. Default to 1 (no rotate,
* no flip) if not present or crazy.
*
* Returns: the image orientation.
*/
int
vips_image_get_orientation( VipsImage *image )
{
int orientation;
if( vips_image_get_typeof( image, VIPS_META_ORIENTATION ) &&
!vips_image_get_int( image, VIPS_META_ORIENTATION,
&orientation ) &&
orientation > 0 &&
orientation < 9 )
return( orientation );
return( 1 );
}
/**
* vips_image_get_orientation_swap: (method)
* @image: image to get from
*
* Return %TRUE if applying the orientation would swap width and height.
*
* Returns: if width/height will swap
*/
gboolean
vips_image_get_orientation_swap( VipsImage *image )
{
int orientation = vips_image_get_orientation( image );
return( orientation >= 5 &&
orientation <= 8 );
}
/**
* vips_image_get_data: (method)
* @image: image to get data for
@ -934,14 +998,11 @@ static int
meta_cp( VipsImage *dst, const VipsImage *src )
{
if( src->meta ) {
/* Loop, copying fields.
*/
meta_init( dst );
/* We lock with vips_image_set() to stop races in highly-
* threaded applications.
*/
g_mutex_lock( vips__meta_lock );
meta_init( dst );
vips_slist_map2( src->meta_traverse,
(VipsSListMap2Fn) meta_cp_field, dst, NULL );
g_mutex_unlock( vips__meta_lock );
@ -1032,8 +1093,6 @@ vips_image_set( VipsImage *image, const char *name, GValue *value )
g_assert( name );
g_assert( value );
meta_init( image );
/* We lock between modifying metadata and copying metadata between
* images, see meta_cp().
*
@ -1042,6 +1101,7 @@ vips_image_set( VipsImage *image, const char *name, GValue *value )
* highly-threaded applications.
*/
g_mutex_lock( vips__meta_lock );
meta_init( image );
(void) meta_new( image, name, value );
g_mutex_unlock( vips__meta_lock );

View File

@ -88,6 +88,8 @@
* - add "background" parameter
* - better clipping means we have no jaggies on edges
* - premultiply alpha
* 18/5/20
* - add "premultiplied" flag
*/
/*
@ -166,6 +168,10 @@ typedef struct _VipsAffine {
*/
VipsPel *ink;
/* True if the input is already premultiplied (and we don't need to).
*/
gboolean premultiplied;
} VipsAffine;
typedef VipsResampleClass VipsAffineClass;
@ -524,11 +530,13 @@ vips_affine_build( VipsObject *object )
affine->trn.idx -= 1;
affine->trn.idy -= 1;
/* If there's an alpha, we have to premultiply before resampling. See
/* If there's an alpha and we've not premultiplied, we have to
* premultiply before resampling. See
* https://github.com/libvips/libvips/issues/291
*/
have_premultiplied = FALSE;
if( vips_image_hasalpha( in ) ) {
if( vips_image_hasalpha( in ) &&
!affine->premultiplied ) {
if( vips_premultiply( in, &t[3], NULL ) )
return( -1 );
have_premultiplied = TRUE;
@ -680,6 +688,13 @@ vips_affine_class_init( VipsAffineClass *class )
G_STRUCT_OFFSET( VipsAffine, background ),
VIPS_TYPE_ARRAY_DOUBLE );
VIPS_ARG_BOOL( class, "premultiplied", 117,
_( "Premultiplied" ),
_( "Images have premultiplied alpha" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsAffine, premultiplied ),
FALSE );
}
static void
@ -709,6 +724,7 @@ vips_affine_init( VipsAffine *affine )
* * @ody: %gdouble, output vertical offset
* * @extend: #VipsExtend how to generate new pixels
* * @background: #VipsArrayDouble colour for new pixels
* * @premultiplied: %gboolean, images are already premultiplied
*
* This operator performs an affine transform on an image using @interpolate.
*
@ -737,6 +753,10 @@ vips_affine_init( VipsAffine *affine )
*
* @idx, @idy, @odx, @ody default to zero.
*
* Image are normally treated as unpremultiplied, so this operation can be used
* directly on PNG images. If your images have been through vips_premultiply(),
* set @premultiplied.
*
* This operation does not change xres or yres. The image resolution needs to
* be updated by the application.
*

View File

@ -289,6 +289,7 @@ vips_resize_build( VipsObject *object )
"idx", id,
"idy", id,
"extend", VIPS_EXTEND_COPY,
"premultiplied", TRUE,
NULL ) )
return( -1 );
in = t[4];
@ -300,6 +301,7 @@ vips_resize_build( VipsObject *object )
"idx", id,
"idy", id,
"extend", VIPS_EXTEND_COPY,
"premultiplied", TRUE,
NULL ) )
return( -1 );
in = t[4];
@ -311,6 +313,7 @@ vips_resize_build( VipsObject *object )
"idx", id,
"idy", id,
"extend", VIPS_EXTEND_COPY,
"premultiplied", TRUE,
NULL ) )
return( -1 );
in = t[4];
@ -444,7 +447,10 @@ vips_resize_init( VipsResize *resize )
* This operation does not change xres or yres. The image resolution needs to
* be updated by the application.
*
* See also: vips_shrink(), vips_reduce().
* This operation does not premultiply alpha. If your image has an alpha
* channel, you should use vips_premultiply() on it first.
*
* See also: vips_premultiply(), vips_shrink(), vips_reduce().
*
* Returns: 0 on success, -1 on error
*/

View File

@ -28,6 +28,8 @@
* - smarter heif thumbnail selection
* 12/10/19
* - add thumbnail_source
* 2/6/20
* - add subifd pyr support
*/
/*
@ -114,9 +116,11 @@ typedef struct _VipsThumbnail {
int input_width;
int input_height;
int page_height;
VipsAngle angle; /* From vips_autorot_get_angle() */
int orientation; /* From vips_image_get_orientation() */
gboolean swap; /* If we must swap width / height */
int n_pages; /* Pages in file */
int n_loaded_pages; /* Pages we've loaded from file */
int n_subifds; /* Number of subifds */
/* For openslide, we need to read out the size of each level too.
*
@ -131,6 +135,10 @@ typedef struct _VipsThumbnail {
int heif_thumbnail_width;
int heif_thumbnail_height;
/* For TIFF sources, open subifds rather than pages to get pyr layers.
*/
gboolean subifd_pyramid;
} VipsThumbnail;
typedef struct _VipsThumbnailClass {
@ -196,9 +204,11 @@ vips_thumbnail_read_header( VipsThumbnail *thumbnail, VipsImage *image )
{
thumbnail->input_width = image->Xsize;
thumbnail->input_height = image->Ysize;
thumbnail->angle = vips_autorot_get_angle( image );
thumbnail->orientation = vips_image_get_orientation( image );
thumbnail->swap = vips_image_get_orientation_swap( image );
thumbnail->page_height = vips_image_get_page_height( image );
thumbnail->n_pages = vips_image_get_n_pages( image );
thumbnail->n_subifds = vips_image_get_n_subifds( image );
/* VIPS_META_N_PAGES is the number of pages in the document,
* not the number we've read out into this image. We calculate
@ -235,15 +245,24 @@ vips_thumbnail_read_header( VipsThumbnail *thumbnail, VipsImage *image )
}
}
/* This may not be a pyr tiff, so no error if we can't find the layers.
* We just look for two or more pages following roughly /2 shrinks.
/* Detect a TIFF pyramid made of pages following a roughly /2 shrink.
*
* This may not be a pyr tiff, so no error if we can't find the layers.
*/
static void
vips_thumbnail_get_tiff_pyramid( VipsThumbnail *thumbnail )
vips_thumbnail_get_tiff_pyramid_page( VipsThumbnail *thumbnail )
{
VipsThumbnailClass *class = VIPS_THUMBNAIL_GET_CLASS( thumbnail );
int i;
#ifdef DEBUG
printf( "vips_thumbnail_get_tiff_pyramid_page:\n" );
#endif /*DEBUG*/
/* Tell open() that we want to open pages rather than subifds.
*/
thumbnail->subifd_pyramid = FALSE;
for( i = 0; i < thumbnail->n_pages; i++ ) {
VipsImage *page;
int level_width;
@ -276,8 +295,68 @@ vips_thumbnail_get_tiff_pyramid( VipsThumbnail *thumbnail )
/* Now set level_count. This signals that we've found a pyramid.
*/
#ifdef DEBUG
printf( "vips_thumbnail_get_tiff_pyramid: %d layer pyramid detected\n",
thumbnail->n_pages );
printf( "vips_thumbnail_get_tiff_pyramid_page: "
"%d layer pyramid detected\n",
thumbnail->n_pages );
#endif /*DEBUG*/
thumbnail->level_count = thumbnail->n_pages;
}
/* Detect a TIFF pyramid made of subifds following a roughly /2 shrink.
*
* This may not be a pyr tiff, so no error if we can't find the layers.
*/
static void
vips_thumbnail_get_tiff_pyramid_subifd( VipsThumbnail *thumbnail )
{
VipsThumbnailClass *class = VIPS_THUMBNAIL_GET_CLASS( thumbnail );
int i;
#ifdef DEBUG
printf( "vips_thumbnail_get_tiff_pyramid_subifd:\n" );
#endif /*DEBUG*/
/* Tell open() that we want to open subifds rather than pages.
*/
thumbnail->subifd_pyramid = TRUE;
for( i = 0; i < thumbnail->n_subifds; i++ ) {
VipsImage *page;
int level_width;
int level_height;
int expected_level_width;
int expected_level_height;
if( !(page = class->open( thumbnail, i )) )
return;
level_width = page->Xsize;
level_height = page->Ysize;
VIPS_UNREF( page );
/* The main image is size 1, subifd 0 is half that.
*/
expected_level_width = thumbnail->input_width / (2 << i);
expected_level_height = thumbnail->input_height / (2 << i);
/* This won't be exact due to rounding etc.
*/
if( abs( level_width - expected_level_width ) > 5 ||
level_width < 2 )
return;
if( abs( level_height - expected_level_height ) > 5 ||
level_height < 2 )
return;
thumbnail->level_width[i] = level_width;
thumbnail->level_height[i] = level_height;
}
/* Now set level_count. This signals that we've found a pyramid.
*/
#ifdef DEBUG
printf( "vips_thumbnail_get_tiff_pyramid_subifd: "
"%d layer pyramid detected\n",
thumbnail->n_pages );
#endif /*DEBUG*/
thumbnail->level_count = thumbnail->n_pages;
}
@ -313,11 +392,10 @@ static void
vips_thumbnail_calculate_shrink( VipsThumbnail *thumbnail,
int input_width, int input_height, double *hshrink, double *vshrink )
{
/* If we will be rotating, swap the target width and height.
/* If we will be rotating, swap the target width and height.
*/
gboolean rotate =
(thumbnail->angle == VIPS_ANGLE_D90 ||
thumbnail->angle == VIPS_ANGLE_D270) &&
thumbnail->swap &&
thumbnail->auto_rotate;
int target_width = rotate ?
thumbnail->height : thumbnail->width;
@ -459,11 +537,18 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
g_info( "input size is %d x %d",
thumbnail->input_width, thumbnail->input_height );
/* For tiff, we need a separate ->open() for each page to
* get all the pyramid levels.
/* For tiff, scan the image and try to spot page-based and ifd-based
* pyramids.
*/
if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) )
vips_thumbnail_get_tiff_pyramid( thumbnail );
if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ) {
/* Test for a subifd pyr first, since we can do that from just
* one page.
*/
vips_thumbnail_get_tiff_pyramid_subifd( thumbnail );
if( thumbnail->level_count == 0 )
vips_thumbnail_get_tiff_pyramid_page( thumbnail );
}
/* For heif, we need to fetch the thumbnail size, in case we can use
* that as the source.
@ -524,13 +609,12 @@ vips_thumbnail_build( VipsObject *object )
{
VipsThumbnail *thumbnail = VIPS_THUMBNAIL( object );
VipsImage **t = (VipsImage **) vips_object_local_array( object, 15 );
VipsInterpretation interpretation = thumbnail->linear ?
VIPS_INTERPRETATION_scRGB : VIPS_INTERPRETATION_sRGB;
VipsImage *in;
int preshrunk_page_height;
double hshrink;
double vshrink;
VipsInterpretation interpretation;
/* TRUE if we've done the import of an ICC transform and still need to
* export.
@ -628,6 +712,12 @@ vips_thumbnail_build( VipsObject *object )
*/
if( in->Type == VIPS_INTERPRETATION_CMYK )
have_imported = TRUE;
if( thumbnail->linear )
interpretation = VIPS_INTERPRETATION_scRGB;
else if( in->Bands < 3 )
interpretation = VIPS_INTERPRETATION_B_W;
else
interpretation = VIPS_INTERPRETATION_sRGB;
g_info( "converting to processing space %s",
vips_enum_nick( VIPS_TYPE_INTERPRETATION, interpretation ) );
if( vips_colourspace( in, &t[2], interpretation, NULL ) )
@ -742,21 +832,12 @@ vips_thumbnail_build( VipsObject *object )
}
if( thumbnail->auto_rotate &&
thumbnail->angle != VIPS_ANGLE_D0 ) {
VipsAngle angle = vips_autorot_get_angle( in );
g_info( "rotating by %s",
vips_enum_nick( VIPS_TYPE_ANGLE, angle ) );
/* Need to copy to memory, we have to stay seq.
*/
if( !(t[9] = vips_image_copy_memory( in )) ||
vips_rot( t[9], &t[10], angle, NULL ) ||
vips_copy( t[10], &t[14], NULL ) )
return( -1 );
thumbnail->orientation != 1 ) {
g_info( "rotating by EXIF orientation %d",
thumbnail->orientation );
if( vips_autorot( in, &t[14], NULL ) )
return( -1 );
in = t[14];
vips_autorot_remove_angle( in );
}
/* Crop after rotate so we don't need to rotate the crop box.
@ -962,10 +1043,16 @@ vips_thumbnail_file_open( VipsThumbnail *thumbnail, double factor )
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ) {
return( vips_image_new_from_file( file->filename,
"access", VIPS_ACCESS_SEQUENTIAL,
"page", (int) factor,
NULL ) );
if( thumbnail->subifd_pyramid )
return( vips_image_new_from_file( file->filename,
"access", VIPS_ACCESS_SEQUENTIAL,
"subifd", (int) factor,
NULL ) );
else
return( vips_image_new_from_file( file->filename,
"access", VIPS_ACCESS_SEQUENTIAL,
"page", (int) factor,
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) ) {
return( vips_image_new_from_file( file->filename,

View File

@ -1,435 +1,393 @@
libvips/arithmetic/hough_circle.c
libvips/arithmetic/boolean.c
libvips/arithmetic/complex.c
libvips/arithmetic/hough_line.c
libvips/arithmetic/profile.c
libvips/arithmetic/sign.c
libvips/arithmetic/hough.c
libvips/arithmetic/getpoint.c
libvips/arithmetic/remainder.c
libvips/arithmetic/math.c
libvips/arithmetic/sum.c
libvips/arithmetic/hist_find_ndim.c
libvips/arithmetic/subtract.c
libvips/arithmetic/statistic.c
libvips/arithmetic/unary.c
libvips/arithmetic/abs.c
libvips/arithmetic/round.c
libvips/arithmetic/measure.c
libvips/arithmetic/linear.c
libvips/arithmetic/relational.c
libvips/arithmetic/multiply.c
libvips/arithmetic/deviate.c
libvips/arithmetic/unaryconst.c
libvips/arithmetic/min.c
libvips/arithmetic/add.c
libvips/arithmetic/nary.c
libvips/arithmetic/sum.c
libvips/arithmetic/stats.c
libvips/arithmetic/binary.c
libvips/arithmetic/project.c
libvips/arithmetic/hist_find.c
libvips/arithmetic/arithmetic.c
libvips/arithmetic/divide.c
libvips/arithmetic/invert.c
libvips/arithmetic/hough_line.c
libvips/arithmetic/binary.c
libvips/arithmetic/deviate.c
libvips/arithmetic/max.c
libvips/arithmetic/statistic.c
libvips/arithmetic/nary.c
libvips/arithmetic/invert.c
libvips/arithmetic/remainder.c
libvips/arithmetic/boolean.c
libvips/arithmetic/sign.c
libvips/arithmetic/hist_find_ndim.c
libvips/arithmetic/multiply.c
libvips/arithmetic/hough_circle.c
libvips/arithmetic/measure.c
libvips/arithmetic/hist_find.c
libvips/arithmetic/find_trim.c
libvips/arithmetic/math2.c
libvips/arithmetic/getpoint.c
libvips/arithmetic/add.c
libvips/arithmetic/divide.c
libvips/arithmetic/relational.c
libvips/arithmetic/hough.c
libvips/arithmetic/arithmetic.c
libvips/arithmetic/abs.c
libvips/arithmetic/avg.c
libvips/arithmetic/linear.c
libvips/arithmetic/round.c
libvips/arithmetic/math2.c
libvips/arithmetic/unaryconst.c
libvips/arithmetic/complex.c
libvips/arithmetic/profile.c
libvips/arithmetic/unary.c
libvips/arithmetic/subtract.c
libvips/arithmetic/hist_find_indexed.c
libvips/colour/dE76.c
libvips/colour/scRGB2XYZ.c
libvips/arithmetic/math.c
libvips/colour/LabQ2LabS.c
libvips/colour/profile_load.c
libvips/colour/rad2float.c
libvips/colour/XYZ2scRGB.c
libvips/colour/Lab2LabS.c
libvips/colour/LabS2LabQ.c
libvips/colour/UCS2LCh.c
libvips/colour/XYZ2CMYK.c
libvips/colour/CMYK2XYZ.c
libvips/colour/profiles.c
libvips/colour/float2rad.c
libvips/colour/scRGB2XYZ.c
libvips/colour/LabQ2Lab.c
libvips/colour/HSV2sRGB.c
libvips/colour/XYZ2Lab.c
libvips/colour/UCS2LCh.c
libvips/colour/dE76.c
libvips/colour/colour.c
libvips/colour/sRGB2HSV.c
libvips/colour/Lab2LabQ.c
libvips/colour/LCh2UCS.c
libvips/colour/sRGB2scRGB.c
libvips/colour/dECMC.c
libvips/colour/LCh2Lab.c
libvips/colour/Yxy2XYZ.c
libvips/colour/XYZ2Lab.c
libvips/colour/scRGB2BW.c
libvips/colour/sRGB2HSV.c
libvips/colour/LabQ2Lab.c
libvips/colour/LabQ2sRGB.c
libvips/colour/Lab2XYZ.c
libvips/colour/CMYK2XYZ.c
libvips/colour/XYZ2Yxy.c
libvips/colour/XYZ2scRGB.c
libvips/colour/colour.c
libvips/colour/profile_load.c
libvips/colour/Lab2LCh.c
libvips/colour/rad2float.c
libvips/colour/XYZ2CMYK.c
libvips/colour/Lab2LabQ.c
libvips/colour/dECMC.c
libvips/colour/colourspace.c
libvips/colour/scRGB2sRGB.c
libvips/colour/sRGB2scRGB.c
libvips/colour/LCh2UCS.c
libvips/colour/icc_transform.c
libvips/colour/dE00.c
libvips/colour/float2rad.c
libvips/colour/HSV2sRGB.c
libvips/colour/Lab2LabS.c
libvips/colour/LabS2Lab.c
libvips/conversion/bandjoin.c
libvips/colour/LabQ2sRGB.c
libvips/colour/scRGB2BW.c
libvips/colour/Lab2LCh.c
libvips/colour/icc_transform.c
libvips/colour/scRGB2sRGB.c
libvips/colour/dE00.c
libvips/colour/Lab2XYZ.c
libvips/colour/XYZ2Yxy.c
libvips/colour/colourspace.c
libvips/conversion/conversion.c
libvips/conversion/embed.c
libvips/conversion/zoom.c
libvips/conversion/transpose3d.c
libvips/conversion/replicate.c
libvips/conversion/bandfold.c
libvips/conversion/wrap.c
libvips/conversion/arrayjoin.c
libvips/conversion/premultiply.c
libvips/conversion/switch.c
libvips/conversion/scale.c
libvips/conversion/flatten.c
libvips/conversion/conversion.c
libvips/conversion/rot.c
libvips/conversion/sequential.c
libvips/conversion/gamma.c
libvips/conversion/msb.c
libvips/conversion/autorot.c
libvips/conversion/smartcrop.c
libvips/conversion/bandmean.c
libvips/conversion/copy.c
libvips/conversion/tilecache.c
libvips/conversion/extract.c
libvips/conversion/bandbool.c
libvips/conversion/grid.c
libvips/conversion/transpose3d.c
libvips/conversion/unpremultiply.c
libvips/conversion/bandrank.c
libvips/conversion/ifthenelse.c
libvips/conversion/join.c
libvips/conversion/falsecolour.c
libvips/conversion/cache.c
libvips/conversion/embed.c
libvips/conversion/insert.c
libvips/conversion/replicate.c
libvips/conversion/rot45.c
libvips/conversion/byteswap.c
libvips/conversion/bandunfold.c
libvips/conversion/cast.c
libvips/conversion/flip.c
libvips/conversion/zoom.c
libvips/conversion/bandfold.c
libvips/conversion/flatten.c
libvips/conversion/copy.c
libvips/conversion/bandjoin.c
libvips/conversion/rot45.c
libvips/conversion/msb.c
libvips/conversion/extract.c
libvips/conversion/cast.c
libvips/conversion/bandunfold.c
libvips/conversion/tilecache.c
libvips/conversion/sequential.c
libvips/conversion/smartcrop.c
libvips/conversion/premultiply.c
libvips/conversion/bandmean.c
libvips/conversion/falsecolour.c
libvips/conversion/byteswap.c
libvips/conversion/subsample.c
libvips/conversion/bandbool.c
libvips/conversion/recomb.c
libvips/conversion/bandary.c
libvips/conversion/ifthenelse.c
libvips/conversion/gamma.c
libvips/conversion/join.c
libvips/conversion/cache.c
libvips/conversion/grid.c
libvips/conversion/scale.c
libvips/conversion/insert.c
libvips/conversion/autorot.c
libvips/conversion/rot.c
libvips/conversion/bandrank.c
libvips/conversion/switch.c
libvips/convolution/spcor.c
libvips/convolution/conva.c
libvips/convolution/correlation.c
libvips/convolution/gaussblur.c
libvips/convolution/conv.c
libvips/convolution/fastcor.c
libvips/convolution/sobel.c
libvips/convolution/canny.c
libvips/convolution/convi.c
libvips/convolution/compass.c
libvips/convolution/convolution.c
libvips/convolution/convf.c
libvips/convolution/gaussblur.c
libvips/convolution/convasep.c
libvips/convolution/convsep.c
libvips/convolution/sharpen.c
libvips/convolution/convolution.c
libvips/convolution/fastcor.c
libvips/convolution/canny.c
libvips/convolution/convf.c
libvips/convolution/spcor.c
libvips/convolution/compass.c
libvips/convolution/convasep.c
libvips/convolution/sobel.c
libvips/create/perlin.c
libvips/create/worley.c
libvips/create/zone.c
libvips/create/mask_ideal_band.c
libvips/create/gaussmat.c
libvips/convolution/conv.c
libvips/convolution/correlation.c
libvips/create/sines.c
libvips/create/grey.c
libvips/create/buildlut.c
libvips/create/mask_ideal.c
libvips/create/create.c
libvips/create/fractsurf.c
libvips/create/black.c
libvips/create/mask_gaussian.c
libvips/create/xyz.c
libvips/create/invertlut.c
libvips/create/mask.c
libvips/create/mask_butterworth.c
libvips/create/mask_ideal_ring.c
libvips/create/point.c
libvips/create/tonelut.c
libvips/create/mask_ideal.c
libvips/create/text.c
libvips/create/mask_butterworth_ring.c
libvips/create/mask_gaussian.c
libvips/create/gaussnoise.c
libvips/create/mask_butterworth_band.c
libvips/create/sines.c
libvips/create/mask_gaussian_ring.c
libvips/create/eye.c
libvips/create/logmat.c
libvips/create/identity.c
libvips/create/gaussmat.c
libvips/create/worley.c
libvips/create/mask_gaussian_ring.c
libvips/create/gaussnoise.c
libvips/create/zone.c
libvips/create/tonelut.c
libvips/create/perlin.c
libvips/create/point.c
libvips/create/mask.c
libvips/create/mask_butterworth_band.c
libvips/create/mask_ideal_ring.c
libvips/create/mask_butterworth.c
libvips/create/mask_gaussian_band.c
libvips/create/fractsurf.c
libvips/create/identity.c
libvips/create/text.c
libvips/create/mask_fractal.c
libvips/draw/drawink.c
libvips/create/eye.c
libvips/create/black.c
libvips/create/mask_ideal_band.c
libvips/create/buildlut.c
libvips/draw/draw_line.c
libvips/draw/draw_circle.c
libvips/draw/draw_flood.c
libvips/draw/draw_rect.c
libvips/draw/draw_smudge.c
libvips/draw/draw_image.c
libvips/draw/draw_mask.c
libvips/draw/draw.c
libvips/foreign/vipssave.c
libvips/foreign/dzsave.c
libvips/foreign/csv.c
libvips/foreign/niftiload.c
libvips/foreign/magick.c
libvips/foreign/ppmload.c
libvips/foreign/openslideload.c
libvips/foreign/tiffload.c
libvips/foreign/tiff2vips.c
libvips/foreign/radsave.c
libvips/foreign/analyze2vips.c
libvips/foreign/fits.c
libvips/foreign/csvsave.c
libvips/foreign/analyzeload.c
libvips/foreign/ppmsave.c
libvips/foreign/radload.c
libvips/foreign/pdfload.c
libvips/foreign/fitssave.c
libvips/foreign/rawload.c
libvips/foreign/heifload.c
libvips/foreign/jpeg2vips.c
libvips/foreign/vips2jpeg.c
libvips/foreign/pdfiumload.c
libvips/foreign/webpsave.c
libvips/foreign/magick7load.c
libvips/foreign/csvload.c
libvips/foreign/heifsave.c
libvips/foreign/radiance.c
libvips/foreign/pngload.c
libvips/foreign/openslide2vips.c
libvips/foreign/matrixsave.c
libvips/foreign/tiffsave.c
libvips/foreign/magickload.c
libvips/foreign/jpegsave.c
libvips/foreign/webpload.c
libvips/foreign/gifload.c
libvips/foreign/pngsave.c
libvips/foreign/exif.c
libvips/foreign/magick2vips.c
libvips/foreign/openexr2vips.c
libvips/foreign/matload.c
libvips/foreign/vips2webp.c
libvips/foreign/openexrload.c
libvips/foreign/rawsave.c
libvips/foreign/fitsload.c
libvips/foreign/niftisave.c
libvips/foreign/tiff.c
libvips/foreign/quantise.c
libvips/foreign/webp2vips.c
libvips/foreign/vips2tiff.c
libvips/foreign/cairo.c
libvips/foreign/magicksave.c
libvips/foreign/svgload.c
libvips/foreign/jpegload.c
libvips/foreign/vipsload.c
libvips/foreign/matlab.c
libvips/draw/draw_smudge.c
libvips/draw/drawink.c
libvips/draw/draw_circle.c
libvips/draw/draw_flood.c
libvips/draw/draw_rect.c
libvips/foreign/foreign.c
libvips/foreign/csvsave.c
libvips/foreign/webp2vips.c
libvips/foreign/pngload.c
libvips/foreign/matlab.c
libvips/foreign/webpload.c
libvips/foreign/pdfiumload.c
libvips/foreign/pngsave.c
libvips/foreign/magicksave.c
libvips/foreign/niftiload.c
libvips/foreign/tiff2vips.c
libvips/foreign/fitsload.c
libvips/foreign/matrixsave.c
libvips/foreign/vips2webp.c
libvips/foreign/tiff.c
libvips/foreign/ppmsave.c
libvips/foreign/csvload.c
libvips/foreign/vipspng.c
libvips/foreign/heifsave.c
libvips/foreign/vips2jpeg.c
libvips/foreign/ppmload.c
libvips/foreign/magickload.c
libvips/foreign/openexr2vips.c
libvips/foreign/gifload.c
libvips/foreign/magick7load.c
libvips/foreign/openslide2vips.c
libvips/foreign/exif.c
libvips/foreign/fitssave.c
libvips/foreign/pdfload.c
libvips/foreign/heifload.c
libvips/foreign/magick2vips.c
libvips/foreign/fits.c
libvips/foreign/cairo.c
libvips/foreign/openslideload.c
libvips/foreign/rawload.c
libvips/foreign/jpeg2vips.c
libvips/foreign/tiffsave.c
libvips/foreign/svgload.c
libvips/foreign/radsave.c
libvips/foreign/dzsave.c
libvips/foreign/radload.c
libvips/foreign/niftisave.c
libvips/foreign/openexrload.c
libvips/foreign/vipssave.c
libvips/foreign/webpsave.c
libvips/foreign/radiance.c
libvips/foreign/rawsave.c
libvips/foreign/jpegsave.c
libvips/foreign/analyze2vips.c
libvips/foreign/matrixload.c
libvips/foreign/jpegload.c
libvips/foreign/magick.c
libvips/foreign/analyzeload.c
libvips/foreign/vips2tiff.c
libvips/foreign/matload.c
libvips/foreign/quantise.c
libvips/foreign/vipsload.c
libvips/foreign/tiffload.c
libvips/freqfilt/spectrum.c
libvips/freqfilt/phasecor.c
libvips/freqfilt/fwfft.c
libvips/freqfilt/freqmult.c
libvips/freqfilt/freqfilt.c
libvips/freqfilt/invfft.c
libvips/freqfilt/freqmult.c
libvips/freqfilt/spectrum.c
libvips/freqfilt/fwfft.c
libvips/freqfilt/phasecor.c
libvips/histogram/hist_norm.c
libvips/histogram/hist_cum.c
libvips/histogram/histogram.c
libvips/histogram/hist_match.c
libvips/histogram/hist_entropy.c
libvips/histogram/hist_plot.c
libvips/histogram/stdif.c
libvips/histogram/percent.c
libvips/histogram/hist_ismonotonic.c
libvips/histogram/hist_equal.c
libvips/histogram/maplut.c
libvips/histogram/hist_unary.c
libvips/histogram/case.c
libvips/histogram/hist_match.c
libvips/histogram/hist_cum.c
libvips/histogram/hist_equal.c
libvips/histogram/stdif.c
libvips/histogram/histogram.c
libvips/histogram/hist_entropy.c
libvips/histogram/hist_ismonotonic.c
libvips/histogram/hist_norm.c
libvips/histogram/hist_plot.c
libvips/histogram/hist_unary.c
libvips/histogram/hist_local.c
libvips/histogram/percent.c
libvips/histogram/maplut.c
libvips/introspect.c
libvips/iofuncs/target.c
libvips/iofuncs/sinkscreen.c
libvips/iofuncs/vipsmarshal.c
libvips/iofuncs/init.c
libvips/iofuncs/dbuf.c
libvips/iofuncs/buffer.c
libvips/iofuncs/operation.c
libvips/iofuncs/sbuf.c
libvips/iofuncs/sinkmemory.c
libvips/iofuncs/window.c
libvips/iofuncs/reorder.c
libvips/iofuncs/targetcustom.c
libvips/iofuncs/source.c
libvips/iofuncs/memory.c
libvips/iofuncs/sinkdisc.c
libvips/iofuncs/region.c
libvips/iofuncs/rect.c
libvips/iofuncs/util.c
libvips/iofuncs/vips.c
libvips/iofuncs/cache.c
libvips/iofuncs/type.c
libvips/iofuncs/semaphore.c
libvips/iofuncs/gate.c
libvips/iofuncs/sourcecustom.c
libvips/iofuncs/mapfile.c
libvips/iofuncs/sink.c
libvips/iofuncs/enumtypes.c
libvips/iofuncs/vector.c
libvips/iofuncs/header.c
libvips/iofuncs/error.c
libvips/iofuncs/generate.c
libvips/iofuncs/gate.c
libvips/iofuncs/type.c
libvips/iofuncs/image.c
libvips/iofuncs/connection.c
libvips/iofuncs/threadpool.c
libvips/iofuncs/buf.c
libvips/iofuncs/buffer.c
libvips/iofuncs/mapfile.c
libvips/iofuncs/reorder.c
libvips/iofuncs/sbuf.c
libvips/iofuncs/enumtypes.c
libvips/iofuncs/sinkdisc.c
libvips/iofuncs/target.c
libvips/iofuncs/vector.c
libvips/iofuncs/operation.c
libvips/iofuncs/sinkmemory.c
libvips/iofuncs/generate.c
libvips/iofuncs/dbuf.c
libvips/iofuncs/window.c
libvips/iofuncs/ginputsource.c
libvips/iofuncs/targetcustom.c
libvips/iofuncs/sourcecustom.c
libvips/iofuncs/source.c
libvips/iofuncs/system.c
libvips/iofuncs/header.c
libvips/iofuncs/init.c
libvips/iofuncs/rect.c
libvips/iofuncs/region.c
libvips/iofuncs/cache.c
libvips/iofuncs/vips.c
libvips/iofuncs/error.c
libvips/iofuncs/util.c
libvips/iofuncs/semaphore.c
libvips/iofuncs/memory.c
libvips/iofuncs/sinkscreen.c
libvips/iofuncs/object.c
libvips/morphology/labelregions.c
libvips/iofuncs/connection.c
libvips/iofuncs/buf.c
libvips/iofuncs/vipsmarshal.c
libvips/morphology/morph.c
libvips/morphology/nearest.c
libvips/morphology/morphology.c
libvips/morphology/rank.c
libvips/morphology/hitmiss.c
libvips/morphology/countlines.c
libvips/mosaicing/global_balance.c
libvips/mosaicing/im_lrmosaic.c
libvips/mosaicing/im_initialize.c
libvips/mosaicing/im_remosaic.c
libvips/mosaicing/im_improve.c
libvips/mosaicing/im_lrcalcon.c
libvips/mosaicing/mosaic1.c
libvips/mosaicing/im_chkpair.c
libvips/mosaicing/im_tbcalcon.c
libvips/mosaicing/mosaicing.c
libvips/mosaicing/mosaic.c
libvips/mosaicing/im_avgdxdy.c
libvips/mosaicing/im_clinear.c
libvips/mosaicing/merge.c
libvips/mosaicing/im_tbmosaic.c
libvips/mosaicing/match.c
libvips/mosaicing/im_lrmerge.c
libvips/morphology/nearest.c
libvips/morphology/labelregions.c
libvips/morphology/morphology.c
libvips/morphology/hitmiss.c
libvips/mosaicing/im_tbmerge.c
libvips/resample/shrinkh.c
libvips/resample/shrinkv.c
libvips/resample/reduce.c
libvips/resample/transform.c
libvips/resample/resize.c
libvips/resample/mapim.c
libvips/resample/thumbnail.c
libvips/resample/resample.c
libvips/mosaicing/im_improve.c
libvips/mosaicing/im_chkpair.c
libvips/mosaicing/im_lrmosaic.c
libvips/mosaicing/im_tbcalcon.c
libvips/mosaicing/merge.c
libvips/mosaicing/im_remosaic.c
libvips/mosaicing/im_lrcalcon.c
libvips/mosaicing/im_initialize.c
libvips/mosaicing/mosaicing.c
libvips/mosaicing/global_balance.c
libvips/mosaicing/im_avgdxdy.c
libvips/mosaicing/im_lrmerge.c
libvips/mosaicing/mosaic.c
libvips/mosaicing/im_tbmosaic.c
libvips/mosaicing/im_clinear.c
libvips/mosaicing/match.c
libvips/mosaicing/mosaic1.c
libvips/resample/affine.c
libvips/resample/shrinkv.c
libvips/resample/mapim.c
libvips/resample/resize.c
libvips/resample/transform.c
libvips/resample/reduce.c
libvips/resample/shrinkh.c
libvips/resample/resample.c
libvips/resample/quadratic.c
libvips/resample/interpolate.c
libvips/resample/similarity.c
libvips/resample/thumbnail.c
libvips/resample/shrink.c
libvips/resample/similarity.c
libvips/resample/interpolate.c
tools/vips.c
tools/vipsedit.c
tools/vipsheader.c
tools/vipsthumbnail.c
cplusplus/include/vips/VImage8.h
cplusplus/include/vips/VInterpolate8.h
cplusplus/include/vips/VError8.h
cplusplus/include/vips/vips-operators.h
cplusplus/include/vips/VConnection8.h
cplusplus/include/vips/VImage8.h
cplusplus/include/vips/VInterpolate8.h
libvips/arithmetic/binary.h
libvips/arithmetic/unary.h
libvips/arithmetic/parithmetic.h
libvips/arithmetic/hough.h
libvips/arithmetic/nary.h
libvips/arithmetic/statistic.h
libvips/arithmetic/unaryconst.h
libvips/colour/profiles.h
libvips/colour/pcolour.h
libvips/conversion/bandary.h
libvips/conversion/pconversion.h
libvips/convolution/correlation.h
libvips/convolution/pconvolution.h
libvips/create/pmask.h
libvips/create/pcreate.h
libvips/create/point.h
libvips/draw/pdraw.h
libvips/draw/drawink.h
libvips/foreign/tiff.h
libvips/foreign/pforeign.h
libvips/foreign/dbh.h
libvips/foreign/magick.h
libvips/foreign/jpeg.h
libvips/freqfilt/pfreqfilt.h
libvips/histogram/phistogram.h
libvips/histogram/hist_unary.h
libvips/include/vips/freqfilt.h
libvips/include/vips/arithmetic.h
libvips/include/vips/buf.h
libvips/include/vips/histogram.h
libvips/include/vips/intl.h
libvips/include/vips/threadpool.h
libvips/include/vips/operation.h
libvips/include/vips/connection.h
libvips/include/vips/x.h
libvips/include/vips/enumtypes.h
libvips/include/vips/video.h
libvips/include/vips/memory.h
libvips/include/vips/conversion.h
libvips/include/vips/internal.h
libvips/include/vips/histogram.h
libvips/include/vips/cimg_funcs.h
libvips/include/vips/buf.h
libvips/include/vips/thread.h
libvips/include/vips/region.h
libvips/include/vips/mask.h
libvips/include/vips/private.h
libvips/include/vips/interpolate.h
libvips/include/vips/internal.h
libvips/include/vips/basic.h
libvips/include/vips/region.h
libvips/include/vips/foreign.h
libvips/include/vips/gate.h
libvips/include/vips/almostdeprecated.h
libvips/include/vips/dispatch.h
libvips/include/vips/image.h
libvips/include/vips/mosaicing.h
libvips/include/vips/vector.h
libvips/include/vips/cimg_funcs.h
libvips/include/vips/dbuf.h
libvips/include/vips/error.h
libvips/include/vips/connection.h
libvips/include/vips/sbuf.h
libvips/include/vips/type.h
libvips/include/vips/soname.h
libvips/include/vips/vips7compat.h
libvips/include/vips/create.h
libvips/include/vips/generate.h
libvips/include/vips/format.h
libvips/include/vips/util.h
libvips/include/vips/convolution.h
libvips/include/vips/thread.h
libvips/include/vips/rect.h
libvips/include/vips/dispatch.h
libvips/include/vips/version.h
libvips/include/vips/error.h
libvips/include/vips/debug.h
libvips/include/vips/vips.h
libvips/include/vips/morphology.h
libvips/include/vips/resample.h
libvips/include/vips/object.h
libvips/include/vips/vips.h
libvips/include/vips/generate.h
libvips/include/vips/basic.h
libvips/include/vips/inlines.h
libvips/include/vips/transform.h
libvips/include/vips/draw.h
libvips/include/vips/semaphore.h
libvips/include/vips/vips7compat.h
libvips/include/vips/sbuf.h
libvips/include/vips/header.h
libvips/include/vips/soname.h
libvips/include/vips/rect.h
libvips/include/vips/type.h
libvips/include/vips/semaphore.h
libvips/include/vips/image.h
libvips/include/vips/dbuf.h
libvips/include/vips/vector.h
libvips/include/vips/intl.h
libvips/include/vips/gate.h
libvips/include/vips/freqfilt.h
libvips/include/vips/colour.h
libvips/include/vips/mask.h
libvips/include/vips/debug.h
libvips/include/vips/morphology.h
libvips/include/vips/enumtypes.h
libvips/include/vips/deprecated.h
libvips/include/vips/version.h
libvips/iofuncs/sink.h
libvips/iofuncs/vipsmarshal.h
libvips/morphology/pmorphology.h
libvips/mosaicing/pmosaicing.h
libvips/mosaicing/global_balance.h
libvips/resample/presample.h
libvips/resample/templates.h
cplusplus/examples/invert.cpp
cplusplus/examples/avg.cpp
cplusplus/examples/test.cpp
cplusplus/examples/resize.cpp
cplusplus/examples/buffer.cpp
cplusplus/examples/embed.cpp
cplusplus/examples/profile.cpp
cplusplus/examples/test_overloads.cpp
libvips/include/vips/arithmetic.h
libvips/include/vips/threadpool.h
libvips/include/vips/format.h
libvips/include/vips/conversion.h
libvips/include/vips/draw.h
libvips/include/vips/mosaicing.h
libvips/include/vips/util.h
libvips/include/vips/convolution.h
libvips/include/vips/foreign.h
libvips/include/vips/transform.h
libvips/include/vips/memory.h
cplusplus/VConnection.cpp
cplusplus/VError.cpp
cplusplus/VImage.cpp
cplusplus/VInterpolate.cpp
cplusplus/vips-operators.cpp
cplusplus/VConnection.cpp
libvips/conversion/composite.cpp
libvips/resample/reduceh.cpp
libvips/resample/vsqbs.cpp
libvips/resample/lbb.cpp
libvips/resample/nohalo.cpp
libvips/resample/reducev.cpp
libvips/resample/bicubic.cpp
libvips/resample/vsqbs.cpp
libvips/resample/nohalo.cpp
libvips/resample/lbb.cpp
libvips/resample/reducev.cpp

View File

@ -28,7 +28,7 @@ find */* -name "*.cxx" >> po/POTFILES.in
find */* -name "*.cpp" >> po/POTFILES.in
regenerate the list of files to search for strings ... don't forget to
remove autogenerated files, fuzzer sources, deprecated stuff etc.
remove: fuzz/ test/ deprecated/ examples/ non-public includes
intltool-update --pot

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -1,17 +1,24 @@
# vim: set fileencoding=utf-8 :
import filecmp
from functools import reduce
import os
import pytest
import tempfile
import shutil
import pyvips
from helpers import JPEG_FILE, unsigned_formats, \
from helpers import IMAGES, JPEG_FILE, unsigned_formats, \
signed_formats, float_formats, int_formats, \
noncomplex_formats, all_formats, max_value, \
sizeof_format, rot45_angles, rot45_angle_bonds, \
rot_angles, rot_angle_bonds, run_cmp, run_cmp2, \
assert_almost_equal_objects
assert_almost_equal_objects, temp_filename
class TestConversion:
tempdir = None
# run a function on an image,
# 50,50 and 10,10 should have different values on the test image
# don't loop over band elements
@ -37,6 +44,7 @@ class TestConversion:
@classmethod
def setup_class(cls):
cls.tempdir = tempfile.mkdtemp()
im = pyvips.Image.mask_ideal(100, 100, 0.5,
reject=True, optical=True)
cls.colour = (im * [1, 2, 3] + [2, 3, 4]).copy(interpretation="srgb")
@ -46,6 +54,7 @@ class TestConversion:
@classmethod
def teardown_class(cls):
shutil.rmtree(cls.tempdir, ignore_errors=True)
cls.colour = None
cls.mono = None
cls.image = None
@ -738,6 +747,39 @@ class TestConversion:
diff = (after - im).abs().max()
assert diff == 0
def test_autorot(self):
rotation_images = os.path.join(IMAGES, 'rotation')
files = os.listdir(rotation_images)
files.sort()
meta = {
0: {'w': 290, 'h': 442},
1: {'w': 308, 'h': 410},
2: {'w': 308, 'h': 410},
3: {'w': 308, 'h': 410},
4: {'w': 308, 'h': 410},
5: {'w': 231, 'h': 308},
6: {'w': 231, 'h': 308},
7: {'w': 231, 'h': 308},
8: {'w': 231, 'h': 308},
}
i = 0
for f in files:
if '.autorot.' not in f and not f.startswith('.'):
source_filename = os.path.join(rotation_images, f)
actual_filename = temp_filename(self.tempdir, '.jpg')
pyvips.Image.new_from_file(source_filename).autorot().write_to_file(actual_filename)
actual = pyvips.Image.new_from_file(actual_filename)
assert actual.width == meta[i]['w']
assert actual.height == meta[i]['h']
assert actual.get('orientation') if actual.get_typeof('orientation') else None is None
i = i + 1
def test_scaleimage(self):
for fmt in noncomplex_formats:
test = self.colour.cast(fmt)

View File

@ -340,8 +340,12 @@ class TestForeign:
self.colour, 0)
self.save_load_file(".tif", "[tile]", self.colour, 0)
self.save_load_file(".tif", "[tile,pyramid]", self.colour, 0)
self.save_load_file(".tif", "[tile,pyramid,subifd]", self.colour, 0)
self.save_load_file(".tif",
"[tile,pyramid,compression=jpeg]", self.colour, 80)
self.save_load_file(".tif",
"[tile,pyramid,subifd,compression=jpeg]",
self.colour, 80)
self.save_load_file(".tif", "[bigtiff]", self.colour, 0)
self.save_load_file(".tif", "[compression=jpeg]", self.colour, 80)
self.save_load_file(".tif",

View File

@ -24,7 +24,7 @@ bin_SCRIPTS = \
EXTRA_DIST = \
vipsprofile \
vips-8.9 \
vips-8.10 \
light_correct.in \
shrink_width.in \
batch_image_convert.in \