Merge branch 'master' into add-libnsgif

This commit is contained in:
John Cupitt 2019-09-02 17:07:49 +01:00
commit b04299aaa2
66 changed files with 1378 additions and 699 deletions

View File

@ -8,6 +8,13 @@
- support webp and zstd compression in tiff
- loaders use "minimise" to close input files earlier
- integrate support for oss-fuzz [omira-sch]
- add vips_switch() / vips_case() ... fast many-way ifthenelse
- better const handling for arithmetic operators fixes comparisons against out
of range values
- handle alpha in heifload / heifsave [meyermarcel]
31/8/19 started 8.8.3
- revert sharpen restoring the input colourspace
9/7/19 started 8.8.2
- better early shutdown in readers
@ -18,6 +25,17 @@
- fix build with GM
- add locks for pdfium load
- fix build with MSVC
- fix a problem with shinkv tail processing [angelmixu]
- fix a read one byte beyond buffer bug in jpegload
- make GIF parsing less strict
- better feof() handling in GIF load
- clip coding and interpretation on vips image read
- check image bounds for GIF load
- prevent over-pre-shrink in thumbnail [kleisauke]
- fix sharpen with sigma 0.5 [2h4dl]
- sharpen restores input colourspace
- verify bands/format for coded images
- improve data_length handling for jpeg metadata
24/5/19 started 8.8.1
- improve realpath() use on older libc

View File

@ -560,18 +560,6 @@ if test x"$expat_found" = x"no"; then
exit 1
fi
# enable vips7 C++ binding ... this defaults off, the vips8 C++ binding
# defaults on
AC_ARG_ENABLE([cpp7],
AS_HELP_STRING([--enable-cpp7],
[enable deprecated vips7 C++ binding (default: no)]),
[enable_cpp7=$enableval
],
[enable_cpp7="no (default)"
]
)
AM_CONDITIONAL(ENABLE_CPP7, [test x"$enable_cpp7" = x"yes"])
# optional supporting libraries
AC_ARG_WITH([gsf],
@ -1124,40 +1112,6 @@ if test x"$with_pangoft2" != x"no"; then
)
fi
# install vips8 python
AC_ARG_ENABLE([pyvips8],
AS_HELP_STRING([--enable-pyvips8],
[install vips8 Python overrides (default: no)]),
[enable_pyvips8=$enableval
],
[enable_pyvips8="no (default)"
]
)
if test x"$enable_pyvips8" = x"auto"; then
PKG_CHECK_EXISTS([pygobject-3.0 >= 3.13.0],
[enable_pyvips8=yes
],
[AC_MSG_WARN([pygobject-3.0 not found; disabling vips8 python support])
enable_pyvips8=no
]
)
fi
if test x"$enable_pyvips8" = x"yes"; then
JD_PATH_PYTHON(2.7,,
[enable_pyvips8=no
AC_MSG_WARN([Python not found; disabling vips8 Python binding])
]
)
fi
if test x"$enable_pyvips8" = x"yes"; then
PKG_CHECK_MODULES(PYGOBJECT, [pygobject-3.0 >= 3.13.0])
fi
AM_CONDITIONAL(ENABLE_PYVIPS8, [test x"$enable_pyvips8" = x"yes"])
# look for TIFF with pkg-config ... fall back to our tester
# pkgconfig support for libtiff starts with libtiff-4
AC_ARG_WITH([tiff],
@ -1462,22 +1416,6 @@ image pyramid export: $with_gsf
use libexif to load/save JPEG metadata: $with_libexif
])
if test x"$found_introspection" = x"yes" -a "$VIPS_LIBDIR/girepository-1.0" != "$INTROSPECTION_TYPELIBDIR"; then
case "$VIPS_LIBDIR" in
/usr/local/Cellar/vips/*)
;; # ignore for homebrew
*)
AC_MSG_RESULT([dnl
Vips-8.0.typelib will be installed to $VIPS_LIBDIR/girepository-1.0, but
your system repository seems to be $INTROSPECTION_TYPELIBDIR.
You may need to add this directory to your typelib path, for example:
export GI_TYPELIB_PATH="$VIPS_LIBDIR/girepository-1.0"
])
;;
esac
fi
if test x"$vips_os_win32" = x"yes"; then
if test x"$have_g_win32_get_command_line" != x"yes"; then
AC_MSG_RESULT([dnl

View File

@ -2,11 +2,14 @@ TESTS = \
test_fuzz.sh
FUZZPROGS = \
jpegsave_file_fuzzer \
jpegsave_buffer_fuzzer \
pngsave_buffer_fuzzer \
webpsave_buffer_fuzzer \
sharpen_fuzzer \
thumbnail_fuzzer
thumbnail_fuzzer \
smartcrop_fuzzer \
mosaic_fuzzer
AM_DEFAULT_SOURCE_EXT = .cc

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

View File

@ -0,0 +1,15 @@
P2
#vips2ppm - Fri Aug 23 12:48:07 2019
10 10
255
96 101 113 118 124 130 136 141 147 150
81 87 98 101 107 112 117 123 130 135
73 78 85 90 95 99 103 110 118 124
46 51 60 67 73 81 87 94 103 109
34 35 40 48 60 69 77 81 85 88
28 26 31 36 45 54 59 64 69 72
32 31 41 39 39 40 45 52 61 66
38 38 47 42 38 36 38 43 49 53
37 38 39 39 37 37 37 36 34 32
36 36 38 36 35 34 35 32 28 25

View File

@ -0,0 +1,10 @@
96 101 113 118 124 130 136 141 147 150
81 87 98 101 107 112 117 123 130 135
73 78 85 90 95 99 103 110 118 124
46 51 60 67 73 81 87 94 103 109
34 35 40 48 60 69 77 81 85 88
28 26 31 36 45 54 59 64 69 72
32 31 41 39 39 40 45 52 61 66
38 38 47 42 38 36 38 43 49 53
37 38 39 39 37 37 37 36 34 32
36 36 38 36 35 34 35 32 28 25
1 96 101 113 118 124 130 136 141 147 150
2 81 87 98 101 107 112 117 123 130 135
3 73 78 85 90 95 99 103 110 118 124
4 46 51 60 67 73 81 87 94 103 109
5 34 35 40 48 60 69 77 81 85 88
6 28 26 31 36 45 54 59 64 69 72
7 32 31 41 39 39 40 45 52 61 66
8 38 38 47 42 38 36 38 43 49 53
9 37 38 39 39 37 37 37 36 34 32
10 36 36 38 36 35 34 35 32 28 25

View File

@ -0,0 +1,11 @@
10 10
96 101 113 118 124 130 136 141 147 150
81 87 98 101 107 112 117 123 130 135
73 78 85 90 95 99 103 110 118 124
46 51 60 67 73 81 87 94 103 109
34 35 40 48 60 69 77 81 85 88
28 26 31 36 45 54 59 64 69 72
32 31 41 39 39 40 45 52 61 66
38 38 47 42 38 36 38 43 49 53
37 38 39 39 37 37 37 36 34 32
36 36 38 36 35 34 35 32 28 25

View File

@ -0,0 +1,6 @@
P5
#vips2ppm - Fri Aug 23 12:47:38 2019
10 10
255
`eqv|ˆ<E2809A>QWbekpu{‡INUZ_cgnv|.3<CIQW^gm"#(0<EMQUX$-6;@EH )''(-4=B&&/*&$&+15%&''%%%$" $$&$#"# 

View File

@ -0,0 +1,6 @@
P6
#vips2ppm - Fri Aug 23 12:47:50 2019
10 10
255
g[ilaoxm{{s€<73>yˆ<E280A0>Œ…—<E280A6>œ•<C593>¤˜”¥\K[`Rai]klaophwtm}yr„wŒ„•‰…TBRYHX^P_aUcf[kh_plcvrjzr‰€y:(6=-:E7DJ>LQDUWL]\SfcZolbzri|.(/)3#.9+8E7FM@QSHYVM`ZQf]Tg' %*$- *6(5=1?C6HG;OL@VOCW,!)!3$+2",0#-1#05(9</AE8LJ=Q1"%/#%7+/3&-/"+- */!04&7:+>>/B.""/##0$&/#'.!(.!*. --.+.)-," ,"!/##-!#,&+'-*+*'(#%

View File

@ -0,0 +1,62 @@
#include <vips/vips.h>
extern "C" int
LLVMFuzzerInitialize( int *argc, char ***argv )
{
vips_concurrency_set( 1 );
return( 0 );
}
static int
test_one_file( const char *name )
{
VipsImage *image;
void *buf;
size_t len;
if( !(image = vips_image_new_from_file( name,
"access", VIPS_ACCESS_SEQUENTIAL,
NULL )) )
return( 0 );
/* Skip big images. They are likely to timeout.
*/
if( image->Xsize > 1024 ||
image->Ysize > 1024 ||
image->Bands > 10 ) {
g_object_unref( image );
return( 0 );
}
if( vips_jpegsave_buffer( image, &buf, &len, NULL ) ) {
g_object_unref( image );
return( 0 );
}
g_free( buf );
g_object_unref( image );
return( 0 );
}
extern "C" int
LLVMFuzzerTestOneInput( const guint8 *data, size_t size )
{
char *name;
if( !(name = vips__temp_name( "%s" )) )
return( 0 );
if( !g_file_set_contents( name, (const char *) data, size, NULL ) ||
test_one_file( name ) ) {
g_unlink( name );
g_free( name );
return( 0 );
}
g_unlink( name );
g_free( name );
return( 0 );
}

63
fuzz/mosaic_fuzzer.cc Normal file
View File

@ -0,0 +1,63 @@
#include <vips/vips.h>
struct mosaic_opt {
guint8 dir : 1;
guint16 xref;
guint16 yref;
guint16 xsec;
guint16 ysec;
};
extern "C" int
LLVMFuzzerInitialize( int *argc, char ***argv )
{
vips_concurrency_set( 1 );
return( 0 );
}
extern "C" int
LLVMFuzzerTestOneInput( const guint8 *data, size_t size )
{
VipsImage *ref, *sec, *out;
struct mosaic_opt *opt;
double d;
if( size < sizeof(struct mosaic_opt) )
return( 0 );
if( !(ref = vips_image_new_from_buffer( data, size, "", NULL )) )
return( 0 );
/* Skip big images. They are likely to timeout.
*/
if( ref->Xsize > 1024 ||
ref->Ysize > 1024 ||
ref->Bands > 10 ) {
g_object_unref( ref );
return( 0 );
}
if( vips_rot180( ref, &sec, NULL ) ) {
g_object_unref( ref );
return( 0 );
}
/* Extract some bytes from the tail to fuzz the arguments of the API.
*/
opt = (struct mosaic_opt *) (data + size - sizeof(struct mosaic_opt));
if( vips_mosaic( ref, sec, &out, (VipsDirection) opt->dir,
opt->xref, opt->yref, opt->xsec, opt->ysec, NULL ) ) {
g_object_unref( sec );
g_object_unref( ref );
return( 0 );
}
vips_max( out, &d, NULL );
g_object_unref( out );
g_object_unref( sec );
g_object_unref( ref );
return( 0 );
}

39
fuzz/smartcrop_fuzzer.cc Normal file
View File

@ -0,0 +1,39 @@
#include <vips/vips.h>
extern "C" int
LLVMFuzzerInitialize( int *argc, char ***argv )
{
vips_concurrency_set( 1 );
return( 0 );
}
extern "C" int
LLVMFuzzerTestOneInput( const guint8 *data, size_t size )
{
VipsImage *image, *out;
double d;
if( !(image = vips_image_new_from_buffer( data, size, "", NULL )) )
return( 0 );
/* Skip big images. They are likely to timeout.
*/
if( image->Xsize > 1024 ||
image->Ysize > 1024 ||
image->Bands > 10 ) {
g_object_unref( image );
return( 0 );
}
if( vips_smartcrop( image, &out, 32, 32, NULL ) ) {
g_object_unref( image );
return( 0 );
}
vips_min( out, &d, NULL );
g_object_unref( out );
g_object_unref( image );
return( 0 );
}

View File

@ -14,7 +14,7 @@ export VIPS_WARNING=0
ret=0
for fuzzer in *_fuzzer; do
find "${fuzzer}_corpus" -type f -not -empty -print0 \
find "common_fuzzer_corpus" -type f -not -empty -print0 \
| xargs -0 -n1 "./$fuzzer" || ret=1
done

View File

@ -458,14 +458,11 @@ vips_boolean_const_build( VipsObject *object )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsUnary *unary = (VipsUnary *) object;
VipsUnaryConst *uconst = (VipsUnaryConst *) object;
if( unary->in &&
vips_check_noncomplex( class->nickname, unary->in ) )
return( -1 );
uconst->const_format = VIPS_FORMAT_INT;
if( VIPS_OBJECT_CLASS( vips_boolean_const_parent_class )->
build( object ) )
return( -1 );
@ -474,9 +471,9 @@ vips_boolean_const_build( VipsObject *object )
}
#define LOOPC( TYPE, OP ) { \
TYPE *p = (TYPE *) in[0]; \
TYPE *q = (TYPE *) out; \
int *c = (int *) uconst->c_ready; \
TYPE * restrict p = (TYPE *) in[0]; \
TYPE * restrict q = (TYPE *) out; \
int * restrict c = uconst->c_int; \
\
for( i = 0, x = 0; x < width; x++ ) \
for( b = 0; b < bands; b++, i++ ) \
@ -484,9 +481,9 @@ vips_boolean_const_build( VipsObject *object )
}
#define FLOOPC( TYPE, OP ) { \
TYPE *p = (TYPE *) in[0]; \
int *q = (int *) out; \
int *c = (int *) uconst->c_ready; \
TYPE * restrict p = (TYPE *) in[0]; \
int * restrict q = (int *) out; \
int * restrict c = uconst->c_int; \
\
for( i = 0, x = 0; x < width; x++ ) \
for( b = 0; b < bands; b++, i++ ) \

View File

@ -338,14 +338,11 @@ vips_math2_const_build( VipsObject *object )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsUnary *unary = (VipsUnary *) object;
VipsUnaryConst *uconst = (VipsUnaryConst *) object;
if( unary->in &&
vips_check_noncomplex( class->nickname, unary->in ) )
return( -1 );
uconst->const_format = VIPS_FORMAT_DOUBLE;
if( VIPS_OBJECT_CLASS( vips_math2_const_parent_class )->
build( object ) )
return( -1 );
@ -356,7 +353,7 @@ vips_math2_const_build( VipsObject *object )
#define LOOPC( IN, OUT, OP ) { \
IN * restrict p = (IN *) in[0]; \
OUT * restrict q = (OUT *) out; \
double * restrict c = (double *) uconst->c_ready; \
double * restrict c = uconst->c_double; \
\
for( i = 0, x = 0; x < width; x++ ) \
for( b = 0; b < bands; b++, i++ ) \

View File

@ -457,25 +457,18 @@ typedef VipsUnaryConstClass VipsRelationalConstClass;
G_DEFINE_TYPE( VipsRelationalConst,
vips_relational_const, VIPS_TYPE_UNARY_CONST );
static int
vips_relational_const_build( VipsObject *object )
{
VipsUnary *unary = (VipsUnary *) object;
VipsUnaryConst *uconst = (VipsUnaryConst *) object;
if( unary->in )
uconst->const_format = unary->in->BandFmt;
if( VIPS_OBJECT_CLASS( vips_relational_const_parent_class )->
build( object ) )
return( -1 );
return( 0 );
#define RLOOPCI( TYPE, OP ) { \
TYPE * restrict p = (TYPE *) in[0]; \
int * restrict c = uconst->c_int; \
\
for( i = 0, x = 0; x < width; x++ ) \
for( b = 0; b < bands; b++, i++ ) \
out[i] = (p[i] OP c[b]) ? 255 : 0; \
}
#define RLOOPC( TYPE, OP ) { \
#define RLOOPCF( TYPE, OP ) { \
TYPE * restrict p = (TYPE *) in[0]; \
TYPE * restrict c = (TYPE *) uconst->c_ready; \
double * restrict c = uconst->c_double; \
\
for( i = 0, x = 0; x < width; x++ ) \
for( b = 0; b < bands; b++, i++ ) \
@ -486,7 +479,7 @@ vips_relational_const_build( VipsObject *object )
TYPE * restrict p = (TYPE *) in[0]; \
\
for( i = 0, x = 0; x < width; x++ ) { \
TYPE * restrict c = (TYPE *) uconst->c_ready; \
double * restrict c = uconst->c_double; \
\
for( b = 0; b < bands; b++, i++ ) { \
out[i] = OP( p[0], p[1], c[0], c[1]) ? 255 : 0; \
@ -505,32 +498,64 @@ vips_relational_const_buffer( VipsArithmetic *arithmetic,
VipsRelationalConst *rconst = (VipsRelationalConst *) arithmetic;
VipsImage *im = arithmetic->ready[0];
int bands = im->Bands;
gboolean is_int = uconst->is_int &&
vips_band_format_isint( im->BandFmt );
int i, x, b;
switch( rconst->relational ) {
case VIPS_OPERATION_RELATIONAL_EQUAL:
SWITCH( RLOOPC, CLOOPC, ==, CEQUAL );
case VIPS_OPERATION_RELATIONAL_EQUAL:
if( is_int ) {
SWITCH( RLOOPCI, CLOOPC, ==, CEQUAL );
}
else {
SWITCH( RLOOPCF, CLOOPC, ==, CEQUAL );
}
break;
case VIPS_OPERATION_RELATIONAL_NOTEQ:
SWITCH( RLOOPC, CLOOPC, !=, CNOTEQ );
if( is_int ) {
SWITCH( RLOOPCI, CLOOPC, !=, CNOTEQ );
}
else {
SWITCH( RLOOPCF, CLOOPC, !=, CNOTEQ );
}
break;
case VIPS_OPERATION_RELATIONAL_LESS:
SWITCH( RLOOPC, CLOOPC, <, CLESS );
case VIPS_OPERATION_RELATIONAL_LESS:
if( is_int ) {
SWITCH( RLOOPCI, CLOOPC, <, CLESS );
}
else {
SWITCH( RLOOPCF, CLOOPC, <, CLESS );
}
break;
case VIPS_OPERATION_RELATIONAL_LESSEQ:
SWITCH( RLOOPC, CLOOPC, <=, CLESSEQ );
case VIPS_OPERATION_RELATIONAL_LESSEQ:
if( is_int ) {
SWITCH( RLOOPCI, CLOOPC, <=, CLESSEQ );
}
else {
SWITCH( RLOOPCF, CLOOPC, <=, CLESSEQ );
}
break;
case VIPS_OPERATION_RELATIONAL_MORE:
SWITCH( RLOOPC, CLOOPC, >, CMORE );
case VIPS_OPERATION_RELATIONAL_MORE:
if( is_int ) {
SWITCH( RLOOPCI, CLOOPC, >, CMORE );
}
else {
SWITCH( RLOOPCF, CLOOPC, >, CMORE );
}
break;
case VIPS_OPERATION_RELATIONAL_MOREEQ:
SWITCH( RLOOPC, CLOOPC, >=, CMOREEQ );
case VIPS_OPERATION_RELATIONAL_MOREEQ:
if( is_int ) {
SWITCH( RLOOPCI, CLOOPC, >=, CMOREEQ );
}
else {
SWITCH( RLOOPCF, CLOOPC, >=, CMOREEQ );
}
break;
default:
@ -551,7 +576,6 @@ vips_relational_const_class_init( VipsRelationalConstClass *class )
object_class->nickname = "relational_const";
object_class->description =
_( "relational operations against a constant" );
object_class->build = vips_relational_const_build;
aclass->process_line = vips_relational_const_buffer;

View File

@ -238,15 +238,11 @@ vips_remainder_const_build( VipsObject *object )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsUnary *unary = (VipsUnary *) object;
VipsUnaryConst *uconst = (VipsUnaryConst *) object;
if( unary->in &&
vips_check_noncomplex( class->nickname, unary->in ) )
return( -1 );
if( unary->in )
uconst->const_format = unary->in->BandFmt;
if( VIPS_OBJECT_CLASS( vips_remainder_const_parent_class )->
build( object ) )
return( -1 );
@ -259,7 +255,7 @@ vips_remainder_const_build( VipsObject *object )
#define IREMAINDERCONST( TYPE ) { \
TYPE * restrict p = (TYPE *) in[0]; \
TYPE * restrict q = (TYPE *) out; \
TYPE * restrict c = (TYPE *) uconst->c_ready; \
int * restrict c = uconst->c_int; \
\
for( i = 0, x = 0; x < width; x++ ) \
for( b = 0; b < bands; b++, i++ ) \
@ -271,7 +267,7 @@ vips_remainder_const_build( VipsObject *object )
#define FREMAINDERCONST( TYPE ) { \
TYPE * restrict p = (TYPE *) in[0]; \
TYPE * restrict q = (TYPE *) out; \
TYPE * restrict c = (TYPE *) uconst->c_ready; \
int * restrict c = uconst->c_int; \
\
for( i = 0, x = 0; x < width; x++ ) \
for( b = 0; b < bands; b++, i++ ) { \

View File

@ -2,6 +2,8 @@
*
* 11/11/11
* - from arith_binary_const
* 21/8/19
* - revise to fix out of range comparisons
*/
/*
@ -49,100 +51,6 @@
G_DEFINE_ABSTRACT_TYPE( VipsUnaryConst, vips_unary_const, VIPS_TYPE_UNARY );
/* Cast a vector of double to a vector of TYPE, clipping to a range.
*/
#define CAST_CLIP( TYPE, N, X ) { \
TYPE * restrict tq = (TYPE *) q; \
\
for( i = 0; i < m; i++ ) { \
double v = p[VIPS_MIN( n - 1, i )]; \
\
tq[i] = (TYPE) VIPS_FCLIP( N, v, X ); \
} \
}
/* Cast a vector of double to a vector of TYPE.
*/
#define CAST( TYPE ) { \
TYPE * restrict tq = (TYPE *) q; \
\
for( i = 0; i < m; i++ ) \
tq[i] = (TYPE) p[VIPS_MIN( n - 1, i )]; \
}
/* Cast a vector of double to a complex vector of TYPE.
*/
#define CASTC( TYPE ) { \
TYPE * restrict tq = (TYPE *) q; \
\
for( i = 0; i < m; i++ ) { \
tq[0] = (TYPE) p[VIPS_MIN( n - 1, i )]; \
tq[1] = 0; \
\
tq += 2; \
} \
}
/* Cast a n-band vector of double to a m-band vector in another format.
*/
static VipsPel *
make_pixel( VipsObject *obj,
int m, VipsBandFormat fmt, int n, double * restrict p )
{
VipsPel *q;
int i;
if( !(q = VIPS_ARRAY( obj, m * vips_format_sizeof( fmt ), VipsPel )) )
return( NULL );
switch( fmt ) {
case VIPS_FORMAT_CHAR:
CAST_CLIP( signed char, SCHAR_MIN, SCHAR_MAX );
break;
case VIPS_FORMAT_UCHAR:
CAST_CLIP( unsigned char, 0, UCHAR_MAX );
break;
case VIPS_FORMAT_SHORT:
CAST_CLIP( signed short, SCHAR_MIN, SCHAR_MAX );
break;
case VIPS_FORMAT_USHORT:
CAST_CLIP( unsigned short, 0, USHRT_MAX );
break;
case VIPS_FORMAT_INT:
CAST_CLIP( signed int, INT_MIN, INT_MAX );
break;
case VIPS_FORMAT_UINT:
CAST_CLIP( unsigned int, 0, UINT_MAX );
break;
case VIPS_FORMAT_FLOAT:
CAST( float );
break;
case VIPS_FORMAT_DOUBLE:
CAST( double );
break;
case VIPS_FORMAT_COMPLEX:
CASTC( float );
break;
case VIPS_FORMAT_DPCOMPLEX:
CASTC( double );
break;
default:
g_assert_not_reached();
}
return( q );
}
static int
vips_unary_const_build( VipsObject *object )
{
@ -161,27 +69,54 @@ vips_unary_const_build( VipsObject *object )
uconst->n = VIPS_MAX( uconst->n, unary->in->Bands );
arithmetic->base_bands = uconst->n;
if( unary->in && uconst->c ) {
if( unary->in &&
uconst->c ) {
if( vips_check_vector( class->nickname,
uconst->c->n, unary->in ) )
return( -1 );
}
/* Some operations need the vector in the input type (eg.
* im_equal_vec() where the output type is always uchar and is useless
* for comparisons), some need it in the output type (eg.
* im_andimage_vec() where we want to get the double to an int so we
* can do bitwise-and without having to cast for each pixel), some
* need a fixed type (eg. im_powtra_vec(), where we want to keep it as
* double).
/* Some operations need int constants, for example boolean AND, SHIFT
* etc.
*
* Therefore pass in the desired vector type as a param.
* Some can use int constants as an optimisation, for example (x <
* 12). It depends on the value though: obviously (x < 12.5) should
* not use the int form.
*
* For complex images, we double the vector length and set the
* imaginary part to 0.
*/
if( uconst->c ) {
gboolean is_complex =
vips_band_format_iscomplex( unary->in->BandFmt );
int step = is_complex ? 2 : 1;
int n = step * uconst->n;
double *c = (double *) uconst->c->data;
if( uconst->c )
uconst->c_ready = make_pixel( (VipsObject *) uconst,
uconst->n, uconst->const_format,
uconst->c->n, (double *) uconst->c->data );
int i;
uconst->c_int = VIPS_ARRAY( object, n, int );
uconst->c_double = VIPS_ARRAY( object, n, double );
if( !uconst->c_int ||
!uconst->c_double )
return( -1 );
memset( uconst->c_int, 0, n * sizeof( int ) );
memset( uconst->c_double, 0, n * sizeof( double ) );
for( i = 0; i < n; i += step )
uconst->c_double[i] =
c[VIPS_MIN( i / step, uconst->c->n - 1)];
for( i = 0; i < n; i += step )
uconst->c_int[i] = uconst->c_double[i];
uconst->is_int = TRUE;
for( i = 0; i < n; i += step )
if( uconst->c_int[i] != uconst->c_double[i] ) {
uconst->is_int = FALSE;
break;
}
}
if( VIPS_OBJECT_CLASS( vips_unary_const_parent_class )->
build( object ) )

View File

@ -59,16 +59,15 @@ typedef struct _VipsUnaryConst {
*/
VipsArea *c;
/* The format the constant should be cast to. Subclasses set this
* ready for unaryconst's build method.
*/
VipsBandFormat const_format;
/* Our constant expanded to match arith->ready in size and
* const_format in type.
/* Our constant expanded to match arith->ready in size. We need int
* and double versions.
*
* is_int is TRUE if the two arrays are equal for every element.
*/
int n;
VipsPel *c_ready;
int *c_int;
double *c_double;
gboolean is_int;
} VipsUnaryConst;

View File

@ -127,7 +127,7 @@ make_hI( void )
for( i = 0; i < 361; i++ ) {
int k;
for( k = 0; k < 360 && hl[j][k] <= i; k++ )
for( k = 1; k < 360 && hl[j][k] <= i; k++ )
;
hI[j][i] = k - 1 + (i - hl[j][k - 1]) /

View File

@ -7,7 +7,9 @@
* - gtkdoc
* - cleanups
* 20/9/12
* redo as a class
* - redo as a class
* 29/8/19
* - avoid /0
*/
/*
@ -67,18 +69,26 @@ vips_Yxy2XYZ_line( VipsColour *colour, VipsPel *out, VipsPel **in, int width )
float x = p[1];
float y = p[2];
double total;
float X, Z;
p += 3;
if( x == 0.0 ||
y == 0.0 ) {
X = 0.0;
Z = 0.0;
}
else {
double total;
total = Y / y;
X = x * total;
Z = (X - x * X - x * Y) / x;
total = Y / y;
X = x * total;
Z = (X - x * X - x * Y) / x;
}
q[0] = X;
q[1] = Y;
q[2] = Z;
p += 3;
q += 3;
}
}

View File

@ -164,17 +164,10 @@ vips_rad2float_line( VipsColour *colour, VipsPel *out, VipsPel **in, int width )
COLR *inp = (COLR *) in[0];
COLOR *outbuf = (COLOR *) out;
colr_color(outbuf[0], inp[0]);
while (--width > 0) {
outbuf++; inp++;
if (inp[0][RED] == inp[-1][RED] &&
inp[0][GRN] == inp[-1][GRN] &&
inp[0][BLU] == inp[-1][BLU] &&
inp[0][EXP] == inp[-1][EXP])
copycolor(outbuf[0], outbuf[-1]);
else
colr_color(outbuf[0], inp[0]);
}
int i;
for( i = 0; i < width; i++ )
colr_color( outbuf[i], inp[i] );
}
static void

View File

@ -1,6 +1,7 @@
noinst_LTLIBRARIES = libconversion.la
libconversion_la_SOURCES = \
switch.c \
transpose3d.c \
composite.cpp \
smartcrop.c \
@ -44,4 +45,10 @@ libconversion_la_SOURCES = \
subsample.c \
zoom.c
AM_CPPFLAGS = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@
# gcc annoyingly warns about clang pragmas, and does not support suppressing
# the warning with a gcc pragma
AM_CPPFLAGS = \
-I${top_srcdir}/libvips/include \
@VIPS_CFLAGS@ @VIPS_INCLUDES@ \
-Wno-unknown-pragmas

View File

@ -383,6 +383,7 @@ vips_conversion_operation_init( void )
extern GType vips_rot45_get_type( void );
extern GType vips_autorot_get_type( void );
extern GType vips_ifthenelse_get_type( void );
extern GType vips_switch_get_type( void );
extern GType vips_recomb_get_type( void );
extern GType vips_bandmean_get_type( void );
extern GType vips_bandfold_get_type( void );
@ -434,6 +435,7 @@ vips_conversion_operation_init( void )
vips_rot45_get_type();
vips_autorot_get_type();
vips_ifthenelse_get_type();
vips_switch_get_type();
vips_recomb_get_type();
vips_bandmean_get_type();
vips_bandfold_get_type();

260
libvips/conversion/switch.c Normal file
View File

@ -0,0 +1,260 @@
/* switch between an array of images
*
* 28/7/19
* - from maplut.c
*/
/*
This file is part of VIPS.
VIPS is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vips/vips.h>
#include <vips/internal.h>
typedef struct _VipsSwitch {
VipsOperation parent_instance;
VipsArrayImage *tests;
VipsImage *out;
int n;
} VipsSwitch;
typedef VipsOperationClass VipsSwitchClass;
G_DEFINE_TYPE( VipsSwitch, vips_switch, VIPS_TYPE_OPERATION );
static int
vips_switch_gen( VipsRegion *or, void *seq, void *a, void *b,
gboolean *stop )
{
VipsRegion **ar = (VipsRegion **) seq;
VipsSwitch *swit = (VipsSwitch *) b;
VipsRect *r = &or->valid;
int x, y, i;
VipsPel * restrict q;
size_t qls;
VipsPel * restrict p[256];
size_t ls[256];
if( vips_reorder_prepare_many( or->im, ar, r ) )
return( -1 );
g_assert( ar->im->BandFmt == VIPS_FORMAT_UCHAR );
g_assert( ar->im->Bands == 1 );
for( i = 0; i < swit->n; i++ ) {
p[i] = VIPS_REGION_ADDR( ar[i], r->left, r->top );
ls[i] = VIPS_REGION_LSKIP( ar[i] );
}
q = VIPS_REGION_ADDR( or, r->left, r->top );
qls = VIPS_REGION_LSKIP( or );
for( y = 0; y < r->height; y++ ) {
for( x = 0; x < r->width; x++ ) {
for( i = 0; i < swit->n; i++ )
if( p[i][x] )
break;
q[x] = i;
}
q += qls;
for( i = 0; i < swit->n; i++ )
p[i] += ls[i];
}
return( 0 );
}
static int
vips_switch_build( VipsObject *object )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsSwitch *swit = (VipsSwitch *) object;
VipsImage **tests;
VipsImage **decode;
VipsImage **format;
VipsImage **band;
VipsImage **size;
int i;
g_object_set( object, "out", vips_image_new(), NULL );
if( VIPS_OBJECT_CLASS( vips_switch_parent_class )->build( object ) )
return( -1 );
/* 255 rather than 256, since we want to reserve +1 as the no
* match value.
*/
tests = vips_area_get_data( &swit->tests->area,
NULL, &swit->n, NULL, NULL );
if( swit->n > 255 ||
swit->n < 1 ) {
vips_error( class->nickname, "%s", _( "bad number of tests" ) );
return( -1 );
}
decode = (VipsImage **) vips_object_local_array( object, swit->n );
format = (VipsImage **) vips_object_local_array( object, swit->n );
band = (VipsImage **) vips_object_local_array( object, swit->n + 1 );
size = (VipsImage **) vips_object_local_array( object, swit->n + 1 );
/* Decode RAD/LABQ etc.
*/
for( i = 0; i < swit->n; i++ )
if( vips_image_decode( tests[i], &decode[i] ) )
return( -1 );
tests = decode;
/* Must be uchar.
*/
for( i = 0; i < swit->n; i++ )
if( vips_cast_uchar( tests[i], &format[i], NULL ) )
return( -1 );
tests = format;
/* Images must match in size and bands.
*/
if( vips__bandalike_vec( class->nickname, tests, band, swit->n, 1 ) ||
vips__sizealike_vec( band, size, swit->n ) )
return( -1 );
tests = size;
if( tests[0]->Bands > 1 ) {
vips_error( class->nickname,
"%s", _( "test images not 1-band" ) );
return( -1 );
}
if( vips_image_pipeline_array( swit->out,
VIPS_DEMAND_STYLE_THINSTRIP, tests ) )
return( -1 );
if( vips_image_generate( swit->out,
vips_start_many, vips_switch_gen, vips_stop_many,
tests, swit ) )
return( -1 );
return( 0 );
}
static void
vips_switch_class_init( VipsSwitchClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class );
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "switch";
object_class->description =
_( "find the index of the first non-zero pixel in tests" );
object_class->build = vips_switch_build;
operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
VIPS_ARG_BOXED( class, "tests", 1,
_( "Tests" ),
_( "Table of images to test" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsSwitch, tests ),
VIPS_TYPE_ARRAY_IMAGE );
VIPS_ARG_IMAGE( class, "out", 2,
_( "Output" ),
_( "Output image" ),
VIPS_ARGUMENT_REQUIRED_OUTPUT,
G_STRUCT_OFFSET( VipsSwitch, out ) );
}
static void
vips_switch_init( VipsSwitch *swit )
{
}
static int
vips_switchv( VipsImage **tests, VipsImage **out, int n, va_list ap )
{
VipsArrayImage *tests_array;
int result;
tests_array = vips_array_image_new( tests, n );
result = vips_call_split( "switch", ap, tests_array, out );
vips_area_unref( VIPS_AREA( tests_array ) );
return( result );
}
/**
* vips_switch: (method)
* @tests: (array length=n): test these images
* @out: (out): output index image
* @n: number of input images
* @...: %NULL-terminated list of optional named arguments
*
* The @tests images are evaluated and at each point the index of the first
* non-zero value is written to @out. If all @tests are false, the value
* (@n + 1) is written.
*
* Images in @tests must have one band. They are expanded to the
* bounding box of the set of images in @tests, and that size is used for
* @out. @tests can have up to 255 elements.
*
* Combine with vips_case() to make an efficient multi-way vips_ifthenelse().
*
* See also: vips_maplut(), vips_case(), vips_ifthenelse().
*
* Returns: 0 on success, -1 on error
*/
int
vips_switch( VipsImage **tests, VipsImage **out, int n, ... )
{
va_list ap;
int result;
va_start( ap, n );
result = vips_switchv( tests, out, n, ap );
va_end( ap );
return( result );
}

View File

@ -39,6 +39,9 @@
* - swap "radius" for "sigma", allows finer control
* - allow a much greater range of parameters
* - move to defaults suitable for screen output
* 28/8/19
* - fix sigma 0.5 case (thanks 2h4dl)
* - restore input colourspace
*/
/*
@ -170,10 +173,11 @@ vips_sharpen_build( VipsObject *object )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsSharpen *sharpen = (VipsSharpen *) object;
VipsImage **t = (VipsImage **) vips_object_local_array( object, 7 );
VipsImage **t = (VipsImage **) vips_object_local_array( object, 8 );
VipsImage **args = (VipsImage **) vips_object_local_array( object, 2 );
VipsImage *in;
VipsInterpretation old_interpretation;
int i;
VIPS_GATE_START( "vips_sharpen_build: build" );
@ -190,6 +194,7 @@ vips_sharpen_build( VipsObject *object )
in = sharpen->in;
old_interpretation = in->Type;
if( vips_colourspace( in, &t[0], VIPS_INTERPRETATION_LABS, NULL ) )
return( -1 );
in = t[0];
@ -199,10 +204,10 @@ vips_sharpen_build( VipsObject *object )
vips_check_format( class->nickname, in, VIPS_FORMAT_SHORT ) )
return( -1 );
/* Stop at 20% of max ... a bit mean. We always sharpen a short,
/* Stop at 10% of max ... a bit mean. We always sharpen a short,
* so there's no point using a float mask.
*/
if( vips_gaussmat( &t[1], sharpen->sigma, 0.2,
if( vips_gaussmat( &t[1], sharpen->sigma, 0.1,
"separable", TRUE,
"precision", VIPS_PRECISION_INTEGER,
NULL ) )
@ -269,9 +274,6 @@ vips_sharpen_build( VipsObject *object )
NULL ) )
return( -1 );
/* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause
* too many recalculations on overlaps.
*/
t[5] = vips_image_new();
if( vips_image_pipeline_array( t[5],
VIPS_DEMAND_STYLE_FATSTRIP, args ) )
@ -287,7 +289,8 @@ vips_sharpen_build( VipsObject *object )
/* Reattach the rest.
*/
if( vips_bandjoin2( t[5], t[3], &t[6], NULL ) ||
vips_image_write( t[6], sharpen->out ) )
vips_colourspace( t[6], &t[7], old_interpretation, NULL ) ||
vips_image_write( t[7], sharpen->out ) )
return( -1 );
VIPS_GATE_STOP( "vips_sharpen_build: build" );

View File

@ -237,14 +237,25 @@ vips_exif_get_double( ExifData *ed,
{
ExifRational rv;
ExifSRational srv;
double value;
if( !vips_exif_get_rational( ed, entry, component, &rv ) )
*out = (double) rv.numerator / rv.denominator;
else if( !vips_exif_get_srational( ed, entry, component, &srv ) )
*out = (double) srv.numerator / srv.denominator;
if( !vips_exif_get_rational( ed, entry, component, &rv ) ) {
if( rv.denominator == 0 )
value = 0;
else
value = (double) rv.numerator / rv.denominator;
}
else if( !vips_exif_get_srational( ed, entry, component, &srv ) ) {
if( srv.denominator == 0 )
value = 0;
else
value = (double) srv.numerator / srv.denominator;
}
else
return( -1 );
*out = value;
return( 0 );
}
@ -681,7 +692,11 @@ vips_exif_set_double( ExifData *ed,
ExifRational rv;
rv = exif_get_rational( entry->data + offset, bo );
old_value = (double) rv.numerator / rv.denominator;
if( rv.denominator == 0 )
old_value = 0;
else
old_value = (double) rv.numerator / rv.denominator;
if( VIPS_FABS( old_value - value ) > 0.0001 ) {
vips_exif_double_to_rational( value, &rv );
@ -696,7 +711,11 @@ vips_exif_set_double( ExifData *ed,
ExifSRational srv;
srv = exif_get_srational( entry->data + offset, bo );
old_value = (double) srv.numerator / srv.denominator;
if( srv.denominator == 0 )
old_value = 0;
else
old_value = (double) srv.numerator / srv.denominator;
if( VIPS_FABS( old_value - value ) > 0.0001 ) {
vips_exif_double_to_srational( value, &srv );

View File

@ -27,6 +27,13 @@
* 24/7/19
* - close early on minimise
* - close early on error
* 23/8/18
* - allow GIF read errors during header scan
* - better feof() handling
* 27/8/19
* - check image and frame bounds, since giflib does not
* 1/9/19
* - improve early close again
*/
/*
@ -192,7 +199,6 @@ typedef struct _VipsForeignLoadGif {
/* Params for DGifOpen(). Set by subclasses, called by base class in
* _open().
*/
void *userPtr;
InputFunc read_func;
} VipsForeignLoadGif;
@ -203,6 +209,10 @@ typedef struct _VipsForeignLoadGifClass {
/* Close and reopen gif->file.
*/
int (*open)( VipsForeignLoadGif *gif );
/* Close any underlying file resource.
*/
void (*close)( VipsForeignLoadGif *gif );
} VipsForeignLoadGifClass;
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadGif, vips_foreign_load_gif,
@ -297,32 +307,14 @@ vips_foreign_load_gif_error( VipsForeignLoadGif *gif )
vips_foreign_load_gif_error_vips( gif, error );
}
static void
vips_foreign_load_gif_close( VipsForeignLoadGif *gif )
{
#ifdef HAVE_GIFLIB_5
if( gif->file ) {
int error;
if( DGifCloseFile( gif->file, &error ) == GIF_ERROR )
vips_foreign_load_gif_error_vips( gif, error );
gif->file = NULL;
}
#else
if( gif->file ) {
if( DGifCloseFile( gif->file ) == GIF_ERROR )
vips_foreign_load_gif_error_vips( gif, GifLastError() );
gif->file = NULL;
}
#endif
}
static void
vips_foreign_load_gif_dispose( GObject *gobject )
{
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gobject;
VipsForeignLoadGifClass *class =
(VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( gif );
vips_foreign_load_gif_close( gif );
class->close( gif );
VIPS_UNREF( gif->frame );
VIPS_UNREF( gif->previous );
@ -373,6 +365,23 @@ vips_foreign_load_gif_is_a( const char *filename )
return( 0 );
}
/* Make sure delays is allocated and large enough.
*/
static void
vips_foreign_load_gif_allocate_delays( VipsForeignLoadGif *gif )
{
if( gif->n_pages >= gif->delays_length ) {
int old = gif->delays_length;
int i;
gif->delays_length = gif->delays_length + gif->n_pages + 64;
gif->delays = (int *) g_realloc( gif->delays,
gif->delays_length * sizeof( int ) );
for( i = old; i < gif->delays_length; i++ )
gif->delays[i] = 40;
}
}
static int
vips_foreign_load_gif_ext_next( VipsForeignLoadGif *gif,
GifByteType **extension )
@ -406,15 +415,19 @@ vips_foreign_load_gif_code_next( VipsForeignLoadGif *gif,
/* Quickly scan an image record.
*/
static int
vips_foreign_load_gif_scan_image_record( VipsForeignLoadGif *gif )
vips_foreign_load_gif_scan_image( VipsForeignLoadGif *gif )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
GifFileType *file = gif->file;
ColorMapObject *map = file->Image.ColorMap ?
file->Image.ColorMap : file->SColorMap;
ColorMapObject *map;
GifByteType *extension;
if( DGifGetImageDesc( gif->file ) == GIF_ERROR ) {
vips_foreign_load_gif_error( gif );
return( -1 );
}
/* Check that the frame looks sane. Perhaps giflib checks
* this for us.
*/
@ -432,6 +445,7 @@ vips_foreign_load_gif_scan_image_record( VipsForeignLoadGif *gif )
/* Test for a non-greyscale colourmap for this frame.
*/
map = file->Image.ColorMap ? file->Image.ColorMap : file->SColorMap;
if( !gif->has_colour &&
map ) {
int i;
@ -525,18 +539,6 @@ vips_foreign_load_gif_scan_extension( VipsForeignLoadGif *gif )
gif->has_transparency = TRUE;
}
if( gif->n_pages >= gif->delays_length ) {
int old = gif->delays_length;
int i;
gif->delays_length =
gif->delays_length + gif->n_pages + 64;
gif->delays = (int *) g_realloc( gif->delays,
gif->delays_length * sizeof( int ) );
for( i = old; i < gif->delays_length; i++ )
gif->delays[i] = 40;
}
/* giflib uses centiseconds, we use ms.
*/
gif->delays[gif->n_pages] =
@ -610,49 +612,35 @@ vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image )
}
/* Attempt to quickly scan a GIF and discover what we need for our header. We
* need to scan the whole file to get n_pages, transparency and colour.
* need to scan the whole file to get n_pages, transparency and colour.
*
* Don't flag errors during header scan. Many GIFs do not follow spec.
*/
static int
vips_foreign_load_gif_header( VipsForeignLoad *load )
vips_foreign_load_gif_scan( VipsForeignLoadGif *gif )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadGifClass *gif_class =
(VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
GifRecordType record;
if( gif_class->open( gif ) )
return( -1 );
gif->n_pages = 0;
do {
if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) {
vips_foreign_load_gif_error( gif );
return( -1 );
}
if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR )
continue;
switch( record ) {
case IMAGE_DESC_RECORD_TYPE:
if( DGifGetImageDesc( gif->file ) == GIF_ERROR ) {
vips_foreign_load_gif_error( gif );
return( -1 );
}
if( vips_foreign_load_gif_scan_image_record( gif ) )
return( -1 );
(void) vips_foreign_load_gif_scan_image( gif );
gif->n_pages += 1;
vips_foreign_load_gif_allocate_delays( gif );
break;
case EXTENSION_RECORD_TYPE:
/* We need to fetch the extensions to check for
* cmaps and transparency.
*/
if( vips_foreign_load_gif_scan_extension( gif ) )
return( -1 );
(void) vips_foreign_load_gif_scan_extension( gif );
break;
case TERMINATE_RECORD_TYPE:
@ -678,11 +666,34 @@ vips_foreign_load_gif_header( VipsForeignLoad *load )
return( -1 );
}
/* And set the output vips header from what we've learned.
*/
if( vips_foreign_load_gif_set_header( gif, load->out ) )
return( 0 );
}
/* Scan the GIF and set the libvips header. We always close after scan, even
* on an error.
*/
static int
vips_foreign_load_gif_header( VipsForeignLoad *load )
{
VipsForeignLoadGifClass *class =
(VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
if( class->open( gif ) )
return( -1 );
if( vips_foreign_load_gif_scan( gif ) ) {
class->close( gif );
return( -1 );
}
if( vips_foreign_load_gif_set_header( gif, load->out ) ) {
class->close( gif );
return( -1 );
}
class->close( gif );
return( 0 );
}
@ -749,6 +760,11 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif )
{
GifFileType *file = gif->file;
if( DGifGetImageDesc( file ) == GIF_ERROR ) {
vips_foreign_load_gif_error( gif );
return( -1 );
}
/* Update the colour map for this frame.
*/
vips_foreign_load_gif_build_cmap( gif );
@ -774,7 +790,28 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif )
VIPS_IMAGE_ADDR( gif->frame, 0, 0 ),
VIPS_IMAGE_SIZEOF_IMAGE( gif->frame ) );
if( file->Image.Interlace ) {
/* giflib does not check that the Left / Top / Width / Height for this
* Image is inside the canvas.
*
* We could clip against the canvas, but for now, just ignore out of
* bounds frames. Watch for int overflow too.
*/
if( file->Image.Left < 0 ||
file->Image.Left > VIPS_MAX_COORD ||
file->Image.Width <= 0 ||
file->Image.Width > VIPS_MAX_COORD ||
file->Image.Left + file->Image.Width > file->SWidth ||
file->Image.Top < 0 ||
file->Image.Top > VIPS_MAX_COORD ||
file->Image.Height <= 0 ||
file->Image.Height > VIPS_MAX_COORD ||
file->Image.Top + file->Image.Height > file->SHeight ) {
VIPS_DEBUG_MSG( "vips_foreign_load_gif_render: "
"out of bounds frame of %d x %d pixels at %d x %d\n",
file->Image.Width, file->Image.Height,
file->Image.Left, file->Image.Top );
}
else if( file->Image.Interlace ) {
int i;
VIPS_DEBUG_MSG( "vips_foreign_load_gif_render: "
@ -888,11 +925,6 @@ vips_foreign_load_gif_next_page( VipsForeignLoadGif *gif )
VIPS_DEBUG_MSG( "vips_foreign_load_gif_next_page: "
"IMAGE_DESC_RECORD_TYPE\n" );
if( DGifGetImageDesc( gif->file ) == GIF_ERROR ) {
vips_foreign_load_gif_error( gif );
return( -1 );
}
if( vips_foreign_load_gif_render( gif ) )
return( -1 );
@ -1013,7 +1045,10 @@ vips_foreign_load_gif_generate( VipsRegion *or,
static void
vips_foreign_load_gif_minimise( VipsObject *object, VipsForeignLoadGif *gif )
{
vips_foreign_load_gif_close( gif );
VipsForeignLoadGifClass *class =
(VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( gif );
class->close( gif );
}
static int
@ -1025,8 +1060,6 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( load ), 4 );
/* Rewind.
*/
if( class->open( gif ) )
return( -1 );
@ -1080,25 +1113,38 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
static int
vips_foreign_load_gif_open( VipsForeignLoadGif *gif )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
#ifdef HAVE_GIFLIB_5
{
int error;
if( !(gif->file = DGifOpen( gif->userPtr, gif->read_func, &error )) ) {
if( !(gif->file = DGifOpen( gif, gif->read_func, &error )) ) {
vips_foreign_load_gif_error_vips( gif, error );
return( -1 );
}
}
#else
if( !(gif->file = DGifOpen( gif->userPtr, gif->read_func )) ) {
vips_foreign_load_gif_error_vips( gif, GifLastError() );
return( -1 );
#else
if( !(gif->file = DGifOpen( gif, gif->read_func )) ) {
vips_foreign_load_gif_error_vips( gif, GifLastError() );
return( -1 );
}
#endif
gif->eof = FALSE;
gif->current_page = 0;
/* giflib does no checking of image dimensions, not even for 0.
*/
if( gif->file->SWidth <= 0 ||
gif->file->SWidth > VIPS_MAX_COORD ||
gif->file->SHeight <= 0 ||
gif->file->SHeight > VIPS_MAX_COORD ) {
vips_error( class->nickname,
"%s", _( "image size out of bounds" ) );
return( -1 );
}
/* Allocate a line buffer now that we have the GIF width.
*/
VIPS_FREE( gif->line )
@ -1108,6 +1154,26 @@ vips_foreign_load_gif_open( VipsForeignLoadGif *gif )
return( 0 );
}
static void
vips_foreign_load_gif_close( VipsForeignLoadGif *gif )
{
#ifdef HAVE_GIFLIB_5
if( gif->file ) {
int error;
if( DGifCloseFile( gif->file, &error ) == GIF_ERROR )
vips_foreign_load_gif_error_vips( gif, error );
gif->file = NULL;
}
#else
if( gif->file ) {
if( DGifCloseFile( gif->file ) == GIF_ERROR )
vips_foreign_load_gif_error_vips( gif, GifLastError() );
gif->file = NULL;
}
#endif
}
static void
vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class )
{
@ -1121,6 +1187,7 @@ vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class )
gobject_class->get_property = vips_object_get_property;
gif_class->open = vips_foreign_load_gif_open;
gif_class->close = vips_foreign_load_gif_close;
load_class->header = vips_foreign_load_gif_header;
load_class->load = vips_foreign_load_gif_load;
@ -1157,6 +1224,8 @@ vips_foreign_load_gif_init( VipsForeignLoadGif *gif )
gif->loop = 0;
gif->comment = NULL;
gif->dispose = 0;
vips_foreign_load_gif_allocate_delays( gif );
}
typedef struct _VipsForeignLoadGifFile {
@ -1177,40 +1246,35 @@ typedef VipsForeignLoadGifClass VipsForeignLoadGifFileClass;
G_DEFINE_TYPE( VipsForeignLoadGifFile, vips_foreign_load_gif_file,
vips_foreign_load_gif_get_type() );
static void
vips_foreign_load_gif_file_dispose( GObject *gobject )
{
VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gobject;
VIPS_FREEF( fclose, file->fp );
G_OBJECT_CLASS( vips_foreign_load_gif_file_parent_class )->
dispose( gobject );
}
/* Our input function for file open. We can't use DGifOpenFileName(), since
* that just calls open() and won't work with unicode on win32. We can't use
* DGifOpenFileHandle() since that's an fd from open() and you can't pass those
* across DLL boundaries on Windows.
*/
static int
vips_giflib_file_read( GifFileType *file, GifByteType *buffer, int n )
static int
vips_giflib_file_read( GifFileType *gfile, GifByteType *buffer, int n )
{
FILE *fp = (FILE *) file->UserData;
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gfile->UserData;
VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif;
return( (int) fread( (void *) buffer, 1, n, fp ) );
if( feof( file->fp ) )
gif->eof = TRUE;
return( (int) fread( (void *) buffer, 1, n, file->fp ) );
}
static int
vips_foreign_load_gif_file_header( VipsForeignLoad *load )
{
VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) load;
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
VipsForeignLoadGifClass *class =
(VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load );
if( VIPS_FOREIGN_LOAD_CLASS(
vips_foreign_load_gif_file_parent_class )->header( load ) ) {
/* Close early if header read fails in our base class.
*/
VIPS_FREEF( fclose, file->fp );
class->close( gif );
return( -1 );
}
@ -1219,32 +1283,41 @@ vips_foreign_load_gif_file_header( VipsForeignLoad *load )
/* We have to have _open() as a vfunc since our base class needs to be able to
* make two scans of the gif (scan for header, then scan for pixels), so we
* must be able to close and reopen (or rewind).
* must be able to close and reopen.
*/
static int
vips_foreign_load_gif_file_open( VipsForeignLoadGif *gif )
{
VipsForeignLoad *load = (VipsForeignLoad *) gif;
VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif;
VipsForeignLoadGifClass *class =
(VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load );
if( !file->fp ) {
if( !(file->fp =
vips__file_open_read( file->filename, NULL, FALSE )) )
return( -1 );
class->close( gif );
VIPS_SETSTR( load->out->filename, file->filename );
}
else
rewind( file->fp );
if( !(file->fp =
vips__file_open_read( file->filename, NULL, FALSE )) )
return( -1 );
VIPS_SETSTR( load->out->filename, file->filename );
vips_foreign_load_gif_close( gif );
gif->userPtr = file->fp;
gif->read_func = vips_giflib_file_read;
return( VIPS_FOREIGN_LOAD_GIF_CLASS(
vips_foreign_load_gif_file_parent_class )->open( gif ) );
}
static void
vips_foreign_load_gif_file_close( VipsForeignLoadGif *gif )
{
VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif;
VIPS_FOREIGN_LOAD_GIF_CLASS(
vips_foreign_load_gif_file_parent_class )->close( gif );
VIPS_FREEF( fclose, file->fp );
}
static const char *vips_foreign_gif_suffs[] = {
".gif",
NULL
@ -1260,7 +1333,6 @@ vips_foreign_load_gif_file_class_init(
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
VipsForeignLoadGifClass *gif_class = (VipsForeignLoadGifClass *) class;
gobject_class->dispose = vips_foreign_load_gif_file_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
@ -1273,6 +1345,7 @@ vips_foreign_load_gif_file_class_init(
load_class->header = vips_foreign_load_gif_file_header;
gif_class->open = vips_foreign_load_gif_file_open;
gif_class->close = vips_foreign_load_gif_file_close;
VIPS_ARG_STRING( class, "filename", 1,
_( "Filename" ),
@ -1314,26 +1387,31 @@ G_DEFINE_TYPE( VipsForeignLoadGifBuffer, vips_foreign_load_gif_buffer,
static int
vips_giflib_buffer_read( GifFileType *file, GifByteType *buf, int n )
{
VipsForeignLoadGifBuffer *buffer =
(VipsForeignLoadGifBuffer *) file->UserData;
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) file->UserData;
VipsForeignLoadGifBuffer *buffer = (VipsForeignLoadGifBuffer *) gif;
size_t will_read = VIPS_MIN( n, buffer->bytes_to_go );
memcpy( buf, buffer->p, will_read );
buffer->p += will_read;
buffer->bytes_to_go -= will_read;
return( will_read );
if( will_read == 0 )
gif->eof = TRUE;
return( will_read );
}
static int
vips_foreign_load_gif_buffer_open( VipsForeignLoadGif *gif )
{
VipsForeignLoadGifBuffer *buffer = (VipsForeignLoadGifBuffer *) gif;
VipsForeignLoadGifClass *class =
(VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( gif );
class->close( gif );
vips_foreign_load_gif_close( gif );
buffer->p = buffer->buf->data;
buffer->bytes_to_go = buffer->buf->length;
gif->userPtr = gif;
gif->read_func = vips_giflib_buffer_read;;
return( VIPS_FOREIGN_LOAD_GIF_CLASS(

View File

@ -7,6 +7,8 @@
* 24/7/19
* - close early on minimise
* - close early on error
* 1/9/19 [meyermarcel]
* - handle alpha
*/
/*
@ -86,6 +88,10 @@ typedef struct _VipsForeignLoadHeif {
*/
int n_top;
/* TRUE for RGBA ... otherwise, RGB.
*/
gboolean has_alpha;
/* Size of final output image.
*/
int width;
@ -260,7 +266,6 @@ vips_foreign_load_heif_set_page( VipsForeignLoadHeif *heif,
static int
vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out )
{
gboolean has_alpha;
int bands;
int i;
/* Surely, 16 metadata items will be enough for anyone.
@ -275,10 +280,12 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out )
if( vips_foreign_load_heif_set_page( heif, heif->page, FALSE ) )
return( -1 );
/* FIXME ... never seen this return TRUE on any image, strangely.
*/
has_alpha = heif_image_handle_has_alpha_channel( heif->handle );
bands = has_alpha ? 4 : 3;
heif->has_alpha = heif_image_handle_has_alpha_channel( heif->handle );
#ifdef DEBUG
printf( "heif_image_handle_has_alpha_channel() = %d\n",
heif->has_alpha );
#endif /*DEBUG*/
bands = heif->has_alpha ? 4 : 3;
/* FIXME .. need to test XMP and IPCT.
*/
@ -617,13 +624,11 @@ vips_foreign_load_heif_generate( VipsRegion *or,
if( !heif->img ) {
struct heif_error error;
struct heif_decoding_options *options;
enum heif_chroma chroma = heif->has_alpha ?
heif_chroma_interleaved_RGBA :
heif_chroma_interleaved_RGB;
/* Decode the image to 24bit interleaved.
*
* FIXME What will this do for RGBA? Or is alpha always
* separate?
*
* Only disable transforms if we have been able to fetch the
/* Only disable transforms if we have been able to fetch the
* untransformed dimensions.
*/
options = heif_decoding_options_alloc();
@ -631,7 +636,7 @@ vips_foreign_load_heif_generate( VipsRegion *or,
options->ignore_transformations = !heif->autorotate;
#endif /*HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH*/
error = heif_decode_image( heif->handle, &heif->img,
heif_colorspace_RGB, heif_chroma_interleaved_RGB,
heif_colorspace_RGB, chroma,
options );
heif_decoding_options_free( options );
if( error.code ) {

View File

@ -4,6 +4,8 @@
* - from niftisave.c
* 3/7/19 [lovell]
* - add "compression" option
* 1/9/19 [meyermarcel]
* - save alpha when necessary
*/
/*
@ -199,10 +201,8 @@ vips_foreign_save_heif_write_page( VipsForeignSaveHeif *heif, int page )
#ifdef HAVE_HEIF_ENCODING_OPTIONS_ALLOC
options = heif_encoding_options_alloc();
/* FIXME .. should be an option, though I don't know of any way to
* test it
*/
options->save_alpha_channel = 1;
if( vips_image_hasalpha( save->ready ) )
options->save_alpha_channel = 1;
#else /*!HAVE_HEIF_ENCODING_OPTIONS_ALLOC*/
options = NULL;
#endif /*HAVE_HEIF_ENCODING_OPTIONS_ALLOC*/
@ -336,14 +336,19 @@ vips_foreign_save_heif_build( VipsObject *object )
* here and write a frame each time it fills.
*/
error = heif_image_create( heif->page_width, heif->page_height,
heif_colorspace_RGB, heif_chroma_interleaved_RGB, &heif->img );
heif_colorspace_RGB,
vips_image_hasalpha( save->ready ) ?
heif_chroma_interleaved_RGBA :
heif_chroma_interleaved_RGB,
&heif->img );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
error = heif_image_add_plane( heif->img, heif_channel_interleaved,
heif->page_width, heif->page_height, 24 );
heif->page_width, heif->page_height,
vips_image_hasalpha( save->ready ) ? 32 : 24 );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
@ -394,7 +399,7 @@ vips_foreign_save_heif_class_init( VipsForeignSaveHeifClass *class )
foreign_class->suffs = vips__heif_suffs;
save_class->saveable = VIPS_SAVEABLE_RGB;
save_class->saveable = VIPS_SAVEABLE_RGBA_ONLY;
save_class->format_table = vips_heif_bandfmt;
VIPS_ARG_INT( class, "Q", 10,

View File

@ -313,7 +313,7 @@ find_chroma_subsample( struct jpeg_decompress_struct *cinfo )
}
static int
attach_blob( VipsImage *im, const char *field, void *data, int data_length )
attach_blob( VipsImage *im, const char *field, void *data, size_t data_length )
{
/* Only use the first one.
*/
@ -339,18 +339,21 @@ attach_blob( VipsImage *im, const char *field, void *data, int data_length )
* the real XMP.
*/
static int
attach_xmp_blob( VipsImage *im, void *data, int data_length )
attach_xmp_blob( VipsImage *im, void *data, size_t data_length )
{
char *p = (char *) data;
int i;
if( !vips_isprefix( "http", p ) )
if( data_length < 4 ||
!vips_isprefix( "http", p ) )
return( 0 );
/* Search for a null char within the first few characters. 80
* should be plenty for a basic URL.
*
* -2 for the extra null.
*/
for( i = 0; i < 80; i++ )
for( i = 0; i < VIPS_MIN( 80, data_length - 2 ); i++ )
if( !p[i] )
break;
if( p[i] )
@ -499,7 +502,7 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out )
for( p = cinfo->marker_list; p; p = p->next ) {
#ifdef DEBUG
{
printf( "read_jpeg_header: seen %d bytes of APP%d\n",
printf( "read_jpeg_header: seen %u bytes of APP%d\n",
p->data_length,
p->marker - JPEG_APP0 );
@ -596,7 +599,7 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out )
default:
#ifdef DEBUG
printf( "read_jpeg_header: "
"ignoring %d byte APP%d block\n",
"ignoring %u byte APP%d block\n",
p->data_length, p->marker - JPEG_APP0 );
#endif /*DEBUG*/
break;

View File

@ -323,7 +323,10 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load )
vips_foreign_load_pdf_set_image( pdf, load->out );
/* Convert the background to the image format.
/* Convert the background to the image format.
*
* FIXME ... we probably should convert this to pre-multiplied BGRA
* to match the Cairo convention. See vips__cairo2rgba().
*/
if( !(pdf->ink = vips__vector_to_ink( class->nickname,
load->out,
@ -747,8 +750,8 @@ vips_foreign_load_pdf_is_a( const char *filename )
* you can scale the rendering from the default 1 point == 1 pixel by
* setting @scale.
*
* Use @background to set the background colour, including transparency. The
* default is 255 (solid white).
* Use @background to set the background RGBA colour. The default is 255
* (solid white), use eg. 0 for a transparent background.
*
* The operation fills a number of header fields with metadata, for example
* "pdf-author". They may be useful.

View File

@ -776,9 +776,6 @@ empty_output_buffer( j_compress_ptr cinfo )
METHODDEF(void)
init_destination( j_compress_ptr cinfo )
{
OutputBuffer *buf = (OutputBuffer *) cinfo->dest;
vips_dbuf_init( &buf->dbuf );
empty_output_buffer( cinfo );
}
@ -845,6 +842,7 @@ buf_dest( j_compress_ptr cinfo, void **obuf, size_t *olen )
/* Save output parameters.
*/
vips_dbuf_init( &buf->dbuf );
buf->obuf = obuf;
buf->olen = olen;
}

View File

@ -547,7 +547,9 @@ read_header( Read *read, VipsImage *out )
}
if( read->width <= 0 ||
read->height <= 0 ) {
read->height <= 0 ||
read->width > 0x3FFF ||
read->height > 0x3FFF ) {
vips_error( "webp", "%s", _( "bad image dimensions" ) );
return( -1 );
}

View File

@ -4,6 +4,7 @@ libhistogram_la_SOURCES = \
histogram.c \
phistogram.h \
maplut.c \
case.c \
hist_unary.c \
hist_unary.h \
hist_cum.c \

313
libvips/histogram/case.c Normal file
View File

@ -0,0 +1,313 @@
/* use pixel values to pick cases from an array of images
*
* 28/7/19
* - from maplut.c
*/
/*
This file is part of VIPS.
VIPS is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vips/vips.h>
#include <vips/internal.h>
typedef struct _VipsCase {
VipsOperation parent_instance;
VipsImage *index;
VipsArrayImage *cases;
VipsImage *out;
int n;
} VipsCase;
typedef VipsOperationClass VipsCaseClass;
G_DEFINE_TYPE( VipsCase, vips_case, VIPS_TYPE_OPERATION );
static int
vips_case_gen( VipsRegion *or, void *seq, void *a, void *b,
gboolean *stop )
{
VipsRegion **ar = (VipsRegion **) seq;
VipsCase *cas = (VipsCase *) b;
VipsRect *r = &or->valid;
VipsRegion *index = ar[cas->n];
int x, y, i;
VipsPel * restrict ip;
VipsPel * restrict q;
size_t ils;
size_t qls;
int hist[256];
VipsPel * restrict p[256];
size_t ls[256];
size_t ps;
if( vips_region_prepare( index, r ) )
return( -1 );
g_assert( index->im->BandFmt == VIPS_FORMAT_UCHAR );
g_assert( index->im->Bands == 1 );
/* Histogram of index region, so we know which of our inputs we will
* need to prepare.
*/
memset( hist, 0, cas->n * sizeof( int ) );
ip = VIPS_REGION_ADDR( index, r->left, r->top );
ils = VIPS_REGION_LSKIP( index );
for( y = 0; y < r->height; y++ ) {
for( x = 0; x < r->width; x++ ) {
int v = VIPS_MIN( ip[x], cas->n - 1 );
hist[v] += 1;
}
ip += ils;
}
for( i = 0; i < cas->n; i++ )
if( hist[i] ) {
if( vips_region_prepare( ar[i], r ) )
return( -1 );
p[i] = VIPS_REGION_ADDR( ar[i], r->left, r->top );
ls[i] = VIPS_REGION_LSKIP( ar[i] );
}
ip = VIPS_REGION_ADDR( index, r->left, r->top );
q = VIPS_REGION_ADDR( or, r->left, r->top );
qls = VIPS_REGION_LSKIP( or );
ps = VIPS_IMAGE_SIZEOF_PEL( or->im );
for( y = 0; y < r->height; y++ ) {
int k;
k = 0;
for( x = 0; x < r->width; x++ ) {
int v = VIPS_MIN( ip[x], cas->n - 1 );
VipsPel * restrict pv = p[v];
int j;
for( j = 0; j < ps; j++ ) {
q[k] = pv[k];
k += 1;
}
}
ip += ils;
q += qls;
for( i = 0; i < cas->n; i++ )
if( hist[i] )
p[i] += ls[i];
}
return( 0 );
}
static int
vips_case_build( VipsObject *object )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsCase *cas = (VipsCase *) object;
VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 );
VipsImage *index;
VipsImage **cases;
VipsImage **decode;
VipsImage **format;
VipsImage **band;
VipsImage **size;
int i;
g_object_set( object, "out", vips_image_new(), NULL );
if( VIPS_OBJECT_CLASS( vips_case_parent_class )->build( object ) )
return( -1 );
index = cas->index;
cases = vips_area_get_data( &cas->cases->area,
NULL, &cas->n, NULL, NULL );
if( cas->n > 256 ||
cas->n < 1 ) {
vips_error( class->nickname, "%s", _( "bad number of cases" ) );
return( -1 );
}
if( index->Bands > 1 ) {
vips_error( class->nickname,
"%s", _( "index image not 1-band" ) );
return( -1 );
}
/* Cast @index to u8 to make the index image.
*/
if( vips_cast( index, &t[0], VIPS_FORMAT_UCHAR, NULL ) )
return( -1 );
index = t[0];
decode = (VipsImage **) vips_object_local_array( object, cas->n );
format = (VipsImage **) vips_object_local_array( object, cas->n );
band = (VipsImage **) vips_object_local_array( object, cas->n + 1 );
size = (VipsImage **) vips_object_local_array( object, cas->n + 1 );
/* Decode RAD/LABQ etc.
*/
for( i = 0; i < cas->n; i++ )
if( vips_image_decode( cases[i], &decode[i] ) )
return( -1 );
cases = decode;
/* case images must match in format, size and bands.
*
* We want everything sized up to the size of the index image, so add
* that to the end of the set of images for sizealike.
*/
band[cas->n] = index;
g_object_ref( index );
if( vips__formatalike_vec( cases, format, cas->n ) ||
vips__bandalike_vec( class->nickname,
format, band, cas->n, 1 ) ||
vips__sizealike_vec( band, size, cas->n + 1 ) )
return( -1 );
cases = size;
if( vips_image_pipeline_array( cas->out,
VIPS_DEMAND_STYLE_THINSTRIP, cases ) )
return( -1 );
cas->out->BandFmt = cases[0]->BandFmt;
cas->out->Bands = cases[0]->Bands;
cas->out->Type = cases[0]->Type;
if( vips_image_generate( cas->out,
vips_start_many, vips_case_gen, vips_stop_many,
cases, cas ) )
return( -1 );
return( 0 );
}
static void
vips_case_class_init( VipsCaseClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class );
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "case";
object_class->description =
_( "use pixel values to pick cases from an array of images" );
object_class->build = vips_case_build;
operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
VIPS_ARG_IMAGE( class, "index", 1,
_( "index" ),
_( "Index image" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsCase, index ) );
VIPS_ARG_BOXED( class, "cases", 2,
_( "cases" ),
_( "Array of case images" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsCase, cases ),
VIPS_TYPE_ARRAY_IMAGE );
VIPS_ARG_IMAGE( class, "out", 3,
_( "Output" ),
_( "Output image" ),
VIPS_ARGUMENT_REQUIRED_OUTPUT,
G_STRUCT_OFFSET( VipsCase, out ) );
}
static void
vips_case_init( VipsCase *cas )
{
}
static int
vips_casev( VipsImage *index, VipsImage **cases, VipsImage **out, int n,
va_list ap )
{
VipsArrayImage *array;
int result;
array = vips_array_image_new( cases, n );
result = vips_call_split( "case", ap, index, array, out );
vips_area_unref( VIPS_AREA( array ) );
return( result );
}
/**
* vips_case: (method)
* @index: index image
* @cases: (array length=n): array of case images
* @out: (out): output image
* @n: number of case images
* @...: %NULL-terminated list of optional named arguments
*
* Use values in @index to select pixels from @cases.
*
* @index must have one band. @cases can have up to 256 elements. Values in
* @index greater than or equal to @n use the final image in @cases. The
* images in @cases must have either one band or the same number of bands.
* The output image is the same size as @index. Images in @cases are
* expanded to the smallest common format and number of bands.
*
* Combine this with vips_switch() to make something like a case statement or
* a multi-way vips_ifthenelse().
*
* See also: vips_maplut(), vips_switch(), vips_ifthenelse().
*
* Returns: 0 on success, -1 on error
*/
int
vips_case( VipsImage *index, VipsImage **cases, VipsImage **out, int n, ... )
{
va_list ap;
int result;
va_start( ap, n );
result = vips_casev( index, cases, out, n, ap );
va_end( ap );
return( result );
}

View File

@ -251,6 +251,7 @@ void
vips_histogram_operation_init( void )
{
extern GType vips_maplut_get_type( void );
extern GType vips_case_get_type( void );
extern GType vips_percent_get_type( void );
extern GType vips_hist_cum_get_type( void );
extern GType vips_hist_norm_get_type( void );
@ -262,15 +263,16 @@ vips_histogram_operation_init( void )
extern GType vips_hist_entropy_get_type( void );
extern GType vips_stdif_get_type( void );
vips_maplut_get_type();
vips_percent_get_type();
vips_stdif_get_type();
vips_hist_cum_get_type();
vips_hist_norm_get_type();
vips_hist_equal_get_type();
vips_hist_plot_get_type();
vips_hist_match_get_type();
vips_hist_local_get_type();
vips_hist_ismonotonic_get_type();
vips_maplut_get_type();
vips_case_get_type();
vips_percent_get_type();
vips_stdif_get_type();
vips_hist_cum_get_type();
vips_hist_norm_get_type();
vips_hist_equal_get_type();
vips_hist_plot_get_type();
vips_hist_match_get_type();
vips_hist_local_get_type();
vips_hist_ismonotonic_get_type();
vips_hist_entropy_get_type();
}

View File

@ -262,6 +262,8 @@ int vips_recomb( VipsImage *in, VipsImage **out, VipsImage *m, ... )
int vips_ifthenelse( VipsImage *cond, VipsImage *in1, VipsImage *in2,
VipsImage **out, ... )
__attribute__((sentinel));
int vips_switch( VipsImage **tests, VipsImage **out, int n, ... )
__attribute__((sentinel));
int vips_flatten( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel));

View File

@ -40,6 +40,8 @@ extern "C" {
int vips_maplut( VipsImage *in, VipsImage **out, VipsImage *lut, ... )
__attribute__((sentinel));
int vips_mapimage( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... )
__attribute__((sentinel));
int vips_percent( VipsImage *in, double percent, int *threshold, ... )
__attribute__((sentinel));
int vips_stdif( VipsImage *in, VipsImage **out, int width, int height, ... )
@ -62,6 +64,10 @@ int vips_hist_ismonotonic( VipsImage *in, gboolean *out, ... )
int vips_hist_entropy( VipsImage *in, double *out, ... )
__attribute__((sentinel));
int vips_case( VipsImage *index, VipsImage **cases, VipsImage **out, int n,
... )
__attribute__((sentinel));
#ifdef __cplusplus
}
#endif /*__cplusplus*/

View File

@ -1454,12 +1454,15 @@ vips_image_set_blob_copy( VipsImage *image,
{
void *data_copy;
/* Cap at 100mb for sanity.
*/
if( !data ||
length == 0 )
length == 0 ||
length > 100 * 1024 * 1024 )
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
* is read as a C string. The libtiff reader attaches
* XMP XML as a blob, for example.
*/
if( !(data_copy = vips_malloc( NULL, length + 1 )) )

View File

@ -25,6 +25,8 @@
* - use O_TMPFILE, if available
* 23/7/18
* - escape ASCII control characters in XML
* 29/8/19
* - verify bands/format for coded images
*/
/*
@ -348,8 +350,8 @@ vips__read_header_bytes( VipsImage *im, unsigned char *from )
from += 4;
if( im->magic != VIPS_MAGIC_INTEL &&
im->magic != VIPS_MAGIC_SPARC ) {
vips_error( "VipsImage", _( "\"%s\" is not a VIPS image" ),
im->filename );
vips_error( "VipsImage",
_( "\"%s\" is not a VIPS image" ), im->filename );
return( -1 );
}
@ -383,10 +385,51 @@ vips__read_header_bytes( VipsImage *im, unsigned char *from )
im->Bands = VIPS_CLIP( 1, im->Bands, VIPS_MAX_COORD );
im->BandFmt = VIPS_CLIP( 0, im->BandFmt, VIPS_FORMAT_LAST - 1 );
/* Type, Coding, Offset, Res, etc. don't affect vips file layout, just
/* Coding and Type have missing values, so we look up in the enum.
*/
im->Type = g_enum_get_value(
g_type_class_ref( VIPS_TYPE_INTERPRETATION ),
im->Type ) ?
im->Type : VIPS_INTERPRETATION_ERROR;
im->Coding = g_enum_get_value(
g_type_class_ref( VIPS_TYPE_CODING ),
im->Coding ) ?
im->Coding : VIPS_CODING_ERROR;
/* Offset, Res, etc. don't affect vips file layout, just
* pixel interpretation, don't clip them.
*/
/* Coding values imply Bands and BandFmt settings --- make sure they
* are sane.
*/
switch( im->Coding ) {
case VIPS_CODING_NONE:
break;
case VIPS_CODING_LABQ:
if( im->Bands != 4 ||
im->BandFmt != VIPS_FORMAT_UCHAR ) {
vips_error( "VipsImage",
"%s", _( "malformed LABQ image" ) );
return( -1 );
}
break;
case VIPS_CODING_RAD:
if( im->Bands != 4 ||
im->BandFmt != VIPS_FORMAT_UCHAR ) {
vips_error( "VipsImage",
"%s", _( "malformed RAD image" ) );
return( -1 );
}
break;
default:
g_assert_not_reached();
break;
}
return( 0 );
}

View File

@ -22,6 +22,8 @@
* - don't force import CMYK, since colourspace knows about it now
* 24/4/19
* - support multi-page (animated) images
* 27/8/19 kleisauke
* - prevent over-pre-shrink in thumbnail
*/
/*
@ -368,11 +370,20 @@ vips_thumbnail_calculate_common_shrink( VipsThumbnail *thumbnail,
{
double hshrink;
double vshrink;
double shrink;
vips_thumbnail_calculate_shrink( thumbnail, width, height,
&hshrink, &vshrink );
return( VIPS_MIN( hshrink, vshrink ) );
shrink = VIPS_MIN( hshrink, vshrink );
/* We don't want to shrink so much that we send an axis to 0.
*/
if( shrink > thumbnail->input_width ||
shrink > thumbnail->input_height )
shrink = 1.0;
return( shrink );
}
/* Find the best jpeg preload shrink.
@ -464,37 +475,21 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
factor = 1.0;
if( vips_isprefix( "VipsForeignLoadJpeg", thumbnail->loader ) ) {
if( vips_isprefix( "VipsForeignLoadJpeg", thumbnail->loader ) )
factor = vips_thumbnail_find_jpegshrink( thumbnail,
thumbnail->input_width, thumbnail->input_height );
g_info( "loading jpeg with factor %g pre-shrink", factor );
}
else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ||
vips_isprefix( "VipsForeignLoadOpenslide",
thumbnail->loader ) ) {
thumbnail->loader ) )
factor = vips_thumbnail_find_pyrlevel( thumbnail,
thumbnail->input_width, thumbnail->input_height );
g_info( "loading pyr level %g", factor );
}
else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ) {
factor = 1.0 /
vips_thumbnail_calculate_common_shrink( thumbnail,
thumbnail->input_width,
thumbnail->page_height );
g_info( "loading PDF with factor %g pre-scale", factor );
}
else if( vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ) {
factor = 1.0 /
vips_thumbnail_calculate_common_shrink( thumbnail,
thumbnail->input_width,
thumbnail->input_height );
g_info( "loading SVG with factor %g pre-scale", factor );
}
else if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) ) {
else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ||
vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ||
vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) )
factor = vips_thumbnail_calculate_common_shrink( thumbnail,
thumbnail->input_width,
thumbnail->page_height );
if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) ) {
/* 'factor' is a gboolean which enables thumbnail load instead
* of image load.
*
@ -505,16 +500,9 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
factor = 1.0;
else
factor = 0.0;
}
else if( vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ) {
factor = 1.0 /
vips_thumbnail_calculate_common_shrink( thumbnail,
thumbnail->input_width,
thumbnail->page_height );
g_info( "loading webp with factor %g pre-scale", factor );
}
g_info( "loading with factor %g pre-shrink", factor );
if( !(im = class->open( thumbnail, factor )) )
return( NULL );
@ -952,7 +940,7 @@ vips_thumbnail_file_open( VipsThumbnail *thumbnail, double factor )
vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ) {
return( vips_image_new_from_file( file->filename,
"access", VIPS_ACCESS_SEQUENTIAL,
"scale", factor,
"scale", 1.0 / factor,
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ) {
@ -1150,7 +1138,7 @@ vips_thumbnail_buffer_open( VipsThumbnail *thumbnail, double factor )
buffer->buf->data, buffer->buf->length,
buffer->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
"scale", factor,
"scale", 1.0 / factor,
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ) {

View File

@ -1,239 +0,0 @@
## Imported from pygobject at commit 5737a9ec4bf4d9d07a7e3994d91abf9077b342cc.
## Automake's built-in version has problems on multiarch systems.
## this one is commonly used with AM_PATH_PYTHONDIR ...
dnl AM_CHECK_PYMOD(MODNAME [,SYMBOL [,ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]]])
dnl Check if a module containing a given symbol is visible to python.
AC_DEFUN([AM_CHECK_PYMOD],
[AC_REQUIRE([AM_PATH_PYTHON])
py_mod_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'`
AC_MSG_CHECKING(for ifelse([$2],[],,[$2 in ])python module $1)
AC_CACHE_VAL(py_cv_mod_$py_mod_var, [
ifelse([$2],[], [prog="
import sys
try:
import $1
except ImportError:
sys.exit(1)
except:
sys.exit(0)
sys.exit(0)"], [prog="
import $1
$1.$2"])
if $PYTHON -c "$prog" 1>&AC_FD_CC 2>&AC_FD_CC
then
eval "py_cv_mod_$py_mod_var=yes"
else
eval "py_cv_mod_$py_mod_var=no"
fi
])
py_val=`eval "echo \`echo '$py_cv_mod_'$py_mod_var\`"`
if test "x$py_val" != xno; then
AC_MSG_RESULT(yes)
ifelse([$3], [],, [$3
])dnl
else
AC_MSG_RESULT(no)
ifelse([$4], [],, [$4
])dnl
fi
])
dnl a macro to check for ability to create python extensions
dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE])
dnl function also defines PYTHON_INCLUDES
AC_DEFUN([AM_CHECK_PYTHON_HEADERS],
[AC_REQUIRE([AM_PATH_PYTHON])
AC_MSG_CHECKING(for headers required to compile python extensions)
dnl deduce PYTHON_INCLUDES
if test "x$PYTHON_INCLUDES" = x; then
PYTHON_CONFIG=`which $PYTHON`-config
if test -x "$PYTHON_CONFIG"; then
PYTHON_INCLUDES=`$PYTHON_CONFIG --includes 2>/dev/null`
else
PYTHON_INCLUDES=`$PYTHON -c "import distutils.sysconfig, sys; sys.stdout.write(distutils.sysconfig.get_python_inc(True))"`
PYTHON_INCLUDES="-I$PYTHON_INCLUDES"
fi
fi
AC_SUBST(PYTHON_INCLUDES)
dnl check if the headers exist:
save_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES"
AC_TRY_CPP([#include <Python.h>],dnl
[AC_MSG_RESULT(found)
$1],dnl
[AC_MSG_RESULT(not found)
$2])
CPPFLAGS="$save_CPPFLAGS"
])
dnl a macro to check for ability to embed python
dnl AM_CHECK_PYTHON_LIBS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE])
dnl function also defines PYTHON_LIBS
AC_DEFUN([AM_CHECK_PYTHON_LIBS],
[AC_REQUIRE([AM_PATH_PYTHON])
AC_MSG_CHECKING(for libraries required to embed python)
dnl deduce PYTHON_LIBS
py_prefix=`$PYTHON -c "import sys; sys.stdout.write(sys.prefix)"`
if test "x$PYTHON_LIBS" = x; then
PYTHON_CONFIG=`which $PYTHON`-config
if test -x "$PYTHON_CONFIG"; then
PYTHON_LIBS=`$PYTHON_CONFIG --ldflags 2>/dev/null`
else
PYTHON_LIBS="-L${py_prefix}/lib -lpython${PYTHON_VERSION}"
fi
fi
if test "x$PYTHON_LIB_LOC" = x; then
PYTHON_LIB_LOC="${py_prefix}/lib"
fi
AC_SUBST(PYTHON_LIBS)
AC_SUBST(PYTHON_LIB_LOC)
dnl check if the headers exist:
save_LIBS="$LIBS"
LIBS="$LIBS $PYTHON_LIBS"
AC_TRY_LINK_FUNC(Py_Initialize, dnl
[LIBS="$save_LIBS"; AC_MSG_RESULT(yes); $1], dnl
[LIBS="$save_LIBS"; AC_MSG_RESULT(no); $2])
])
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# JD_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
# ---------------------------------------------------------------------------
# Adds support for distributing Python modules and packages. To
# install modules, copy them to $(pythondir), using the python_PYTHON
# automake variable. To install a package with the same name as the
# automake package, install to $(pkgpythondir), or use the
# pkgpython_PYTHON automake variable.
#
# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as
# locations to install python extension modules (shared libraries).
# Another macro is required to find the appropriate flags to compile
# extension modules.
#
# If your package is configured with a different prefix to python,
# users will have to add the install directory to the PYTHONPATH
# environment variable, or create a .pth file (see the python
# documentation for details).
#
# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will
# cause an error if the version of python installed on the system
# doesn't meet the requirement. MINIMUM-VERSION should consist of
# numbers and dots only.
AC_DEFUN([JD_PATH_PYTHON],
[
dnl Find a Python interpreter. Python versions prior to 2.0 are not
dnl supported
m4_define_default([_AM_PYTHON_INTERPRETER_LIST],
[python python2 python2.7 python3 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0])
m4_if([$1],[],[
dnl No version check is needed.
# Find any Python interpreter.
if test -z "$PYTHON"; then
AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :)
fi
am_display_PYTHON=python
], [
dnl A version check is needed.
if test -n "$PYTHON"; then
# If the user set $PYTHON, use it and don't search something else.
AC_MSG_CHECKING([whether $PYTHON version >= $1])
AM_PYTHON_CHECK_VERSION([$PYTHON], [$1],
[AC_MSG_RESULT(yes)],
[AC_MSG_ERROR(too old)])
am_display_PYTHON=$PYTHON
else
# Otherwise, try each interpreter until we find one that satisfies
# VERSION.
AC_CACHE_CHECK([for a Python interpreter with version >= $1],
[am_cv_pathless_PYTHON],[
for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do
test "$am_cv_pathless_PYTHON" = none && break
AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break])
done])
# Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON.
if test "$am_cv_pathless_PYTHON" = none; then
PYTHON=:
else
AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON])
fi
am_display_PYTHON=$am_cv_pathless_PYTHON
fi
])
if test "$PYTHON" = :; then
dnl Run any user-specified action, or abort.
m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])])
else
dnl Query Python for its version number. Getting [:3] seems to be
dnl the best way to do this; it's what "site.py" does in the standard
dnl library.
AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version],
[am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`])
AC_SUBST([PYTHON_VERSION], [$am_cv_python_version])
dnl Use the values of $prefix and $exec_prefix for the corresponding
dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made
dnl distinct variables so they can be overridden if need be. However,
dnl general consensus is that you shouldn't need this ability.
AC_SUBST([PYTHON_PREFIX], ['${prefix}'])
AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}'])
dnl At times (like when building shared libraries) you may want
dnl to know which OS platform Python thinks this is.
AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform],
[am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`])
AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform])
dnl Set up 4 directories:
dnl pythondir -- where to install python scripts. This is the
dnl site-packages directory, not the python standard library
dnl directory like in previous automake betas. This behavior
dnl is more consistent with lispdir.m4 for example.
dnl Query distutils for this directory. distutils does not exist in
dnl Python 1.5, so we fall back to the hardcoded directory if it
dnl doesn't work.
AC_CACHE_CHECK([for $am_display_PYTHON script directory],
[am_cv_python_pythondir],
[am_cv_python_pythondir=`$PYTHON -c "from distutils import sysconfig; print(sysconfig.get_python_lib(0,0,prefix='$PYTHON_PREFIX'))" 2>/dev/null ||
echo "$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages"`])
AC_SUBST([pythondir], [$am_cv_python_pythondir])
dnl pkgpythondir -- $PACKAGE directory under pythondir. Was
dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is
dnl more consistent with the rest of automake.
AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE])
dnl pyexecdir -- directory for installing python extension modules
dnl (shared libraries)
dnl Query distutils for this directory. distutils does not exist in
dnl Python 1.5, so we fall back to the hardcoded directory if it
dnl doesn't work.
AC_CACHE_CHECK([for $am_display_PYTHON extension module directory],
[am_cv_python_pyexecdir],
[am_cv_python_pyexecdir=`$PYTHON -c "from distutils import sysconfig; print(sysconfig.get_python_lib(1,0,prefix='$PYTHON_EXEC_PREFIX'))" 2>/dev/null ||
echo "${PYTHON_EXEC_PREFIX}/lib/python${PYTHON_VERSION}/site-packages"`])
AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir])
dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE)
AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE])
dnl Run any user-specified action.
$2
fi
])

View File

@ -1,5 +1,6 @@
# vim: set fileencoding=utf-8 :
# test helpers
import os
import tempfile
import pytest
@ -174,7 +175,7 @@ def run_fn2(fn, x, y):
# test a pair of things which can be lists for approx. equality
def assert_almost_equal_objects(a, b, threshold=0.0001, msg=''):
# print 'assertAlmostEqualObjects %s = %s' % (a, b)
# print('assertAlmostEqualObjects %s = %s' % (a, b))
assert all([pytest.approx(x, abs=threshold) == y
for x, y in zip_expand(a, b)]), msg

View File

@ -1,4 +1,5 @@
# vim: set fileencoding=utf-8 :
import math
import pytest
@ -10,14 +11,16 @@ from helpers import unsigned_formats, float_formats, noncomplex_formats, \
class TestArithmetic:
def run_arith(self, fn, fmt=all_formats):
[run_image2(fn.__name__ + ' image', x.cast(y), x.cast(z), fn)
[run_image2('%s image %s %s %s' % (fn.__name__, x, y, z),
x.cast(y), x.cast(z), fn)
for x in self.all_images for y in fmt for z in fmt]
def run_arith_const(self, fn, fmt=all_formats):
[run_const(fn.__name__ + ' scalar', fn, x.cast(y), 2)
[run_const('%s scalar %s %s' % (fn.__name__, x, y),
fn, x.cast(y), 2)
for x in self.all_images for y in fmt]
[run_const(fn.__name__ + ' vector', fn, self.colour.cast(y),
[1, 2, 3])
[run_const('%s vector %s' % (fn.__name__, y),
fn, self.colour.cast(y), [1, 2, 3])
for y in fmt]
# run a function on an image,
@ -199,6 +202,13 @@ class TestArithmetic:
self.run_arith_const(noteq)
self.run_arith(noteq)
# comparisons against out of range values should always fail, and
# comparisons to fractional values should always fail
x = pyvips.Image.grey(256, 256, uchar=True)
assert (x == 1000).max() == 0
assert (x == 12).max() == 255
assert (x == 12.5).max() == 0
def test_abs(self):
def my_abs(x):
return abs(x)

View File

@ -539,6 +539,26 @@ class TestConversion:
result = r(50, 50)
assert_almost_equal_objects(result, [3.0, 4.9, 6.9], threshold=0.1)
def test_switch(self):
x = pyvips.Image.grey(256, 256, uchar=True)
# slice into two at 128, we should get 50% of pixels in each half
index = pyvips.Image.switch([x < 128, x >= 128])
assert index.avg() == 0.5
# slice into four
index = pyvips.Image.switch([
x < 64,
x >= 64 and x < 128,
x >= 128 and x < 192,
x >= 192
])
assert index.avg() == 1.5
# no match should return n + 1
index = pyvips.Image.switch([x == 1000, x == 2000])
assert index.avg() == 2
def test_insert(self):
for x in all_formats:
for y in all_formats:

View File

@ -842,7 +842,9 @@ class TestForeign:
def test_heifload(self):
def heif_valid(im):
a = im(10, 10)
assert_almost_equal_objects(a, [75.0, 86.0, 81.0])
# different versions of HEIC decode have slightly different
# rounding
assert_almost_equal_objects(a, [75.0, 86.0, 81.0], threshold=2)
assert im.width == 4032
assert im.height == 3024
assert im.bands == 3

View File

@ -109,6 +109,25 @@ class TestHistogram:
# new mean should be closer to target mean
assert abs(im.avg() - 128) > abs(im2.avg() - 128)
def test_case(self):
# slice into two at 128, we should get 50% of pixels in each half
x = pyvips.Image.grey(256, 256, uchar=True)
index = pyvips.Image.switch([x < 128, x >= 128])
y = index.case([10, 20])
assert y.avg() == 15
# slice into four
index = pyvips.Image.switch([
x < 64,
x >= 64 and x < 128,
x >= 128 and x < 192,
x >= 192
])
assert index.case([10, 20, 30, 40]).avg() == 25
# values over N should use the last value
assert index.case([10, 20, 30]).avg() == 22.5
if __name__ == '__main__':
pytest.main()