Merge branch 'master' into add-webp-animated

This commit is contained in:
John Cupitt 2018-11-23 17:41:09 +00:00
commit 0669cf2a23
16 changed files with 359 additions and 273 deletions

View File

@ -5,6 +5,10 @@
- deprecate thumbnail auto_rotate, add no_rotate [jcupitt]
- implement thumbnail shrink-on-load for openslide images [jcupitt]
- add animated webp support [jcupitt]
- revise vips_cast() to improve behaviour with uint images [erdmann]
- add bandand()/or()/eor() to cplusplus binding [clcaalu]
- implement shrink-on-load for tiff pyramids [jcupitt]
- added vips_image_set_blob_copy() [jcupitt]
23/9/18 started 8.7.1
- update function list in docs [janko-m]

View File

@ -628,6 +628,24 @@ public:
return( round( VIPS_OPERATION_ROUND_RINT, options ) );
}
VImage
bandand( VOption *options = 0 ) const
{
return( bandbool( VIPS_OPERATION_BOOLEAN_AND, options ) );
}
VImage
bandor( VOption *options = 0 ) const
{
return( bandbool( VIPS_OPERATION_BOOLEAN_OR, options ) );
}
VImage
bandeor( VOption *options = 0 ) const
{
return( bandbool( VIPS_OPERATION_BOOLEAN_EOR, options ) );
}
VImage
real( VOption *options = 0 ) const
{

View File

@ -51,6 +51,9 @@
* - add @shift option
* 1/3/16
* - better behaviour for shift of non-int types (thanks apacheark)
* 14/11/18
* - revise for better uint/int clipping [erdmann]
* - remove old overflow/underflow detect
*/
/*
@ -107,84 +110,30 @@ typedef struct _VipsCast {
VipsBandFormat format;
gboolean shift;
int underflow; /* Number of underflows */
int overflow; /* Number of overflows */
} VipsCast;
typedef VipsConversionClass VipsCastClass;
G_DEFINE_TYPE( VipsCast, vips_cast, VIPS_TYPE_CONVERSION );
static void
vips_cast_preeval( VipsImage *image, VipsProgress *progress, VipsCast *cast )
{
cast->overflow = 0;
cast->underflow = 0;
}
static void
vips_cast_posteval( VipsImage *image, VipsProgress *progress, VipsCast *cast )
{
if( cast->overflow ||
cast->underflow )
g_warning( _( "%d underflows and %d overflows detected" ),
cast->underflow, cast->overflow );
}
/* Our sequence value: the region this sequence is using, and two local stats.
/* Various saturated casts. We want to cast int to uchar, for example, and
* clip the range. X is an int.
*/
typedef struct {
VipsRegion *ir; /* Input region */
int underflow; /* Number of underflows */
int overflow; /* Number of overflows */
} VipsCastSequence;
#define CAST_UCHAR( X ) VIPS_CLIP( 0, (X), UCHAR_MAX )
#define CAST_CHAR( X ) VIPS_CLIP( SCHAR_MIN, (X), SCHAR_MAX )
#define CAST_USHORT( X ) VIPS_CLIP( 0, (X), USHRT_MAX )
#define CAST_SHORT( X ) VIPS_CLIP( SHRT_MIN, (X), SHRT_MAX )
/* Destroy a sequence value.
/* We know the source cannot be the same as the dest, so we will only use
* CAST_UINT() for an INT source, and vice versa.
*/
static int
vips_cast_stop( void *vseq, void *a, void *b )
{
VipsCastSequence *seq = (VipsCastSequence *) vseq;
VipsCast *cast = (VipsCast *) b;
/* Add to global stats.
*/
cast->underflow += seq->underflow;
cast->overflow += seq->overflow;
VIPS_FREEF( g_object_unref, seq->ir );
g_free( seq );
return( 0 );
}
/* Make a sequence value.
*/
static void *
vips_cast_start( VipsImage *out, void *a, void *b )
{
VipsImage *in = (VipsImage *) a;
VipsCastSequence *seq;
seq = g_new( VipsCastSequence, 1 );
seq->ir = vips_region_new( in );
seq->overflow = 0;
seq->underflow = 0;
if( !seq->ir ) {
vips_cast_stop( seq, a, b );
return( NULL );
}
return( seq );
}
#define CAST_UINT( X ) VIPS_MAX( 0, (X) )
#define CAST_INT( X ) VIPS_MIN( (X), INT_MAX )
/* Rightshift an integer type, ie. sizeof(ITYPE) > sizeof(OTYPE).
*/
#define VIPS_SHIFT_RIGHT( ITYPE, OTYPE ) { \
#define SHIFT_RIGHT( ITYPE, OTYPE ) { \
ITYPE * restrict p = (ITYPE *) in; \
OTYPE * restrict q = (OTYPE *) out; \
int n = ((int) sizeof( ITYPE ) << 3) - ((int) sizeof( OTYPE ) << 3); \
@ -198,7 +147,7 @@ vips_cast_start( VipsImage *out, void *a, void *b )
/* Leftshift an integer type, ie. sizeof(ITYPE) < sizeof(OTYPE). We need to
* copy the bottom bit up into the fresh new bits
*/
#define VIPS_SHIFT_LEFT( ITYPE, OTYPE ) { \
#define SHIFT_LEFT( ITYPE, OTYPE ) { \
ITYPE * restrict p = (ITYPE *) in; \
OTYPE * restrict q = (OTYPE *) out; \
int n = ((int) sizeof( OTYPE ) << 3) - ((int) sizeof( ITYPE ) << 3); \
@ -209,70 +158,66 @@ vips_cast_start( VipsImage *out, void *a, void *b )
q[x] = (p[x] << n) | (((p[x] & 1) << n) - (p[x] & 1)); \
}
/* Cast int types to an int type.
/* Cast int types to an int type. We need to pass in the type of the
* intermediate value, either uint or int, or we'll have problems with uint
* sources turning -ve.
*/
#define VIPS_CLIP_INT_INT( ITYPE, OTYPE, VIPS_CLIP ) { \
#define CAST_INT_INT( ITYPE, OTYPE, TEMP, CAST ) { \
ITYPE * restrict p = (ITYPE *) in; \
OTYPE * restrict q = (OTYPE *) out; \
\
for( x = 0; x < sz; x++ ) { \
int t = p[x]; \
TEMP t = (TEMP) p[x]; \
\
VIPS_CLIP( t, seq ); \
\
q[x] = t; \
q[x] = CAST( t ); \
} \
}
/* Int to int handling.
*/
#define VIPS_INT_INT( ITYPE, OTYPE, VIPS_CLIP ) { \
#define INT_INT( ITYPE, OTYPE, TEMP, CAST ) { \
if( cast->shift && \
sizeof( ITYPE ) > sizeof( OTYPE ) ) { \
VIPS_SHIFT_RIGHT( ITYPE, OTYPE ); \
SHIFT_RIGHT( ITYPE, OTYPE ); \
} \
else if( cast->shift ) { \
VIPS_SHIFT_LEFT( ITYPE, OTYPE ); \
SHIFT_LEFT( ITYPE, OTYPE ); \
} \
else { \
VIPS_CLIP_INT_INT( ITYPE, OTYPE, VIPS_CLIP ); \
CAST_INT_INT( ITYPE, OTYPE, TEMP, CAST ); \
} \
}
/* Cast float types to an int type.
*/
#define VIPS_CLIP_FLOAT_INT( ITYPE, OTYPE, VIPS_CLIP ) { \
#define CAST_FLOAT_INT( ITYPE, OTYPE, TEMP, CAST ) { \
ITYPE * restrict p = (ITYPE *) in; \
OTYPE * restrict q = (OTYPE *) out; \
\
for( x = 0; x < sz; x++ ) { \
ITYPE v = VIPS_FLOOR( p[x] ); \
TEMP v = VIPS_FLOOR( p[x] ); \
\
VIPS_CLIP( v, seq ); \
\
q[x] = v; \
q[x] = CAST( v ); \
} \
}
/* Cast complex types to an int type. Just take the real part.
*/
#define VIPS_CLIP_COMPLEX_INT( ITYPE, OTYPE, VIPS_CLIP ) { \
#define CAST_COMPLEX_INT( ITYPE, OTYPE, TEMP, CAST ) { \
ITYPE * restrict p = (ITYPE *) in; \
OTYPE * restrict q = (OTYPE *) out; \
\
for( x = 0; x < sz; x++ ) { \
ITYPE v = VIPS_FLOOR( p[0] ); \
TEMP v = VIPS_FLOOR( p[0] ); \
\
p += 2; \
\
VIPS_CLIP( v, seq ); \
\
q[x] = v; \
q[x] = CAST( v ); \
} \
}
/* Cast non-complex types to a float type.
*/
#define VIPS_CLIP_REAL_FLOAT( ITYPE, OTYPE ) { \
#define CAST_REAL_FLOAT( ITYPE, OTYPE ) { \
ITYPE * restrict p = (ITYPE *) in; \
OTYPE * restrict q = (OTYPE *) out; \
\
@ -282,7 +227,7 @@ vips_cast_start( VipsImage *out, void *a, void *b )
/* Cast complex types to a float type ... just take real.
*/
#define VIPS_CLIP_COMPLEX_FLOAT( ITYPE, OTYPE ) { \
#define CAST_COMPLEX_FLOAT( ITYPE, OTYPE ) { \
ITYPE * restrict p = (ITYPE *) in; \
OTYPE * restrict q = (OTYPE *) out; \
\
@ -294,7 +239,7 @@ vips_cast_start( VipsImage *out, void *a, void *b )
/* Cast any non-complex to a complex type ... set imaginary to zero.
*/
#define VIPS_CLIP_REAL_COMPLEX( ITYPE, OTYPE ) { \
#define CAST_REAL_COMPLEX( ITYPE, OTYPE ) { \
ITYPE * restrict p = (ITYPE *) in; \
OTYPE * restrict q = (OTYPE *) out; \
\
@ -307,7 +252,7 @@ vips_cast_start( VipsImage *out, void *a, void *b )
/* Cast any complex to a complex type.
*/
#define VIPS_CLIP_COMPLEX_COMPLEX( ITYPE, OTYPE ) { \
#define CAST_COMPLEX_COMPLEX( ITYPE, OTYPE ) { \
ITYPE * restrict p = (ITYPE *) in; \
OTYPE * restrict q = (OTYPE *) out; \
\
@ -322,27 +267,27 @@ vips_cast_start( VipsImage *out, void *a, void *b )
#define BAND_SWITCH_INNER( ITYPE, INT, FLOAT, COMPLEX ) { \
switch( conversion->out->BandFmt ) { \
case VIPS_FORMAT_UCHAR: \
INT( ITYPE, unsigned char, VIPS_CLIP_UCHAR ); \
INT( ITYPE, unsigned char, unsigned int, CAST_UCHAR ); \
break; \
\
case VIPS_FORMAT_CHAR: \
INT( ITYPE, signed char, VIPS_CLIP_CHAR ); \
INT( ITYPE, signed char, int, CAST_CHAR ); \
break; \
\
case VIPS_FORMAT_USHORT: \
INT( ITYPE, unsigned short, VIPS_CLIP_USHORT ); \
INT( ITYPE, unsigned short, unsigned int, CAST_USHORT ); \
break; \
\
case VIPS_FORMAT_SHORT: \
INT( ITYPE, signed short, VIPS_CLIP_SHORT ); \
INT( ITYPE, signed short, int, CAST_SHORT ); \
break; \
\
case VIPS_FORMAT_UINT: \
INT( ITYPE, unsigned int, VIPS_CLIP_UINT ); \
INT( ITYPE, unsigned int, unsigned int, CAST_UINT ); \
break; \
\
case VIPS_FORMAT_INT: \
INT( ITYPE, signed int, VIPS_CLIP_NONE ); \
INT( ITYPE, signed int, int, CAST_INT ); \
break; \
\
case VIPS_FORMAT_FLOAT: \
@ -367,11 +312,9 @@ vips_cast_start( VipsImage *out, void *a, void *b )
}
static int
vips_cast_gen( VipsRegion *or, void *vseq, void *a, void *b,
gboolean *stop )
vips_cast_gen( VipsRegion *or, void *vseq, void *a, void *b, gboolean *stop )
{
VipsCastSequence *seq = (VipsCastSequence *) vseq;
VipsRegion *ir = seq->ir;
VipsRegion *ir = (VipsRegion *) vseq;
VipsCast *cast = (VipsCast *) b;
VipsConversion *conversion = (VipsConversion *) b;
VipsRect *r = &or->valid;
@ -391,72 +334,72 @@ vips_cast_gen( VipsRegion *or, void *vseq, void *a, void *b,
switch( ir->im->BandFmt ) {
case VIPS_FORMAT_UCHAR:
BAND_SWITCH_INNER( unsigned char,
VIPS_INT_INT,
VIPS_CLIP_REAL_FLOAT,
VIPS_CLIP_REAL_COMPLEX );
INT_INT,
CAST_REAL_FLOAT,
CAST_REAL_COMPLEX );
break;
case VIPS_FORMAT_CHAR:
BAND_SWITCH_INNER( signed char,
VIPS_INT_INT,
VIPS_CLIP_REAL_FLOAT,
VIPS_CLIP_REAL_COMPLEX );
INT_INT,
CAST_REAL_FLOAT,
CAST_REAL_COMPLEX );
break;
case VIPS_FORMAT_USHORT:
BAND_SWITCH_INNER( unsigned short,
VIPS_INT_INT,
VIPS_CLIP_REAL_FLOAT,
VIPS_CLIP_REAL_COMPLEX );
INT_INT,
CAST_REAL_FLOAT,
CAST_REAL_COMPLEX );
break;
case VIPS_FORMAT_SHORT:
BAND_SWITCH_INNER( signed short,
VIPS_INT_INT,
VIPS_CLIP_REAL_FLOAT,
VIPS_CLIP_REAL_COMPLEX );
INT_INT,
CAST_REAL_FLOAT,
CAST_REAL_COMPLEX );
break;
case VIPS_FORMAT_UINT:
BAND_SWITCH_INNER( unsigned int,
VIPS_INT_INT,
VIPS_CLIP_REAL_FLOAT,
VIPS_CLIP_REAL_COMPLEX );
INT_INT,
CAST_REAL_FLOAT,
CAST_REAL_COMPLEX );
break;
case VIPS_FORMAT_INT:
BAND_SWITCH_INNER( signed int,
VIPS_INT_INT,
VIPS_CLIP_REAL_FLOAT,
VIPS_CLIP_REAL_COMPLEX );
INT_INT,
CAST_REAL_FLOAT,
CAST_REAL_COMPLEX );
break;
case VIPS_FORMAT_FLOAT:
BAND_SWITCH_INNER( float,
VIPS_CLIP_FLOAT_INT,
VIPS_CLIP_REAL_FLOAT,
VIPS_CLIP_REAL_COMPLEX );
CAST_FLOAT_INT,
CAST_REAL_FLOAT,
CAST_REAL_COMPLEX );
break;
case VIPS_FORMAT_DOUBLE:
BAND_SWITCH_INNER( double,
VIPS_CLIP_FLOAT_INT,
VIPS_CLIP_REAL_FLOAT,
VIPS_CLIP_REAL_COMPLEX );
CAST_FLOAT_INT,
CAST_REAL_FLOAT,
CAST_REAL_COMPLEX );
break;
case VIPS_FORMAT_COMPLEX:
BAND_SWITCH_INNER( float,
VIPS_CLIP_COMPLEX_INT,
VIPS_CLIP_COMPLEX_FLOAT,
VIPS_CLIP_COMPLEX_COMPLEX );
CAST_COMPLEX_INT,
CAST_COMPLEX_FLOAT,
CAST_COMPLEX_COMPLEX );
break;
case VIPS_FORMAT_DPCOMPLEX:
BAND_SWITCH_INNER( double,
VIPS_CLIP_COMPLEX_INT,
VIPS_CLIP_COMPLEX_FLOAT,
VIPS_CLIP_COMPLEX_COMPLEX );
CAST_COMPLEX_INT,
CAST_COMPLEX_FLOAT,
CAST_COMPLEX_COMPLEX );
break;
default:
@ -513,13 +456,8 @@ vips_cast_build( VipsObject *object )
conversion->out->BandFmt = cast->format;
g_signal_connect( in, "preeval",
G_CALLBACK( vips_cast_preeval ), cast );
g_signal_connect( in, "posteval",
G_CALLBACK( vips_cast_posteval ), cast );
if( vips_image_generate( conversion->out,
vips_cast_start, vips_cast_gen, vips_cast_stop,
vips_start_one, vips_cast_gen, vips_stop_one,
in, cast ) )
return( -1 );

View File

@ -241,7 +241,7 @@ getstr( int mx, const char *str )
g_assert( mx < 256 );
strncpy( buf, str, mx );
vips_strncpy( buf, str, mx );
buf[mx]= '\0';
/* How annoying, patient_id has some funny ctrlchars in that mess up

View File

@ -447,22 +447,6 @@ vips_image_resolution_from_exif( VipsImage *image, ExifData *ed )
return( 0 );
}
static int
vips_exif_get_thumbnail( VipsImage *im, ExifData *ed )
{
if( ed->size > 0 ) {
char *thumb_copy;
thumb_copy = g_malloc( ed->size );
memcpy( thumb_copy, ed->data, ed->size );
vips_image_set_blob( im, "jpeg-thumbnail-data",
(VipsCallbackFn) g_free, thumb_copy, ed->size );
}
return( 0 );
}
/* Need to fwd ref this.
*/
static int
@ -516,7 +500,9 @@ vips__exif_parse( VipsImage *image )
exif_data_foreach_content( ed,
(ExifDataForeachContentFunc) vips_exif_get_content, &params );
vips_exif_get_thumbnail( image, ed );
vips_image_set_blob_copy( image,
"jpeg-thumbnail-data", ed->data, ed->size );
exif_data_free( ed );
/* Orientation handling. ifd0 has the Orientation tag for the main

View File

@ -309,8 +309,6 @@ find_chroma_subsample( struct jpeg_decompress_struct *cinfo )
static int
attach_blob( VipsImage *im, const char *field, void *data, int data_length )
{
char *data_copy;
/* Only use the first one.
*/
if( vips_image_get_typeof( im, field ) ) {
@ -325,11 +323,7 @@ attach_blob( VipsImage *im, const char *field, void *data, int data_length )
printf( "attach_blob: attaching %d bytes of %s\n", data_length, field );
#endif /*DEBUG*/
if( !(data_copy = vips_malloc( NULL, data_length )) )
return( -1 );
memcpy( data_copy, data, data_length );
vips_image_set_blob( im, field,
(VipsCallbackFn) vips_free, data_copy, data_length );
vips_image_set_blob_copy( im, field, data, data_length );
return( 0 );
}

View File

@ -500,14 +500,9 @@ vips_foreign_load_nifti_set_header( VipsForeignLoadNifti *nifti,
for( i = 0; i < nim->num_ext; i++ ) {
nifti1_extension *ext = &nim->ext_list[i];
char *data_copy;
vips_snprintf( txt, 256, "nifti-ext-%d-%d", i, ext->ecode );
if( !(data_copy = vips_malloc( NULL, ext->esize )) )
return( -1 );
memcpy( data_copy, ext->edata, ext->esize );
vips_image_set_blob( out, txt,
(VipsCallbackFn) vips_free, data_copy, ext->esize );
vips_image_set_blob_copy( out, txt, ext->edata, ext->esize );
}
if( nim->ny > 1 )

View File

@ -247,6 +247,7 @@ typedef struct _RtiffHeader {
int sample_format;
gboolean separate;
int orientation;
gboolean premultiplied;
/* Result of TIFFIsTiled().
*/
@ -319,6 +320,10 @@ typedef struct _Rtiff {
* strips or tiles interleaved.
*/
tdata_t contig_buf;
/* The Y we are reading at. Used to verify strip read is sequential.
*/
int y_pos;
} Rtiff;
/* Test for field exists.
@ -492,6 +497,7 @@ rtiff_new( VipsImage *out, int page, int n, gboolean autorotate )
rtiff->memcpy = FALSE;
rtiff->plane_buf = NULL;
rtiff->contig_buf = NULL;
rtiff->y_pos = 0;
g_signal_connect( out, "close",
G_CALLBACK( rtiff_close_cb ), rtiff );
@ -1346,74 +1352,38 @@ rtiff_set_header( Rtiff *rtiff, VipsImage *out )
/* Read any ICC profile.
*/
if( TIFFGetField( rtiff->tiff,
TIFFTAG_ICCPROFILE, &data_length, &data ) &&
data &&
data_length ) {
void *data_copy;
if( !(data_copy = vips_malloc( NULL, data_length )) )
return( -1 );
memcpy( data_copy, data, data_length );
vips_image_set_blob( out, VIPS_META_ICC_NAME,
(VipsCallbackFn) vips_free, data_copy, data_length );
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 ) &&
data &&
data_length ) {
void *data_copy;
if( !(data_copy = vips_malloc( NULL, data_length )) )
return( -1 );
memcpy( data_copy, data, data_length );
vips_image_set_blob( out, VIPS_META_XMP_NAME,
(VipsCallbackFn) vips_free, data_copy, data_length );
TIFFTAG_XMLPACKET, &data_length, &data ) ) {
vips_image_set_blob_copy( out,
VIPS_META_XMP_NAME, data, data_length );
}
/* Read any IPTC metadata.
*/
if( TIFFGetField( rtiff->tiff,
TIFFTAG_RICHTIFFIPTC, &data_length, &data ) &&
data &&
data_length ) {
void *data_copy;
/* libtiff stores IPTC as an array of long, not byte.
*/
data_length *= 4;
if( !(data_copy = vips_malloc( NULL, data_length )) )
return( -1 );
memcpy( data_copy, data, data_length );
vips_image_set_blob( out, VIPS_META_IPTC_NAME,
(VipsCallbackFn) vips_free, data_copy, data_length );
TIFFTAG_RICHTIFFIPTC, &data_length, &data ) ) {
vips_image_set_blob_copy( out,
VIPS_META_IPTC_NAME, data, data_length );
/* Older versions of libvips used this misspelt name :-( attach
* under this name too for compatibility.
*/
if( !(data_copy = vips_malloc( NULL, data_length )) )
return( -1 );
memcpy( data_copy, data, data_length );
vips_image_set_blob( out, "ipct-data",
(VipsCallbackFn) vips_free, data_copy, data_length );
vips_image_set_blob_copy( out, "ipct-data", data, data_length );
}
/* Read any photoshop metadata.
*/
if( TIFFGetField( rtiff->tiff,
TIFFTAG_PHOTOSHOP, &data_length, &data ) &&
data &&
data_length ) {
void *data_copy;
if( !(data_copy = vips_malloc( NULL, data_length )) )
return( -1 );
memcpy( data_copy, data, data_length );
vips_image_set_blob( out, VIPS_META_PHOTOSHOP_NAME,
(VipsCallbackFn) vips_free, data_copy, data_length );
TIFFTAG_PHOTOSHOP, &data_length, &data ) ) {
vips_image_set_blob_copy( out,
VIPS_META_PHOTOSHOP_NAME, data, data_length );
}
/* IMAGEDESCRIPTION often has useful metadata.
@ -1687,6 +1657,29 @@ rtiff_autorotate( Rtiff *rtiff, VipsImage *in, VipsImage **out )
return( 0 );
}
/* Unpremultiply associative alpha, if any.
*/
static int
rtiff_unpremultiply( Rtiff *rtiff, VipsImage *in, VipsImage **out )
{
if( rtiff->header.premultiplied ) {
VipsImage *x;
if( vips_unpremultiply( in, &x, NULL ) ||
vips_cast( x, out, in->BandFmt, NULL ) ) {
g_object_unref( x );
return( -1 );
}
g_object_unref( x );
}
else {
*out = in;
g_object_ref( in );
}
return( 0 );
}
/* Tile-type TIFF reader core - pass in a per-tile transform. Generate into
* the im and do it all partially.
*/
@ -1696,7 +1689,7 @@ rtiff_read_tilewise( Rtiff *rtiff, VipsImage *out )
int tile_width = rtiff->header.tile_width;
int tile_height = rtiff->header.tile_height;
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( out ), 3 );
vips_object_local_array( VIPS_OBJECT( out ), 4 );
#ifdef DEBUG
printf( "tiff2vips: rtiff_read_tilewise\n" );
@ -1750,11 +1743,10 @@ rtiff_read_tilewise( Rtiff *rtiff, VipsImage *out )
"tile_width", tile_width,
"tile_height", tile_height,
"max_tiles", 2 * (1 + t[0]->Xsize / tile_width),
NULL ) )
return( -1 );
if( rtiff_autorotate( rtiff, t[1], &t[2] ) )
return( -1 );
if( vips_image_write( t[2], out ) )
NULL ) ||
rtiff_autorotate( rtiff, t[1], &t[2] ) ||
rtiff_unpremultiply( rtiff, t[2], &t[3] ) ||
vips_image_write( t[3], out ) )
return( -1 );
return( 0 );
@ -1825,8 +1817,9 @@ rtiff_stripwise_generate( VipsRegion *or,
int y;
#ifdef DEBUG
printf( "tiff2vips: read_stripwise_generate: top = %d, height = %d\n",
printf( "rtiff_stripwise_generate: top = %d, height = %d\n",
r->top, r->height );
printf( "rtiff_stripwise_generate: y_top = %d\n", rtiff->y_pos );
#endif /*DEBUG*/
/* We're inside a tilecache where tiles are the full image width, so
@ -1846,6 +1839,15 @@ rtiff_stripwise_generate( VipsRegion *or,
g_assert( r->height ==
VIPS_MIN( rows_per_strip, or->im->Ysize - r->top ) );
/* And check that y_pos is correct. It should be, since we are inside
* a vips_sequential().
*/
if( r->top != rtiff->y_pos ) {
vips_error( "tiff2vips",
_( "out of order read at line %d" ), rtiff->y_pos );
return( -1 );
}
VIPS_GATE_START( "rtiff_stripwise_generate: work" );
y = 0;
@ -1943,12 +1945,17 @@ rtiff_stripwise_generate( VipsRegion *or,
}
y += hit.height;
rtiff->y_pos += hit.height;
}
/* Shut down the input file as soon as we can.
*/
if( r->top + y >= or->im->Ysize )
if( rtiff->y_pos >= or->im->Ysize ) {
#ifdef DEBUG
printf( "rtiff_stripwise_generate: early shutdown\n" );
#endif /*DEBUG*/
rtiff_free( rtiff );
}
VIPS_GATE_STOP( "rtiff_stripwise_generate: work" );
@ -1965,7 +1972,7 @@ static int
rtiff_read_stripwise( Rtiff *rtiff, VipsImage *out )
{
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( out ), 3 );
vips_object_local_array( VIPS_OBJECT( out ), 4 );
#ifdef DEBUG
printf( "tiff2vips: rtiff_read_stripwise\n" );
@ -2050,7 +2057,9 @@ rtiff_read_stripwise( Rtiff *rtiff, VipsImage *out )
"tile_height", rtiff->header.rows_per_strip,
NULL ) ||
rtiff_autorotate( rtiff, t[1], &t[2] ) ||
vips_image_write( t[2], out ) )
rtiff_unpremultiply( rtiff, t[2], &t[3] ) ||
vips_image_write( t[3], out ) )
return( -1 );
return( 0 );
@ -2061,6 +2070,9 @@ rtiff_read_stripwise( Rtiff *rtiff, VipsImage *out )
static int
rtiff_header_read( Rtiff *rtiff, RtiffHeader *header )
{
uint16 extra_samples_count;
uint16 *extra_samples_types;
if( !tfget32( rtiff->tiff, TIFFTAG_IMAGEWIDTH, &header->width ) ||
!tfget32( rtiff->tiff, TIFFTAG_IMAGELENGTH, &header->height ) ||
!tfget16( rtiff->tiff,
@ -2164,6 +2176,11 @@ rtiff_header_read( Rtiff *rtiff, RtiffHeader *header )
header->tile_height = 0;
}
TIFFGetFieldDefaulted( rtiff->tiff, TIFFTAG_EXTRASAMPLES,
&extra_samples_count, &extra_samples_types );
header->premultiplied = extra_samples_count > 0 &&
extra_samples_types[0] == EXTRASAMPLE_ASSOCALPHA;
return( 0 );
}

View File

@ -253,7 +253,7 @@ vips_foreign_load_tiff_buffer_get_flags( VipsForeignLoad *load )
VipsForeignFlags flags;
flags = 0;
if( vips__istifftiled_buffer( buffer->buf->data, buffer->buf->length ) )
if( vips__istifftiled_buffer( buffer->buf->data, buffer->buf->length ) )
flags |= VIPS_FOREIGN_PARTIAL;
else
flags |= VIPS_FOREIGN_SEQUENTIAL;

View File

@ -267,25 +267,33 @@ read_new_filename( VipsImage *out, const char *name, gboolean fail )
return( read );
}
/* Set the png text data as metadata on the vips image.
/* Set the png text data as metadata on the vips image. These are always
* null-terminated strings.
*/
static int
vips__set_text( VipsImage *out, int i, const char *key, const char *text )
{
char name[256];
if( strcmp( key, "XML:com.adobe.xmp") == 0 )
/* Save as an XMP tag.
if( strcmp( key, "XML:com.adobe.xmp" ) == 0 ) {
/* Save as an XMP tag. This must be a BLOB, for compatibility
* for things like the XMP blob that the tiff loader adds.
*
* Note that this will remove the null-termination from the
* string. We must carefully reattach this.
*/
strncpy( name, VIPS_META_XMP_NAME, 256 );
else
/* Save as a comment. Some PNGs have EXIF data as
vips_image_set_blob_copy( out,
VIPS_META_XMP_NAME, text, strlen( text ) );
}
else {
/* Save as a string comment. Some PNGs have EXIF data as
* text segments, but the correct way to support this is with
* png_get_eXIf_1().
*/
vips_snprintf( name, 256, "png-comment-%d-%s", i, key );
vips_image_set_string( out, name, text );
vips_image_set_string( out, name, text );
}
return( 0 );
}
@ -445,19 +453,14 @@ png2vips_header( Read *read, VipsImage *out )
*/
if( png_get_iCCP( read->pPng, read->pInfo,
&name, &compression_type, &profile, &proflen ) ) {
void *profile_copy;
#ifdef DEBUG
printf( "png2vips_header: attaching %d bytes of ICC profile\n",
proflen );
printf( "png2vips_header: name = \"%s\"\n", name );
#endif /*DEBUG*/
if( !(profile_copy = vips_malloc( NULL, proflen )) )
return( -1 );
memcpy( profile_copy, profile, proflen );
vips_image_set_blob( out, VIPS_META_ICC_NAME,
(VipsCallbackFn) vips_free, profile_copy, proflen );
vips_image_set_blob_copy( out,
VIPS_META_ICC_NAME, profile, proflen );
}
/* Sanity-check line size.
@ -1100,13 +1103,22 @@ write_vips( Write *write,
}
if( vips_image_get_typeof( in, VIPS_META_XMP_NAME ) ) {
const char *str;
void *data;
size_t length;
char *str;
if( vips_image_get_string( in, VIPS_META_XMP_NAME, &str ) )
/* XMP is attached as a BLOB with no null-termination. We
* must re-add this.
*/
if( vips_image_get_blob( in,
VIPS_META_XMP_NAME, &data, &length ) )
return( -1 );
str = g_malloc( length + 1 );
vips_strncpy( str, data, length + 1 );
vips__png_set_text( write->pPng, write->pInfo,
"XML:com.adobe.xmp", str );
g_free( str );
}
/* Set any "png-comment-xx-yyy" metadata items.

View File

@ -488,19 +488,10 @@ read_header( Read *read, VipsImage *out )
if( flags & vips__webp_names[i].flags ) {
WebPChunkIterator iter;
void *blob;
WebPDemuxGetChunk( read->demux, webp, 1, &iter );
if( !(blob = vips_malloc( NULL, iter.chunk.size )) ) {
WebPDemuxReleaseChunkIterator( &iter );
return( -1 );
}
memcpy( blob, iter.chunk.bytes, iter.chunk.size );
vips_image_set_blob( out, vips,
(VipsCallbackFn) vips_free,
blob, iter.chunk.size );
vips_image_set_blob_copy( out,
vips, iter.chunk.bytes, iter.chunk.size );
WebPDemuxReleaseChunkIterator( &iter );
}
}

View File

@ -193,8 +193,10 @@ void vips_image_set_area( VipsImage *image,
const char *name, VipsCallbackFn free_fn, void *data );
int vips_image_get_area( const VipsImage *image,
const char *name, void **data );
void vips_image_set_blob( VipsImage *image, const char *name,
VipsCallbackFn free_fn, void *data, size_t length );
void vips_image_set_blob( VipsImage *image,
const char *name, VipsCallbackFn free_fn, void *data, size_t length );
void vips_image_set_blob_copy( VipsImage *image,
const char *name, const void *data, size_t length );
int vips_image_get_blob( const VipsImage *image, const char *name,
void **data, size_t *length );

View File

@ -279,7 +279,7 @@ vips_buf_appendns( VipsBuf *buf, const char *str, int sz )
*/
cpy = VIPS_MIN( n, avail );
strncpy( buf->base + buf->i, str, cpy );
vips_strncpy( buf->base + buf->i, str, cpy );
buf->i += cpy;
if( buf->i >= buf->mx - 4 ) {

View File

@ -26,6 +26,8 @@
* - return header enums as enums, not ints
* - vips_image_get_*() all convert everything to target type if they can
* - rename "field" as "name" in docs
* 21/11/18
* - get_string will allow G_STRING and REF_STRING
*/
/*
@ -1347,8 +1349,8 @@ vips_image_get_area( const VipsImage *image, const char *name, void **data )
* See also: vips_image_get_blob(), vips_image_set().
*/
void
vips_image_set_blob( VipsImage *image, const char *name,
VipsCallbackFn free_fn, void *data, size_t length )
vips_image_set_blob( VipsImage *image,
const char *name, VipsCallbackFn free_fn, void *data, size_t length )
{
GValue value = { 0 };
@ -1358,6 +1360,42 @@ vips_image_set_blob( VipsImage *image, const char *name,
g_value_unset( &value );
}
/**
* vips_image_set_blob_copy: (method)
* @image: image to attach the metadata to
* @name: metadata name
* @data: pointer to area of memory
* @length: length of memory area
*
* Attaches @blob as a metadata item on @image under the name @name, taking
* a copy of the memory area. A convenience function over
* vips_image_set_blob().
*
* See also: vips_image_get_blob(), vips_image_set().
*/
void
vips_image_set_blob_copy( VipsImage *image,
const char *name, const void *data, size_t length )
{
void *data_copy;
if( !data ||
length == 0 )
return;
/* We add an extra, secret null byte at the end, just in case this blob
* is read as a C string. The libtiff reader (for example) attaches
* XMP XML as a blob, for example.
*/
if( !(data_copy = vips_malloc( NULL, length + 1 )) )
return;
memcpy( data_copy, data, length );
((unsigned char *) data_copy)[length] = '\0';
vips_image_set_blob( image,
name, (VipsCallbackFn) vips_free, data_copy, length );
}
/**
* vips_image_get_blob: (method)
* @image: image to get the metadata from
@ -1498,7 +1536,7 @@ vips_image_set_double( VipsImage *image, const char *name, double d )
*
* Gets @out from @im under the name @name.
* The field must be of type
* VIPS_TYPE_REFSTRING.
* G_STRING, VIPS_TYPE_REFSTRING.
*
* Do not free @out.
*
@ -1513,21 +1551,29 @@ vips_image_get_string( const VipsImage *image, const char *name,
const char **out )
{
GValue value = { 0 };
VipsArea *area;
if( vips_image_get( image, name, &value ) )
return( -1 );
if( G_VALUE_TYPE( &value ) != VIPS_TYPE_REF_STRING ) {
if( G_VALUE_TYPE( &value ) == VIPS_TYPE_REF_STRING ) {
VipsArea *area;
area = g_value_get_boxed( &value );
*out = area->data;
}
else if( G_VALUE_TYPE( &value ) == G_TYPE_STRING ) {
*out = g_value_get_string( &value );
}
else {
vips_error( "VipsImage",
_( "field \"%s\" is of type %s, not VipsRefString" ),
name,
g_type_name( G_VALUE_TYPE( &value ) ) );
g_value_unset( &value );
return( -1 );
}
area = g_value_get_boxed( &value );
*out = area->data;
g_value_unset( &value );
return( 0 );
@ -1541,9 +1587,9 @@ vips_image_get_string( const VipsImage *image, const char *name,
*
* Attaches @str as a metadata item on @image as @name.
* A convenience
* function over vips_image_set() using an vips_ref_string.
* function over vips_image_set() using #VIPS_TYPE_REF_STRING.
*
* See also: vips_image_get_double(), vips_image_set(), vips_ref_string
* See also: vips_image_get_double(), vips_image_set().
*/
void
vips_image_set_string( VipsImage *image, const char *name, const char *str )

View File

@ -14,6 +14,8 @@
* 31/10/18
* - deprecate auto_rotate, add no_rotate
* - implement shrink-on-load for openslide images
* 16/11/18
* - implement shrink-on-load for tiff pyramid
*/
/*
@ -107,6 +109,10 @@ typedef struct _VipsThumbnail {
int level_width[MAX_LEVELS];
int level_height[MAX_LEVELS];
/* Try to get n-pages too, for pyr tiff load.
*/
int n_pages;
} VipsThumbnail;
typedef struct _VipsThumbnailClass {
@ -174,6 +180,14 @@ vips_thumbnail_read_header( VipsThumbnail *thumbnail, VipsImage *image )
thumbnail->input_height = image->Ysize;
thumbnail->angle = vips_autorot_get_angle( image );
if( vips_image_get_typeof( image, "n-pages" ) ) {
int n_pages;
if( !vips_image_get_int( image, "n-pages", &n_pages ) )
thumbnail->n_pages =
VIPS_CLIP( 1, n_pages, MAX_LEVELS );
}
/* For openslide, read out the level structure too.
*/
if( vips_isprefix( "VipsForeignLoadOpenslide", thumbnail->loader ) ) {
@ -199,6 +213,55 @@ 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.
*/
static void
vips_thumbnail_get_tiff_pyramid( VipsThumbnail *thumbnail )
{
VipsThumbnailClass *class = VIPS_THUMBNAIL_GET_CLASS( thumbnail );
int i;
for( i = 0; i < thumbnail->n_pages; 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 );
/* Try to sanity-check the size of the pages. Do they look
* like a pyramid?
*/
expected_level_width = thumbnail->input_width / (1 << i);
expected_level_height = thumbnail->input_height / (1 << i);
/* 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: %d layer pyramid detected\n",
thumbnail->n_pages );
#endif /*DEBUG*/
thumbnail->level_count = thumbnail->n_pages;
}
/* Calculate the shrink factor, taking into account auto-rotate, the fit mode,
* and so on.
*
@ -313,7 +376,7 @@ vips_thumbnail_find_jpegshrink( VipsThumbnail *thumbnail,
/* Find the best openslide level.
*/
static int
vips_thumbnail_find_openslidelevel( VipsThumbnail *thumbnail,
vips_thumbnail_find_pyrlevel( VipsThumbnail *thumbnail,
int width, int height )
{
int level;
@ -349,6 +412,12 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
g_info( "input size is %d x %d",
thumbnail->input_width, thumbnail->input_height );
/* For tiff, we need to make a separate get_info() for each page to
* get all the pyramid levels.
*/
if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) )
vips_thumbnail_get_tiff_pyramid( thumbnail );
factor = 1.0;
if( vips_isprefix( "VipsForeignLoadJpeg", thumbnail->loader ) ) {
@ -357,12 +426,12 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
g_info( "loading jpeg with factor %g pre-shrink", factor );
}
else if( vips_isprefix( "VipsForeignLoadOpenslide",
thumbnail->loader ) ) {
factor = vips_thumbnail_find_openslidelevel( thumbnail,
else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ||
vips_isprefix( "VipsForeignLoadOpenslide", thumbnail->loader ) ) {
factor = vips_thumbnail_find_pyrlevel( thumbnail,
thumbnail->input_width, thumbnail->input_height );
g_info( "loading openslide level %g", factor );
g_info( "loading pyr level %g", factor );
}
else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ||
vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ) {
@ -385,6 +454,8 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
if( !(im = class->open( thumbnail, factor )) )
return( NULL );
g_info( "pre-shrunk size is %d x %d", im->Xsize, im->Ysize );
return( im );
}
@ -787,6 +858,12 @@ vips_thumbnail_file_open( VipsThumbnail *thumbnail, double factor )
"scale", 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 ) );
}
else {
return( vips_image_new_from_file( file->filename,
"access", VIPS_ACCESS_SEQUENTIAL,
@ -968,6 +1045,13 @@ vips_thumbnail_buffer_open( VipsThumbnail *thumbnail, double factor )
"scale", factor,
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ) {
return( vips_image_new_from_buffer(
buffer->buf->data, buffer->buf->length, "",
"access", VIPS_ACCESS_SEQUENTIAL,
"page", (int) factor,
NULL ) );
}
else {
return( vips_image_new_from_buffer(
buffer->buf->data, buffer->buf->length, "",

View File

@ -241,7 +241,6 @@ class TestForeign:
assert im.width == 290
assert im.height == 442
assert im.bands == 3
assert im.get("xmp-data") == "sample xmp string"
self.file_loader("pngload", PNG_FILE, png_valid)
self.buffer_loader("pngload_buffer", PNG_FILE, png_valid)