add jp2k tests
and buffer load/save
This commit is contained in:
parent
74d2472966
commit
2575d963bd
@ -20,8 +20,7 @@
|
||||
- add vips_fitsload_source(), vips_niftiload_source()
|
||||
- png and gif load note background colour as metadata [781545872]
|
||||
- add vips_image_[set|get]_array_double()
|
||||
- add vips_jp2kload(), vips_jp2kload_source(), vips_jp2ksave(),
|
||||
vips_jp2ksave_target()
|
||||
- add JPEG2000 load and save
|
||||
|
||||
22/12/20 start 8.10.6
|
||||
- don't seek on bad file descriptors [kleisauke]
|
||||
|
@ -2177,8 +2177,10 @@ vips_foreign_operation_init( void )
|
||||
extern GType vips_foreign_load_svg_source_get_type( void );
|
||||
|
||||
extern GType vips_foreign_load_jp2k_file_get_type( void );
|
||||
extern GType vips_foreign_load_jp2k_buffer_get_type( void );
|
||||
extern GType vips_foreign_load_jp2k_source_get_type( void );
|
||||
extern GType vips_foreign_save_jp2k_file_get_type( void );
|
||||
extern GType vips_foreign_save_jp2k_buffer_get_type( void );
|
||||
extern GType vips_foreign_save_jp2k_target_get_type( void );
|
||||
|
||||
extern GType vips_foreign_load_heif_file_get_type( void );
|
||||
@ -2253,8 +2255,10 @@ vips_foreign_operation_init( void )
|
||||
|
||||
#ifdef HAVE_LIBOPENJP2
|
||||
vips_foreign_load_jp2k_file_get_type();
|
||||
vips_foreign_load_jp2k_buffer_get_type();
|
||||
vips_foreign_load_jp2k_source_get_type();
|
||||
vips_foreign_save_jp2k_file_get_type();
|
||||
vips_foreign_save_jp2k_buffer_get_type();
|
||||
vips_foreign_save_jp2k_target_get_type();
|
||||
#endif /*HAVE_LIBOPENJP2*/
|
||||
|
||||
|
@ -32,9 +32,9 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
#define DEBUG_VERBOSE
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
@ -839,6 +839,84 @@ vips_foreign_load_jp2k_file_init( VipsForeignLoadJp2kFile *jp2k )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadJp2kBuffer {
|
||||
VipsForeignLoadJp2k parent_object;
|
||||
|
||||
/* Load from a buffer.
|
||||
*/
|
||||
VipsArea *buf;
|
||||
|
||||
} VipsForeignLoadJp2kBuffer;
|
||||
|
||||
typedef VipsForeignLoadJp2kClass VipsForeignLoadJp2kBufferClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadJp2kBuffer, vips_foreign_load_jp2k_buffer,
|
||||
vips_foreign_load_jp2k_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_load_jp2k_buffer_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignLoadJp2k *jp2k = (VipsForeignLoadJp2k *) object;
|
||||
VipsForeignLoadJp2kBuffer *buffer =
|
||||
(VipsForeignLoadJp2kBuffer *) object;
|
||||
|
||||
if( buffer->buf )
|
||||
if( !(jp2k->source = vips_source_new_from_memory(
|
||||
VIPS_AREA( buffer->buf )->data,
|
||||
VIPS_AREA( buffer->buf )->length )) )
|
||||
return( -1 );
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_load_jp2k_file_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static gboolean
|
||||
vips_foreign_load_jp2k_buffer_is_a( const void *buf, size_t len )
|
||||
{
|
||||
VipsSource *source;
|
||||
gboolean result;
|
||||
|
||||
if( !(source = vips_source_new_from_memory( buf, len )) )
|
||||
return( FALSE );
|
||||
result = vips_foreign_load_jp2k_is_a_source( source );
|
||||
VIPS_UNREF( source );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_jp2k_buffer_class_init(
|
||||
VipsForeignLoadJp2kBufferClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "jp2kload_buffer";
|
||||
object_class->build = vips_foreign_load_jp2k_buffer_build;
|
||||
|
||||
load_class->is_a_buffer = vips_foreign_load_jp2k_buffer_is_a;
|
||||
|
||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||
_( "Buffer" ),
|
||||
_( "Buffer to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadJp2kBuffer, buf ),
|
||||
VIPS_TYPE_BLOB );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_jp2k_buffer_init( VipsForeignLoadJp2kBuffer *buffer )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadJp2kSource {
|
||||
VipsForeignLoadJp2k parent_object;
|
||||
|
||||
@ -939,6 +1017,41 @@ vips_jp2kload( const char *filename, VipsImage **out, ... )
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_jp2kload_buffer:
|
||||
* @buf: (array length=len) (element-type guint8): memory area to load
|
||||
* @len: (type gsize): size of memory area
|
||||
* @out: (out): image to write
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @page: %gint, load this page
|
||||
*
|
||||
* Exactly as vips_jp2kload(), but read from a source.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_jp2kload_buffer( void *buf, size_t len, VipsImage **out, ... )
|
||||
{
|
||||
va_list ap;
|
||||
VipsBlob *blob;
|
||||
int result;
|
||||
|
||||
/* We don't take a copy of the data or free it.
|
||||
*/
|
||||
blob = vips_blob_new( NULL, buf, len );
|
||||
|
||||
va_start( ap, out );
|
||||
result = vips_call_split( "jp2kload_buffer", ap, blob, out );
|
||||
va_end( ap );
|
||||
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_jp2kload_source:
|
||||
* @source: source to load from
|
||||
|
@ -615,6 +615,70 @@ vips_foreign_save_jp2k_file_init( VipsForeignSaveJp2kFile *file )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveJp2kBuffer {
|
||||
VipsForeignSaveJp2k parent_object;
|
||||
|
||||
/* Save to a buffer.
|
||||
*/
|
||||
VipsArea *buf;
|
||||
|
||||
} VipsForeignSaveJp2kBuffer;
|
||||
|
||||
typedef VipsForeignSaveJp2kClass VipsForeignSaveJp2kBufferClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveJp2kBuffer, vips_foreign_save_jp2k_buffer,
|
||||
vips_foreign_save_jp2k_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_save_jp2k_buffer_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignSaveJp2k *jp2k = (VipsForeignSaveJp2k *) object;
|
||||
VipsForeignSaveJp2kBuffer *buffer =
|
||||
(VipsForeignSaveJp2kBuffer *) object;
|
||||
|
||||
VipsBlob *blob;
|
||||
|
||||
if( !(jp2k->target = vips_target_new_to_memory()) )
|
||||
return( -1 );
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_jp2k_buffer_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
g_object_get( jp2k->target, "blob", &blob, NULL );
|
||||
g_object_set( buffer, "buffer", blob, NULL );
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_jp2k_buffer_class_init(
|
||||
VipsForeignSaveJp2kBufferClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "jp2ksave_buffer";
|
||||
object_class->build = vips_foreign_save_jp2k_buffer_build;
|
||||
|
||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||
_( "Buffer" ),
|
||||
_( "Buffer to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_OUTPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveJp2kBuffer, buf ),
|
||||
VIPS_TYPE_BLOB );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_jp2k_buffer_init( VipsForeignSaveJp2kBuffer *buffer )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveJp2kTarget {
|
||||
VipsForeignSaveJp2k parent_object;
|
||||
|
||||
@ -715,6 +779,54 @@ vips_jp2ksave( VipsImage *in, const char *filename, ... )
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_jp2ksave_buffer: (method)
|
||||
* @in: image to save
|
||||
* @buf: (array length=len) (element-type guint8): return output buffer here
|
||||
* @len: (type gsize): return output length here
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @Q: %gint, quality factor
|
||||
* * @lossless: %gboolean, enables lossless compression
|
||||
* * @tile_width: %gint for tile size
|
||||
* * @tile_height: %gint for tile size
|
||||
*
|
||||
* As vips_jp2ksave(), but save to a target.
|
||||
*
|
||||
* See also: vips_jp2ksave(), vips_image_write_to_target().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_jp2ksave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
{
|
||||
va_list ap;
|
||||
VipsArea *area;
|
||||
int result;
|
||||
|
||||
area = NULL;
|
||||
|
||||
va_start( ap, len );
|
||||
result = vips_call_split( "jp2ksave_buffer", ap, in, &area );
|
||||
va_end( ap );
|
||||
|
||||
if( !result &&
|
||||
area ) {
|
||||
if( buf ) {
|
||||
*buf = area->data;
|
||||
area->free_fn = NULL;
|
||||
}
|
||||
if( len )
|
||||
*len = area->length;
|
||||
|
||||
vips_area_unref( area );
|
||||
}
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_jp2ksave_target: (method)
|
||||
* @in: image to save
|
||||
|
@ -678,10 +678,14 @@ int vips_niftisave( VipsImage *in, const char *filename, ... )
|
||||
|
||||
int vips_jp2kload( const char *filename, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_jp2kload_buffer( void *buf, size_t len, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_jp2kload_source( VipsSource *source, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_jp2ksave( VipsImage *in, const char *filename, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_heifsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_jp2ksave_target( VipsImage *in, VipsTarget *target, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
|
@ -56,6 +56,7 @@ MOSAIC_MARKS = [[489, 140], [66, 141],
|
||||
MOSAIC_VERTICAL_MARKS = [[388, 44], [364, 346],
|
||||
[384, 17], [385, 629],
|
||||
[527, 42], [503, 959]]
|
||||
JP2K_FILE = os.path.join(IMAGES, "world.jp2")
|
||||
|
||||
unsigned_formats = [pyvips.BandFormat.UCHAR,
|
||||
pyvips.BandFormat.USHORT,
|
||||
|
BIN
test/test-suite/images/world.jp2
Normal file
BIN
test/test-suite/images/world.jp2
Normal file
Binary file not shown.
@ -18,7 +18,7 @@ from helpers import \
|
||||
GIF_ANIM_DISPOSE_PREVIOUS_EXPECTED_PNG_FILE, \
|
||||
temp_filename, assert_almost_equal_objects, have, skip_if_no, \
|
||||
TIF1_FILE, TIF2_FILE, TIF4_FILE, WEBP_LOOKS_LIKE_SVG_FILE, \
|
||||
WEBP_ANIMATED_FILE
|
||||
WEBP_ANIMATED_FILE, JP2K_FILE
|
||||
|
||||
class TestForeign:
|
||||
tempdir = None
|
||||
@ -1136,6 +1136,32 @@ class TestForeign:
|
||||
y = pyvips.Image.new_from_buffer(buf, "")
|
||||
assert y.get("exif-ifd0-Make").split(" ")[0] == "banana"
|
||||
|
||||
@skip_if_no("jp2kload")
|
||||
def test_jp2kload(self):
|
||||
def jp2k_valid(im):
|
||||
a = im(402, 73)
|
||||
assert_almost_equal_objects(a, [141, 144, 73], threshold=2)
|
||||
assert im.width == 800
|
||||
assert im.height == 400
|
||||
assert im.bands == 3
|
||||
|
||||
self.file_loader("jp2kload", JP2K_FILE, jp2k_valid)
|
||||
self.buffer_loader("jp2kload_buffer", JP2K_FILE, jp2k_valid)
|
||||
|
||||
@skip_if_no("jp2ksave")
|
||||
def test_jp2ksave(self):
|
||||
self.save_load_buffer("jp2ksave_buffer", "jp2kload_buffer",
|
||||
self.colour, 80)
|
||||
|
||||
buf = self.colour.jp2ksave_buffer(lossless=True)
|
||||
im2 = pyvips.Image.new_from_buffer(buf, "")
|
||||
assert self.colour.avg() == im2.avg()
|
||||
|
||||
# higher Q should mean a bigger buffer
|
||||
b1 = self.mono.jp2ksave_buffer(Q=10)
|
||||
b2 = self.mono.jp2ksave_buffer(Q=90)
|
||||
assert len(b2) > len(b1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main()
|
||||
|
Loading…
Reference in New Issue
Block a user