Fix gif dispose handling for DISPOSE_BACKGROUND and DISPOSE_PREVIOUS
- Add 'scratch' field to gif that holds temporary 'scratch buffer' used for rendering frames - For DISPOSE_BACKGROUND: Set background color to transparent instead of 0 - For DISPOSE_BACKGROUND: Write background pixels into scratch after rendering current frame, so it will be used in next frame - For DISPOSE_PREVIOUS: Save frames that are not disposed into 'previous' field in gif, when DISPOSE_PREVIOUS is specified start with that previous frame. see http://webreference.com/content/studio/disposal.html - Add "ANIMEXTS1.0" to Application Extension parser - Graphic Control Extension parser refactor - Compare file contents to expected images for animated gifs in foreign tests
This commit is contained in:
parent
165a3a3855
commit
f88dab9ccd
11
ChangeLog
11
ChangeLog
@ -4,6 +4,17 @@
|
||||
- add max and min to region shrink [rgluskin]
|
||||
- allow \ as an escape character in vips_break_token() [akemrir]
|
||||
- tiffsave has a "depth" param to set max pyr depth
|
||||
- libtiff LOGLUV images load and save as libvips XYZ
|
||||
- add gifload_source
|
||||
- revise vipsthumbnail flags
|
||||
- add VIPS_LEAK env var
|
||||
- add vips_pipe_read_limit_set(), --vips-pipe-read-limit,
|
||||
VIPS_PIPE_READ_LIMIT
|
||||
|
||||
31/1/19 started 8.9.2
|
||||
- fix a deadlock with --vips-leak [DarthSim]
|
||||
- better gifload behaviour for DISPOSAL_UNSPECIFIED [DarthSim]
|
||||
- ban ppm max_value < 0
|
||||
|
||||
20/6/19 started 8.9.1
|
||||
- don't use the new source loaders for new_from_file or new_from_buffer, it
|
||||
|
@ -1640,9 +1640,19 @@ vips_foreign_save_build( VipsObject *object )
|
||||
save->background ) )
|
||||
return( -1 );
|
||||
|
||||
if( save->page_height )
|
||||
if( save->page_height ) {
|
||||
VipsImage *x;
|
||||
|
||||
if( vips_copy( ready, &x, NULL ) ) {
|
||||
VIPS_UNREF( ready );
|
||||
return( -1 );
|
||||
}
|
||||
VIPS_UNREF( ready );
|
||||
ready = x;
|
||||
|
||||
vips_image_set_int( ready,
|
||||
VIPS_META_PAGE_HEIGHT, save->page_height );
|
||||
}
|
||||
|
||||
VIPS_UNREF( save->ready );
|
||||
save->ready = ready;
|
||||
@ -2097,6 +2107,7 @@ vips_foreign_operation_init( void )
|
||||
|
||||
extern GType vips_foreign_load_gif_file_get_type( void );
|
||||
extern GType vips_foreign_load_gif_buffer_get_type( void );
|
||||
extern GType vips_foreign_load_gif_source_get_type( void );
|
||||
|
||||
vips_foreign_load_csv_get_type();
|
||||
vips_foreign_save_csv_get_type();
|
||||
@ -2148,6 +2159,7 @@ vips_foreign_operation_init( void )
|
||||
#ifdef HAVE_GIFLIB
|
||||
vips_foreign_load_gif_file_get_type();
|
||||
vips_foreign_load_gif_buffer_get_type();
|
||||
vips_foreign_load_gif_source_get_type();
|
||||
#endif /*HAVE_GIFLIB*/
|
||||
|
||||
#ifdef HAVE_GSF
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,8 @@
|
||||
* - redone with source/target
|
||||
* - sequential load, plus mmap for filename sources
|
||||
* - faster plus lower memory use
|
||||
* 02/02/2020
|
||||
* - ban max_vaue < 0
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -259,6 +261,12 @@ vips_foreign_load_ppm_parse_header( VipsForeignLoadPpm *ppm )
|
||||
if( get_int( ppm->sbuf, &ppm->max_value ) )
|
||||
return( -1 );
|
||||
|
||||
/* max_value must be > 0 and <= 65535, according to
|
||||
* the spec, but we allow up to 32 bits per pixel.
|
||||
*/
|
||||
if( ppm->max_value < 0 )
|
||||
ppm->max_value = 0;
|
||||
|
||||
if( ppm->max_value > 255 )
|
||||
ppm->bits = 16;
|
||||
if( ppm->max_value > 65535 )
|
||||
|
@ -193,6 +193,8 @@
|
||||
* - switch to source input
|
||||
* 18/11/19
|
||||
* - support ASSOCALPHA in any alpha band
|
||||
* 27/1/20
|
||||
* - read logluv images as XYZ
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -294,6 +296,10 @@ typedef struct _RtiffHeader {
|
||||
*/
|
||||
uint32 read_height;
|
||||
tsize_t read_size;
|
||||
|
||||
/* Scale factor to get absolute cd/m2 from XYZ.
|
||||
*/
|
||||
double stonits;
|
||||
} RtiffHeader;
|
||||
|
||||
/* Scanline-type process function.
|
||||
@ -911,6 +917,52 @@ rtiff_parse_labs( Rtiff *rtiff, VipsImage *out )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* libtiff delivers logluv as illuminant-free 0-1 XYZ in 3 x float.
|
||||
*/
|
||||
static void
|
||||
rtiff_logluv_line( Rtiff *rtiff, VipsPel *q, VipsPel *p, int n, void *dummy )
|
||||
{
|
||||
int samples_per_pixel = rtiff->header.samples_per_pixel;
|
||||
|
||||
float *p1;
|
||||
float *q1;
|
||||
int x;
|
||||
int i;
|
||||
|
||||
p1 = (float *) p;
|
||||
q1 = (float *) q;
|
||||
for( x = 0; x < n; x++ ) {
|
||||
q1[0] = VIPS_D65_X0 * p1[0];
|
||||
q1[1] = VIPS_D65_Y0 * p1[1];
|
||||
q1[2] = VIPS_D65_Z0 * p1[2];
|
||||
|
||||
for( i = 3; i < samples_per_pixel; i++ )
|
||||
q1[i] = p1[i];
|
||||
|
||||
q1 += samples_per_pixel;
|
||||
p1 += samples_per_pixel;
|
||||
}
|
||||
}
|
||||
|
||||
/* LOGLUV images arrive from libtiff as float xyz.
|
||||
*/
|
||||
static int
|
||||
rtiff_parse_logluv( Rtiff *rtiff, VipsImage *out )
|
||||
{
|
||||
if( rtiff_check_min_samples( rtiff, 3 ) ||
|
||||
rtiff_check_interpretation( rtiff, PHOTOMETRIC_LOGLUV ) )
|
||||
return( -1 );
|
||||
|
||||
out->Bands = rtiff->header.samples_per_pixel;
|
||||
out->BandFmt = VIPS_FORMAT_FLOAT;
|
||||
out->Coding = VIPS_CODING_NONE;
|
||||
out->Type = VIPS_INTERPRETATION_XYZ;
|
||||
|
||||
rtiff->sfn = rtiff_logluv_line;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Per-scanline process function for 1 bit images.
|
||||
*/
|
||||
static void
|
||||
@ -1406,6 +1458,9 @@ rtiff_pick_reader( Rtiff *rtiff )
|
||||
return( rtiff_parse_labs );
|
||||
}
|
||||
|
||||
if( photometric_interpretation == PHOTOMETRIC_LOGLUV )
|
||||
return( rtiff_parse_logluv );
|
||||
|
||||
if( photometric_interpretation == PHOTOMETRIC_MINISWHITE ||
|
||||
photometric_interpretation == PHOTOMETRIC_MINISBLACK ) {
|
||||
if( bits_per_sample == 1 )
|
||||
@ -1436,6 +1491,15 @@ rtiff_set_header( Rtiff *rtiff, VipsImage *out )
|
||||
TIFFSetField( rtiff->tiff,
|
||||
TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB );
|
||||
|
||||
/* Ask for LOGLUV as 3 x float XYZ.
|
||||
*/
|
||||
if( rtiff->header.photometric_interpretation == PHOTOMETRIC_LOGLUV ) {
|
||||
TIFFSetField( rtiff->tiff,
|
||||
TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT );
|
||||
|
||||
vips_image_set_double( out, "stonits", rtiff->header.stonits );
|
||||
}
|
||||
|
||||
out->Xsize = rtiff->header.width;
|
||||
out->Ysize = rtiff->header.height * rtiff->n;
|
||||
|
||||
@ -2104,7 +2168,7 @@ rtiff_read_stripwise( Rtiff *rtiff, VipsImage *out )
|
||||
/* Double check: in memcpy mode, the vips linesize should exactly
|
||||
* match the tiff line size.
|
||||
*/
|
||||
if( rtiff->memcpy ) {
|
||||
if( rtiff->memcpy ) {
|
||||
size_t vips_line_size;
|
||||
|
||||
/* Lines are smaller in plane-separated mode.
|
||||
@ -2198,13 +2262,15 @@ rtiff_header_read( Rtiff *rtiff, RtiffHeader *header )
|
||||
|
||||
TIFFGetFieldDefaulted( rtiff->tiff,
|
||||
TIFFTAG_COMPRESSION, &header->compression );
|
||||
|
||||
/* Request YCbCr expansion. libtiff complains if you do this for
|
||||
* non-jpg images. We must set this here since it changes the result
|
||||
* of scanline_size.
|
||||
*/
|
||||
if( header->compression == COMPRESSION_JPEG )
|
||||
/* We want to always expand subsampled YCBCR images to full
|
||||
* RGB.
|
||||
*/
|
||||
TIFFSetField( rtiff->tiff,
|
||||
TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB );
|
||||
else if( header->photometric_interpretation == PHOTOMETRIC_YCBCR ) {
|
||||
else if( header->photometric_interpretation == PHOTOMETRIC_YCBCR ) {
|
||||
/* We rely on the jpg decompressor to upsample chroma
|
||||
* subsampled images. If there is chroma subsampling but
|
||||
* no jpg compression, we have to give up.
|
||||
@ -2223,6 +2289,26 @@ rtiff_header_read( Rtiff *rtiff, RtiffHeader *header )
|
||||
}
|
||||
}
|
||||
|
||||
if( header->photometric_interpretation == PHOTOMETRIC_LOGLUV ) {
|
||||
if( header->compression != COMPRESSION_SGILOG &&
|
||||
header->compression != COMPRESSION_SGILOG24 ) {
|
||||
vips_error( "tiff2vips",
|
||||
"%s", _( "not SGI-compressed LOGLUV" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Always get LOGLUV as 3 x float XYZ. We must set this here
|
||||
* since it'll change the value of scanline_size.
|
||||
*/
|
||||
TIFFSetField( rtiff->tiff,
|
||||
TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT );
|
||||
}
|
||||
|
||||
/* For logluv, the calibration factor to get to absolute luminance.
|
||||
*/
|
||||
if( !TIFFGetField( rtiff->tiff, TIFFTAG_STONITS, &header->stonits ) )
|
||||
header->stonits = 1.0;
|
||||
|
||||
/* Arbitrary sanity-checking limits.
|
||||
*/
|
||||
if( header->width <= 0 ||
|
||||
|
@ -389,7 +389,7 @@ vips_foreign_load_tiff_buffer_build( VipsObject *object )
|
||||
VIPS_AREA( buffer->blob )->length )) )
|
||||
return( -1 );
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_load_tiff_file_parent_class )->
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_load_tiff_buffer_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
@ -456,8 +456,8 @@ vips_foreign_load_tiff_buffer_init( VipsForeignLoadTiffBuffer *buffer )
|
||||
* during load
|
||||
*
|
||||
* Read a TIFF file into a VIPS image. It is a full baseline TIFF 6 reader,
|
||||
* with extensions for tiled images, multipage images, LAB colour space,
|
||||
* pyramidal images and JPEG compression. including CMYK and YCbCr.
|
||||
* with extensions for tiled images, multipage images, XYZ and LAB colour
|
||||
* space, pyramidal images and JPEG compression, including CMYK and YCbCr.
|
||||
*
|
||||
* @page means load this page from the file. By default the first page (page
|
||||
* 0) is read.
|
||||
|
@ -551,6 +551,10 @@ vips_foreign_save_tiff_buffer_init( VipsForeignSaveTiffBuffer *buffer )
|
||||
* good for 1-bit images, and deflate is the best lossless compression TIFF
|
||||
* can do.
|
||||
*
|
||||
* XYZ images are automatically saved as libtiff LOGLUV with SGILOG compression.
|
||||
* Float LAB images are saved as float CIELAB. Set @squash to save as 8-bit
|
||||
* CIELAB.
|
||||
*
|
||||
* Use @Q to set the JPEG compression factor. Default 75.
|
||||
*
|
||||
* User @level to set the ZSTD compression level. Use @lossless to
|
||||
|
@ -189,6 +189,8 @@
|
||||
* - "squash" now squashes 3-band float LAB down to LABQ
|
||||
* 26/1/20
|
||||
* - add "depth" to set pyr depth
|
||||
* 27/1/20
|
||||
* - write XYZ images as logluv
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -608,7 +610,6 @@ wtiff_write_header( Wtiff *wtiff, Layer *layer )
|
||||
{
|
||||
TIFF *tif = layer->tif;
|
||||
|
||||
int format;
|
||||
int orientation;
|
||||
|
||||
/* Output base header fields.
|
||||
@ -702,6 +703,21 @@ wtiff_write_header( Wtiff *wtiff, Layer *layer )
|
||||
photometric = PHOTOMETRIC_CIELAB;
|
||||
colour_bands = 3;
|
||||
}
|
||||
else if( wtiff->input->Type == VIPS_INTERPRETATION_XYZ ) {
|
||||
double stonits;
|
||||
|
||||
photometric = PHOTOMETRIC_LOGLUV;
|
||||
/* Tell libtiff we will write as float XYZ.
|
||||
*/
|
||||
TIFFSetField( tif,
|
||||
TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT );
|
||||
stonits = 1.0;
|
||||
if( vips_image_get_typeof( wtiff->ready, "stonits" ) )
|
||||
vips_image_get_double( wtiff->ready,
|
||||
"stonits", &stonits );
|
||||
TIFFSetField( tif, TIFFTAG_STONITS, stonits );
|
||||
colour_bands = 3;
|
||||
}
|
||||
else if( wtiff->ready->Type == VIPS_INTERPRETATION_CMYK &&
|
||||
wtiff->ready->Bands >= 4 ) {
|
||||
photometric = PHOTOMETRIC_SEPARATED;
|
||||
@ -764,17 +780,23 @@ wtiff_write_header( Wtiff *wtiff, Layer *layer )
|
||||
TIFFSetField( tif, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE );
|
||||
|
||||
/* Sample format.
|
||||
*
|
||||
* Don't set for logluv: libtiff does this for us.
|
||||
*/
|
||||
format = SAMPLEFORMAT_UINT;
|
||||
if( vips_band_format_isuint( wtiff->ready->BandFmt ) )
|
||||
if( wtiff->input->Type != VIPS_INTERPRETATION_XYZ ) {
|
||||
int format;
|
||||
|
||||
format = SAMPLEFORMAT_UINT;
|
||||
else if( vips_band_format_isint( wtiff->ready->BandFmt ) )
|
||||
format = SAMPLEFORMAT_INT;
|
||||
else if( vips_band_format_isfloat( wtiff->ready->BandFmt ) )
|
||||
format = SAMPLEFORMAT_IEEEFP;
|
||||
else if( vips_band_format_iscomplex( wtiff->ready->BandFmt ) )
|
||||
format = SAMPLEFORMAT_COMPLEXIEEEFP;
|
||||
TIFFSetField( tif, TIFFTAG_SAMPLEFORMAT, format );
|
||||
if( vips_band_format_isuint( wtiff->ready->BandFmt ) )
|
||||
format = SAMPLEFORMAT_UINT;
|
||||
else if( vips_band_format_isint( wtiff->ready->BandFmt ) )
|
||||
format = SAMPLEFORMAT_INT;
|
||||
else if( vips_band_format_isfloat( wtiff->ready->BandFmt ) )
|
||||
format = SAMPLEFORMAT_IEEEFP;
|
||||
else if( vips_band_format_iscomplex( wtiff->ready->BandFmt ) )
|
||||
format = SAMPLEFORMAT_COMPLEXIEEEFP;
|
||||
TIFFSetField( tif, TIFFTAG_SAMPLEFORMAT, format );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -1039,6 +1061,11 @@ wtiff_new( VipsImage *input, const char *filename,
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/* XYZ images are written as libtiff LOGLUV.
|
||||
*/
|
||||
if( wtiff->ready->Type == VIPS_INTERPRETATION_XYZ )
|
||||
wtiff->compression = COMPRESSION_SGILOG;
|
||||
|
||||
/* Multipage image?
|
||||
*/
|
||||
if( wtiff->page_height < wtiff->ready->Ysize ) {
|
||||
@ -1331,6 +1358,31 @@ LabS2Lab16( VipsPel *q, VipsPel *p, int n, int samples_per_pixel )
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert VIPS D65 XYZ to TIFF scaled float illuminant-free xyz.
|
||||
*/
|
||||
static void
|
||||
XYZ2tiffxyz( VipsPel *q, VipsPel *p, int n, int samples_per_pixel )
|
||||
{
|
||||
float *p1 = (float *) p;
|
||||
float *q1 = (float *) q;
|
||||
|
||||
int x;
|
||||
|
||||
for( x = 0; x < n; x++ ) {
|
||||
int i;
|
||||
|
||||
q1[0] = p1[0] / VIPS_D65_X0;
|
||||
q1[1] = p1[1] / VIPS_D65_Y0;
|
||||
q1[2] = p1[2] / VIPS_D65_Z0;
|
||||
|
||||
for( i = 3; i < samples_per_pixel; i++ )
|
||||
q1[i] = p1[i];
|
||||
|
||||
q1 += samples_per_pixel;
|
||||
p1 += samples_per_pixel;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pack the pixels in @area from @in into a TIFF tile buffer.
|
||||
*/
|
||||
static void
|
||||
@ -1358,6 +1410,8 @@ wtiff_pack2tiff( Wtiff *wtiff, Layer *layer,
|
||||
LabQ2LabC( q, p, area->width );
|
||||
else if( wtiff->squash )
|
||||
eightbit2onebit( wtiff, q, p, area->width );
|
||||
else if( wtiff->input->Type == VIPS_INTERPRETATION_XYZ )
|
||||
XYZ2tiffxyz( q, p, area->width, in->im->Bands );
|
||||
else if( (in->im->Bands == 1 || in->im->Bands == 2) &&
|
||||
wtiff->miniswhite )
|
||||
invert_band0( wtiff, q, p, area->width );
|
||||
@ -1449,6 +1503,10 @@ wtiff_layer_write_strip( Wtiff *wtiff, Layer *layer, VipsRegion *strip )
|
||||
LabS2Lab16( wtiff->tbuf, p, im->Xsize, im->Bands );
|
||||
p = wtiff->tbuf;
|
||||
}
|
||||
else if( wtiff->input->Type == VIPS_INTERPRETATION_XYZ ) {
|
||||
XYZ2tiffxyz( wtiff->tbuf, p, im->Xsize, im->Bands );
|
||||
p = wtiff->tbuf;
|
||||
}
|
||||
else if( wtiff->squash ) {
|
||||
eightbit2onebit( wtiff, wtiff->tbuf, p, im->Xsize );
|
||||
p = wtiff->tbuf;
|
||||
|
@ -89,6 +89,8 @@ GType vips_connection_get_type( void );
|
||||
const char *vips_connection_filename( VipsConnection *connection );
|
||||
const char *vips_connection_nick( VipsConnection *connection );
|
||||
|
||||
void vips_pipe_read_limit_set( gint64 limit );
|
||||
|
||||
#define VIPS_TYPE_SOURCE (vips_source_get_type())
|
||||
#define VIPS_SOURCE( obj ) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \
|
||||
|
@ -597,6 +597,8 @@ int vips_gifload( const char *filename, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_gifload_buffer( void *buf, size_t len, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_gifload_source( VipsSource *source, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
int vips_heifload( const char *filename, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
|
@ -192,6 +192,8 @@ int vips__view_image( struct _VipsImage *image );
|
||||
*/
|
||||
extern int _vips__argument_id;
|
||||
|
||||
void vips__meta_init( void );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
@ -62,6 +62,7 @@ extern "C" {
|
||||
* @VIPS_REGION_SHRINK_MODE: use the mode
|
||||
* @VIPS_REGION_SHRINK_MAX: use the maximum
|
||||
* @VIPS_REGION_SHRINK_MIN: use the minimum
|
||||
* @VIPS_REGION_SHRINK_NEAREST: use the top-left pixel
|
||||
*
|
||||
* How to calculate the output pixels when shrinking a 2x2 region.
|
||||
*/
|
||||
@ -71,6 +72,7 @@ typedef enum {
|
||||
VIPS_REGION_SHRINK_MODE,
|
||||
VIPS_REGION_SHRINK_MAX,
|
||||
VIPS_REGION_SHRINK_MIN,
|
||||
VIPS_REGION_SHRINK_NEAREST,
|
||||
VIPS_REGION_SHRINK_LAST
|
||||
} VipsRegionShrink;
|
||||
|
||||
|
@ -905,6 +905,7 @@ vips_region_shrink_get_type( void )
|
||||
{VIPS_REGION_SHRINK_MODE, "VIPS_REGION_SHRINK_MODE", "mode"},
|
||||
{VIPS_REGION_SHRINK_MAX, "VIPS_REGION_SHRINK_MAX", "max"},
|
||||
{VIPS_REGION_SHRINK_MIN, "VIPS_REGION_SHRINK_MIN", "min"},
|
||||
{VIPS_REGION_SHRINK_NEAREST, "VIPS_REGION_SHRINK_NEAREST", "nearest"},
|
||||
{VIPS_REGION_SHRINK_LAST, "VIPS_REGION_SHRINK_LAST", "last"},
|
||||
{0, NULL, NULL}
|
||||
};
|
||||
|
@ -36,6 +36,8 @@
|
||||
* - add vips_image_get_n_pages()
|
||||
* 20/6/19
|
||||
* - add vips_image_get/set_array_int()
|
||||
* 31/1/19
|
||||
* - lock for metadata changes
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -130,6 +132,11 @@
|
||||
* these types, it can be copied between images efficiently.
|
||||
*/
|
||||
|
||||
/* Use in various small places where we need a mutex and it's not worth
|
||||
* making a private one.
|
||||
*/
|
||||
static GMutex *vips__meta_lock = NULL;
|
||||
|
||||
/* We have to keep the gtype as a string, since we statically init this.
|
||||
*/
|
||||
typedef struct _HeaderField {
|
||||
@ -934,10 +941,10 @@ meta_cp( VipsImage *dst, const VipsImage *src )
|
||||
/* We lock with vips_image_set() to stop races in highly-
|
||||
* threaded applications.
|
||||
*/
|
||||
g_mutex_lock( vips__global_lock );
|
||||
g_mutex_lock( vips__meta_lock );
|
||||
vips_slist_map2( src->meta_traverse,
|
||||
(VipsSListMap2Fn) meta_cp_field, dst, NULL );
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
g_mutex_unlock( vips__meta_lock );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
@ -1025,13 +1032,6 @@ vips_image_set( VipsImage *image, const char *name, GValue *value )
|
||||
g_assert( name );
|
||||
g_assert( value );
|
||||
|
||||
/* If this image is shared, block metadata changes.
|
||||
*/
|
||||
if( G_OBJECT( image )->ref_count > 1 ) {
|
||||
g_warning( "can't set metadata \"%s\" on shared image", name );
|
||||
return;
|
||||
}
|
||||
|
||||
meta_init( image );
|
||||
|
||||
/* We lock between modifying metadata and copying metadata between
|
||||
@ -1041,9 +1041,9 @@ vips_image_set( VipsImage *image, const char *name, GValue *value )
|
||||
* metadata copy on another -- this can lead to crashes in
|
||||
* highly-threaded applications.
|
||||
*/
|
||||
g_mutex_lock( vips__global_lock );
|
||||
g_mutex_lock( vips__meta_lock );
|
||||
(void) meta_new( image, name, value );
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
g_mutex_unlock( vips__meta_lock );
|
||||
|
||||
/* If we're setting an EXIF data block, we need to automatically expand
|
||||
* out all the tags. This will set things like xres/yres too.
|
||||
@ -1240,14 +1240,6 @@ vips_image_remove( VipsImage *image, const char *name )
|
||||
|
||||
result = FALSE;
|
||||
|
||||
/* If this image is shared, block metadata changes.
|
||||
*/
|
||||
if( G_OBJECT( image )->ref_count > 1 ) {
|
||||
g_warning( "can't remove metadata \"%s\" on shared image",
|
||||
name );
|
||||
return( result );
|
||||
}
|
||||
|
||||
if( image->meta ) {
|
||||
/* We lock between modifying metadata and copying metadata
|
||||
* between images, see meta_cp().
|
||||
@ -1256,9 +1248,9 @@ vips_image_remove( VipsImage *image, const char *name )
|
||||
* racing with metadata copy on another -- this can lead to
|
||||
* crashes in highly-threaded applications.
|
||||
*/
|
||||
g_mutex_lock( vips__global_lock );
|
||||
g_mutex_lock( vips__meta_lock );
|
||||
result = g_hash_table_remove( image->meta, name );
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
g_mutex_unlock( vips__meta_lock );
|
||||
}
|
||||
|
||||
return( result );
|
||||
@ -2027,3 +2019,12 @@ vips_image_get_history( VipsImage *image )
|
||||
|
||||
return( image->Hist ? image->Hist : "" );
|
||||
}
|
||||
|
||||
/* Called during vips_init().
|
||||
*/
|
||||
void
|
||||
vips__meta_init( void )
|
||||
{
|
||||
if( !vips__meta_lock )
|
||||
vips__meta_lock = vips_g_mutex_new();
|
||||
}
|
||||
|
@ -127,6 +127,8 @@ int vips__leak = 0;
|
||||
GQuark vips__image_pixels_quark = 0;
|
||||
#endif /*DEBUG_LEAK*/
|
||||
|
||||
static gint64 vips_pipe_read_limit = 1024 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* vips_get_argv0:
|
||||
*
|
||||
@ -382,6 +384,7 @@ vips_init( const char *argv0 )
|
||||
|
||||
vips__threadpool_init();
|
||||
vips__buffer_init();
|
||||
vips__meta_init();
|
||||
|
||||
/* This does an unsynchronised static hash table init on first call --
|
||||
* we have to make sure we do this single-threaded. See:
|
||||
@ -427,19 +430,20 @@ vips_init( const char *argv0 )
|
||||
g_free( locale );
|
||||
bind_textdomain_codeset( GETTEXT_PACKAGE, "UTF-8" );
|
||||
|
||||
/* Deprecated, this is just for compat.
|
||||
*/
|
||||
if( g_getenv( "VIPS_INFO" ) ||
|
||||
g_getenv( "IM_INFO" ) )
|
||||
vips_info_set( TRUE );
|
||||
|
||||
if( g_getenv( "VIPS_PROFILE" ) )
|
||||
vips_profile_set( TRUE );
|
||||
|
||||
/* Default various settings from env.
|
||||
*/
|
||||
if( g_getenv( "VIPS_LEAK" ) )
|
||||
vips_leak_set( TRUE );
|
||||
if( g_getenv( "VIPS_TRACE" ) )
|
||||
vips_cache_set_trace( TRUE );
|
||||
if( g_getenv( "VIPS_PIPE_READ_LIMIT" ) )
|
||||
vips_pipe_read_limit =
|
||||
g_ascii_strtoll( g_getenv( "VIPS_PIPE_READ_LIMIT" ),
|
||||
NULL, 10 );
|
||||
vips_pipe_read_limit_set( vips_pipe_read_limit );
|
||||
|
||||
/* Register base vips types.
|
||||
*/
|
||||
@ -814,6 +818,9 @@ static GOptionEntry option_entries[] = {
|
||||
{ "vips-version", 0, G_OPTION_FLAG_NO_ARG,
|
||||
G_OPTION_ARG_CALLBACK, (gpointer) &vips_lib_version_cb,
|
||||
N_( "print libvips version" ), NULL },
|
||||
{ "vips-pipe-read-limit", 0, 0,
|
||||
G_OPTION_ARG_INT64, (gpointer) &vips_pipe_read_limit,
|
||||
N_( "read at most this many bytes from a pipe" ), NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@ -1192,8 +1199,8 @@ vips_version( int flag )
|
||||
* vips_leak_set:
|
||||
* @leak: turn leak checking on or off
|
||||
*
|
||||
* Turn on or off vips leak checking. See also --vips-leak and
|
||||
* vips_add_option_entries().
|
||||
* Turn on or off vips leak checking. See also --vips-leak,
|
||||
* vips_add_option_entries() and the `VIPS_LEAK` environment variable.
|
||||
*
|
||||
* You should call this very early in your program.
|
||||
*/
|
||||
|
@ -1235,6 +1235,8 @@ vips_region_shrink_uncoded_mean( VipsRegion *from,
|
||||
* IS stable with respect to the initial arrangement of input values
|
||||
*/
|
||||
#define SHRINK_TYPE_MEDIAN( TYPE ) { \
|
||||
int ls = VIPS_REGION_LSKIP( from ); \
|
||||
\
|
||||
for( x = 0; x < target->width; x++ ) { \
|
||||
TYPE *tp = (TYPE *) p; \
|
||||
TYPE *tp1 = (TYPE *) (p + ls); \
|
||||
@ -1265,6 +1267,8 @@ vips_region_shrink_uncoded_mean( VipsRegion *from,
|
||||
* IS stable with respect to the initial arrangement of input values
|
||||
*/
|
||||
#define SHRINK_TYPE_MODE( TYPE ) { \
|
||||
int ls = VIPS_REGION_LSKIP( from ); \
|
||||
\
|
||||
for( x = 0; x < target->width; x++ ) { \
|
||||
TYPE *tp = (TYPE *) p; \
|
||||
TYPE *tp1 = (TYPE *) (p + ls); \
|
||||
@ -1283,14 +1287,14 @@ vips_region_shrink_uncoded_mean( VipsRegion *from,
|
||||
tq[z] = v[index]; \
|
||||
} \
|
||||
\
|
||||
/* Move on two pels in input. \
|
||||
*/ \
|
||||
p += ps << 1; \
|
||||
q += ps; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SHRINK_TYPE_MAX( TYPE ) { \
|
||||
int ls = VIPS_REGION_LSKIP( from ); \
|
||||
\
|
||||
for( x = 0; x < target->width; x++ ) { \
|
||||
TYPE *tp = (TYPE *) p; \
|
||||
TYPE *tp1 = (TYPE *) (p + ls); \
|
||||
@ -1303,14 +1307,14 @@ vips_region_shrink_uncoded_mean( VipsRegion *from,
|
||||
); \
|
||||
} \
|
||||
\
|
||||
/* Move on two pels in input. \
|
||||
*/ \
|
||||
p += ps << 1; \
|
||||
q += ps; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SHRINK_TYPE_MIN( TYPE ) { \
|
||||
int ls = VIPS_REGION_LSKIP( from ); \
|
||||
\
|
||||
for( x = 0; x < target->width; x++ ) { \
|
||||
TYPE *tp = (TYPE *) p; \
|
||||
TYPE *tp1 = (TYPE *) (p + ls); \
|
||||
@ -1323,8 +1327,19 @@ vips_region_shrink_uncoded_mean( VipsRegion *from,
|
||||
); \
|
||||
} \
|
||||
\
|
||||
/* Move on two pels in input. \
|
||||
*/ \
|
||||
p += ps << 1; \
|
||||
q += ps; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SHRINK_TYPE_NEAREST( TYPE ) { \
|
||||
for( x = 0; x < target->width; x++ ) { \
|
||||
TYPE *tp = (TYPE *) p; \
|
||||
TYPE *tq = (TYPE *) q; \
|
||||
\
|
||||
for( z = 0; z < nb; z++ ) \
|
||||
tq[z] = tp[z]; \
|
||||
\
|
||||
p += ps << 1; \
|
||||
q += ps; \
|
||||
} \
|
||||
@ -1335,7 +1350,6 @@ static void \
|
||||
vips_region_shrink_uncoded_ ## OP( VipsRegion *from, \
|
||||
VipsRegion *to, const VipsRect *target ) \
|
||||
{ \
|
||||
int ls = VIPS_REGION_LSKIP( from ); \
|
||||
int ps = VIPS_IMAGE_SIZEOF_PEL( from->im ); \
|
||||
int nb = from->im->Bands; \
|
||||
\
|
||||
@ -1377,6 +1391,7 @@ VIPS_REGION_SHRINK( MAX );
|
||||
VIPS_REGION_SHRINK( MIN );
|
||||
VIPS_REGION_SHRINK( MODE );
|
||||
VIPS_REGION_SHRINK( MEDIAN );
|
||||
VIPS_REGION_SHRINK( NEAREST );
|
||||
|
||||
/* Generate area @target in @to using pixels in @from. Non-complex.
|
||||
*/
|
||||
@ -1405,6 +1420,10 @@ vips_region_shrink_uncoded( VipsRegion *from,
|
||||
vips_region_shrink_uncoded_MIN( from, to, target );
|
||||
break;
|
||||
|
||||
case VIPS_REGION_SHRINK_NEAREST:
|
||||
vips_region_shrink_uncoded_NEAREST( from, to, target );
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
/* A byte source/sink .. it can be a pipe, file descriptor, memory area,
|
||||
* socket, node.js stream, etc.
|
||||
*
|
||||
* J.Cupitt, 19/6/14
|
||||
* 19/6/14
|
||||
*
|
||||
* 3/2/20
|
||||
* - add vips_pipe_read_limit_set()
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -84,10 +87,40 @@
|
||||
#define MODE_READWRITE BINARYIZE (O_RDWR)
|
||||
#define MODE_WRITE BINARYIZE (O_WRONLY | O_CREAT | O_TRUNC)
|
||||
|
||||
/* -1 on a pipe isn't actually unbounded. Have a limit to prevent
|
||||
* huge sources accidentally filling memory.
|
||||
*
|
||||
* This can be configured with vips_pipe_read_limit_set().
|
||||
*/
|
||||
static gint64 vips__pipe_read_limit = 1024 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* vips_pipe_read_limit_set:
|
||||
* @limit: maximum number of bytes to buffer from a pipe
|
||||
*
|
||||
* If a source does not support mmap or seek and the source is
|
||||
* used with a loader that can only work from memory, then the data will be
|
||||
* automatically read into memory to EOF before the loader starts. This can
|
||||
* produce high memory use if the descriptor represents a large object.
|
||||
*
|
||||
* Use vips_pipe_read_limit_set() to limit the size of object that
|
||||
* will be read in this way. The default is 1GB.
|
||||
*
|
||||
* Set a value of -1 to mean no limit.
|
||||
*
|
||||
* See also: `--vips-pipe-read-limit` and the environment variable
|
||||
* `VIPS_PIPE_READ_LIMIT`.
|
||||
*/
|
||||
void
|
||||
vips_pipe_read_limit_set( gint64 limit )
|
||||
{
|
||||
vips__pipe_read_limit = limit;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE( VipsSource, vips_source, VIPS_TYPE_CONNECTION );
|
||||
|
||||
/* We can't test for seekability or length during _build, since the read and
|
||||
* seek signal handlers may not have been connected yet. Instead, we test
|
||||
* seek signal handlers might not have been connected yet. Instead, we test
|
||||
* when we first need to know.
|
||||
*/
|
||||
static int
|
||||
@ -184,6 +217,9 @@ vips_source_sanity( VipsSource *source )
|
||||
g_assert( source->length == -1 );
|
||||
}
|
||||
else {
|
||||
/* Something like a seekable file.
|
||||
*/
|
||||
|
||||
/* After we're done with the header, the sniff buffer should
|
||||
* be gone.
|
||||
*/
|
||||
@ -377,6 +413,14 @@ vips_source_new_from_descriptor( int descriptor )
|
||||
*
|
||||
* Create an source attached to a file.
|
||||
*
|
||||
* If this descriptor does not support mmap and the source is
|
||||
* used with a loader that can only work from memory, then the data will be
|
||||
* automatically read into memory to EOF before the loader starts. This can
|
||||
* produce high memory use if the descriptor represents a large object.
|
||||
*
|
||||
* Use vips_pipe_read_limit_set() to limit the size of object that
|
||||
* will be read in this way. The default is 1GB.
|
||||
*
|
||||
* Returns: a new source.
|
||||
*/
|
||||
VipsSource *
|
||||
@ -715,19 +759,14 @@ vips_source_read( VipsSource *source, void *buffer, size_t length )
|
||||
return( total_read );
|
||||
}
|
||||
|
||||
/* -1 on a pipe isn't actually unbounded. Have a limit to prevent
|
||||
* huge sources accidentally filling memory.
|
||||
*
|
||||
* 1gb. Why not.
|
||||
*/
|
||||
static const int vips_pipe_read_limit = 1024 * 1024 * 1024;
|
||||
|
||||
/* Read to a position. -1 means read to end of source. Does not change
|
||||
* read_position.
|
||||
*/
|
||||
static int
|
||||
vips_source_pipe_read_to_position( VipsSource *source, gint64 target )
|
||||
{
|
||||
const char *nick = vips_connection_nick( VIPS_CONNECTION( source ) );
|
||||
|
||||
gint64 old_read_position;
|
||||
unsigned char buffer[4096];
|
||||
|
||||
@ -742,7 +781,7 @@ vips_source_pipe_read_to_position( VipsSource *source, gint64 target )
|
||||
(target < 0 ||
|
||||
(source->length != -1 &&
|
||||
target > source->length)) ) {
|
||||
vips_error( vips_connection_nick( VIPS_CONNECTION( source ) ),
|
||||
vips_error( nick,
|
||||
_( "bad read to %" G_GINT64_FORMAT ), target );
|
||||
return( -1 );
|
||||
}
|
||||
@ -760,9 +799,9 @@ vips_source_pipe_read_to_position( VipsSource *source, gint64 target )
|
||||
break;
|
||||
|
||||
if( target == -1 &&
|
||||
source->read_position > vips_pipe_read_limit ) {
|
||||
vips_error( vips_connection_nick( VIPS_CONNECTION( source ) ),
|
||||
"%s", _( "pipe too long" ) );
|
||||
vips__pipe_read_limit != -1 &&
|
||||
source->read_position > vips__pipe_read_limit ) {
|
||||
vips_error( nick, "%s", _( "pipe too long" ) );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
@ -1013,6 +1052,7 @@ vips_source_map_blob( VipsSource *source )
|
||||
gint64
|
||||
vips_source_seek( VipsSource *source, gint64 offset, int whence )
|
||||
{
|
||||
const char *nick = vips_connection_nick( VIPS_CONNECTION( source ) );
|
||||
VipsSourceClass *class = VIPS_SOURCE_GET_CLASS( source );
|
||||
|
||||
gint64 new_pos;
|
||||
@ -1039,8 +1079,7 @@ vips_source_seek( VipsSource *source, gint64 offset, int whence )
|
||||
break;
|
||||
|
||||
default:
|
||||
vips_error( vips_connection_nick( VIPS_CONNECTION( source ) ),
|
||||
"%s", _( "bad 'whence'" ) );
|
||||
vips_error( nick, "%s", _( "bad 'whence'" ) );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
@ -1066,8 +1105,7 @@ vips_source_seek( VipsSource *source, gint64 offset, int whence )
|
||||
break;
|
||||
|
||||
default:
|
||||
vips_error( vips_connection_nick( VIPS_CONNECTION( source ) ),
|
||||
"%s", _( "bad 'whence'" ) );
|
||||
vips_error( nick, "%s", _( "bad 'whence'" ) );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
@ -1081,7 +1119,7 @@ vips_source_seek( VipsSource *source, gint64 offset, int whence )
|
||||
if( new_pos < 0 ||
|
||||
(source->length != -1 &&
|
||||
new_pos > source->length) ) {
|
||||
vips_error( vips_connection_nick( VIPS_CONNECTION( source ) ),
|
||||
vips_error( nick,
|
||||
_( "bad seek to %" G_GINT64_FORMAT ), new_pos );
|
||||
return( -1 );
|
||||
}
|
||||
|
@ -27,6 +27,11 @@ SVG_FILE = os.path.join(IMAGES, "logo.svg")
|
||||
SVGZ_FILE = os.path.join(IMAGES, "logo.svgz")
|
||||
SVG_GZ_FILE = os.path.join(IMAGES, "logo.svg.gz")
|
||||
GIF_ANIM_FILE = os.path.join(IMAGES, "cogs.gif")
|
||||
GIF_ANIM_EXPECTED_PNG_FILE = os.path.join(IMAGES, "cogs.png")
|
||||
GIF_ANIM_DISPOSE_BACKGROUND_FILE = os.path.join(IMAGES, "dispose-background.gif")
|
||||
GIF_ANIM_DISPOSE_BACKGROUND_EXPECTED_PNG_FILE = os.path.join(IMAGES, "dispose-background.png")
|
||||
GIF_ANIM_DISPOSE_PREVIOUS_FILE = os.path.join(IMAGES, "dispose-previous.gif")
|
||||
GIF_ANIM_DISPOSE_PREVIOUS_EXPECTED_PNG_FILE = os.path.join(IMAGES, "dispose-previous.png")
|
||||
DICOM_FILE = os.path.join(IMAGES, "dicom_test_image.dcm")
|
||||
BMP_FILE = os.path.join(IMAGES, "MARBLES.BMP")
|
||||
NIFTI_FILE = os.path.join(IMAGES, "avg152T1_LR_nifti.nii.gz")
|
||||
|
BIN
test/test-suite/images/cogs.png
Normal file
BIN
test/test-suite/images/cogs.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
BIN
test/test-suite/images/dispose-background.gif
Normal file
BIN
test/test-suite/images/dispose-background.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.3 KiB |
BIN
test/test-suite/images/dispose-background.png
Normal file
BIN
test/test-suite/images/dispose-background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
test/test-suite/images/dispose-previous.gif
Normal file
BIN
test/test-suite/images/dispose-previous.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
test/test-suite/images/dispose-previous.png
Normal file
BIN
test/test-suite/images/dispose-previous.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
@ -1,5 +1,5 @@
|
||||
# vim: set fileencoding=utf-8 :
|
||||
|
||||
import filecmp
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
@ -12,6 +12,9 @@ from helpers import \
|
||||
ANALYZE_FILE, GIF_FILE, WEBP_FILE, EXR_FILE, FITS_FILE, OPENSLIDE_FILE, \
|
||||
PDF_FILE, SVG_FILE, SVGZ_FILE, SVG_GZ_FILE, GIF_ANIM_FILE, DICOM_FILE, \
|
||||
BMP_FILE, NIFTI_FILE, ICO_FILE, HEIC_FILE, TRUNCATED_FILE, \
|
||||
GIF_ANIM_EXPECTED_PNG_FILE, \
|
||||
GIF_ANIM_DISPOSE_BACKGROUND_FILE, GIF_ANIM_DISPOSE_BACKGROUND_EXPECTED_PNG_FILE, \
|
||||
GIF_ANIM_DISPOSE_PREVIOUS_FILE, GIF_ANIM_DISPOSE_PREVIOUS_EXPECTED_PNG_FILE, \
|
||||
temp_filename, assert_almost_equal_objects, have, skip_if_no
|
||||
|
||||
|
||||
@ -419,11 +422,14 @@ class TestForeign:
|
||||
assert a.height == b.height
|
||||
assert a.avg() == b.avg()
|
||||
|
||||
# region-shrink added in 8.7
|
||||
x = pyvips.Image.new_from_file(TIF_FILE)
|
||||
buf = x.tiffsave_buffer(tile=True, pyramid=True, region_shrink="mean")
|
||||
buf = x.tiffsave_buffer(tile=True, pyramid=True, region_shrink="mode")
|
||||
buf = x.tiffsave_buffer(tile=True, pyramid=True, region_shrink="median")
|
||||
buf = x.tiffsave_buffer(tile=True, pyramid=True, region_shrink="max")
|
||||
buf = x.tiffsave_buffer(tile=True, pyramid=True, region_shrink="min")
|
||||
buf = x.tiffsave_buffer(tile=True, pyramid=True,
|
||||
region_shrink="nearest")
|
||||
|
||||
@skip_if_no("magickload")
|
||||
def test_magickload(self):
|
||||
@ -703,6 +709,38 @@ class TestForeign:
|
||||
x2 = pyvips.Image.new_from_file(GIF_ANIM_FILE, page=1, n=-1)
|
||||
assert x2.height == 4 * x1.height
|
||||
|
||||
animation = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=-1)
|
||||
filename = temp_filename(self.tempdir, '.png')
|
||||
animation.write_to_file(filename)
|
||||
# Uncomment to see output file
|
||||
# animation.write_to_file('cogs.png')
|
||||
|
||||
assert filecmp.cmp(GIF_ANIM_EXPECTED_PNG_FILE, filename, shallow=False)
|
||||
|
||||
@skip_if_no("gifload")
|
||||
def test_gifload_animation_dispose_background(self):
|
||||
animation = pyvips.Image.new_from_file(GIF_ANIM_DISPOSE_BACKGROUND_FILE, n=-1)
|
||||
|
||||
filename = temp_filename(self.tempdir, '.png')
|
||||
animation.write_to_file(filename)
|
||||
|
||||
# Uncomment to see output file
|
||||
# animation.write_to_file('dispose-background.png')
|
||||
|
||||
assert filecmp.cmp(GIF_ANIM_DISPOSE_BACKGROUND_EXPECTED_PNG_FILE, filename, shallow=False)
|
||||
|
||||
@skip_if_no("gifload")
|
||||
def test_gifload_animation_dispose_previous(self):
|
||||
animation = pyvips.Image.new_from_file(GIF_ANIM_DISPOSE_PREVIOUS_FILE, n=-1)
|
||||
|
||||
filename = temp_filename(self.tempdir, '.png')
|
||||
animation.write_to_file(filename)
|
||||
|
||||
# Uncomment to see output file
|
||||
# animation.write_to_file('dispose-previous.png')
|
||||
|
||||
assert filecmp.cmp(GIF_ANIM_DISPOSE_PREVIOUS_EXPECTED_PNG_FILE, filename, shallow=False)
|
||||
|
||||
@skip_if_no("svgload")
|
||||
def test_svgload(self):
|
||||
def svg_valid(im):
|
||||
|
@ -96,6 +96,10 @@
|
||||
* - add --intent
|
||||
* 23/10/17
|
||||
* - --size Nx didn't work, argh ... thanks jrochkind
|
||||
* 3/2/20
|
||||
* - add --no-rotate
|
||||
* - add --import-profile / --export-profile names
|
||||
* - back to -o for output
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
@ -127,8 +131,8 @@ static char *import_profile = NULL;
|
||||
static gboolean delete_profile = FALSE;
|
||||
static gboolean linear_processing = FALSE;
|
||||
static gboolean crop_image = FALSE;
|
||||
static gboolean no_rotate_image = FALSE;
|
||||
static char *smartcrop_image = NULL;
|
||||
static gboolean rotate_image = FALSE;
|
||||
static char *thumbnail_intent = NULL;
|
||||
|
||||
/* Deprecated and unused.
|
||||
@ -138,25 +142,22 @@ static gboolean nodelete_profile = FALSE;
|
||||
static gboolean verbose = FALSE;
|
||||
static char *convolution_mask = NULL;
|
||||
static char *interpolator = NULL;
|
||||
static gboolean rotate_image = FALSE;
|
||||
|
||||
static GOptionEntry options[] = {
|
||||
{ "size", 's', 0,
|
||||
G_OPTION_ARG_STRING, &thumbnail_size,
|
||||
N_( "shrink to SIZE or to WIDTHxHEIGHT" ),
|
||||
N_( "SIZE" ) },
|
||||
{ "output", 'o', G_OPTION_FLAG_HIDDEN,
|
||||
{ "output", 'o', 0,
|
||||
G_OPTION_ARG_STRING, &output_format,
|
||||
N_( "set output to FORMAT" ),
|
||||
N_( "output to FORMAT" ),
|
||||
N_( "FORMAT" ) },
|
||||
{ "format", 'f', 0,
|
||||
G_OPTION_ARG_STRING, &output_format,
|
||||
N_( "set output format string to FORMAT" ),
|
||||
N_( "FORMAT" ) },
|
||||
{ "eprofile", 'e', 0,
|
||||
{ "export-profile", 'e', 0,
|
||||
G_OPTION_ARG_FILENAME, &export_profile,
|
||||
N_( "export with PROFILE" ),
|
||||
N_( "PROFILE" ) },
|
||||
{ "iprofile", 'i', 0,
|
||||
{ "import-profile", 'i', 0,
|
||||
G_OPTION_ARG_FILENAME, &import_profile,
|
||||
N_( "import untagged images with PROFILE" ),
|
||||
N_( "PROFILE" ) },
|
||||
@ -171,13 +172,28 @@ static GOptionEntry options[] = {
|
||||
G_OPTION_ARG_STRING, &thumbnail_intent,
|
||||
N_( "ICC transform with INTENT" ),
|
||||
N_( "INTENT" ) },
|
||||
{ "rotate", 't', 0,
|
||||
G_OPTION_ARG_NONE, &rotate_image,
|
||||
N_( "auto-rotate" ), NULL },
|
||||
{ "delete", 'd', 0,
|
||||
G_OPTION_ARG_NONE, &delete_profile,
|
||||
N_( "delete profile from exported image" ), NULL },
|
||||
{ "no-rotate", 0, 0,
|
||||
G_OPTION_ARG_NONE, &no_rotate_image,
|
||||
N_( "don't auto-rotate" ), NULL },
|
||||
|
||||
{ "format", 'f', G_OPTION_FLAG_HIDDEN,
|
||||
G_OPTION_ARG_STRING, &output_format,
|
||||
N_( "set output format string to FORMAT" ),
|
||||
N_( "FORMAT" ) },
|
||||
{ "eprofile", 0, G_OPTION_FLAG_HIDDEN,
|
||||
G_OPTION_ARG_FILENAME, &export_profile,
|
||||
N_( "export with PROFILE" ),
|
||||
N_( "PROFILE" ) },
|
||||
{ "iprofile", 0, G_OPTION_FLAG_HIDDEN,
|
||||
G_OPTION_ARG_FILENAME, &import_profile,
|
||||
N_( "import untagged images with PROFILE" ),
|
||||
N_( "PROFILE" ) },
|
||||
{ "rotate", 't', G_OPTION_FLAG_HIDDEN,
|
||||
G_OPTION_ARG_NONE, &rotate_image,
|
||||
N_( "(deprecated, does nothing)" ), NULL },
|
||||
{ "crop", 'c', G_OPTION_FLAG_HIDDEN,
|
||||
G_OPTION_ARG_NONE, &crop_image,
|
||||
N_( "(deprecated, crop exactly to SIZE)" ), NULL },
|
||||
@ -280,11 +296,11 @@ thumbnail_process( VipsObject *process, const char *filename )
|
||||
if( vips_thumbnail( filename, &image, thumbnail_width,
|
||||
"height", thumbnail_height,
|
||||
"size", size_restriction,
|
||||
"auto_rotate", rotate_image,
|
||||
"no-rotate", no_rotate_image,
|
||||
"crop", interesting,
|
||||
"linear", linear_processing,
|
||||
"import_profile", import_profile,
|
||||
"export_profile", export_profile,
|
||||
"import-profile", import_profile,
|
||||
"export-profile", export_profile,
|
||||
"intent", intent,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
|
Loading…
Reference in New Issue
Block a user