Merge branch 'master' into add-webp-animated
This commit is contained in:
commit
0669cf2a23
@ -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]
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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
|
||||
|
@ -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, ¶ms );
|
||||
|
||||
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
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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 )
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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 ) {
|
||||
|
@ -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 )
|
||||
|
@ -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, "",
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user