Merge branch 'master' into add-libnsgif
18
ChangeLog
|
@ -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
|
||||
|
|
62
configure.ac
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
After Width: | Height: | Size: 44 B |
After Width: | Height: | Size: 289 B |
Before Width: | Height: | Size: 71 B After Width: | Height: | Size: 71 B |
|
@ -0,0 +1 @@
|
|||
GIF8 =・
|
Before Width: | Height: | Size: 166 B After Width: | Height: | Size: 166 B |
Before Width: | Height: | Size: 97 B After Width: | Height: | Size: 97 B |
|
@ -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
|
|
@ -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
|
|
|
@ -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
|
|
@ -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%&''%%%$" $$&$#"#
|
|
@ -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$&/#'.!(.!*. --.+.)-," ,"!/##-!#,&+'-*+*'(#%
|
|
@ -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 );
|
||||
}
|
|
@ -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 );
|
||||
}
|
|
@ -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 );
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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++ ) \
|
||||
|
|
|
@ -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++ ) \
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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++ ) { \
|
||||
|
|
|
@ -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 ) )
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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]) /
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
@ -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" );
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ libhistogram_la_SOURCES = \
|
|||
histogram.c \
|
||||
phistogram.h \
|
||||
maplut.c \
|
||||
case.c \
|
||||
hist_unary.c \
|
||||
hist_unary.h \
|
||||
hist_cum.c \
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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*/
|
||||
|
|
|
@ -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 )) )
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ) ) {
|
||||
|
|
239
m4/python.m4
|
@ -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
|
||||
|
||||
])
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|