implement shrink-on-load for openslide thumbs
makes vipsthumbnail much quicker on openslide images, obviously see https://github.com/libvips/libvips/issues/1149
This commit is contained in:
parent
aaf26765f2
commit
22ba9106b5
@ -3,6 +3,7 @@
|
||||
- add low/high to smartcrop [jcupitt]
|
||||
- add XMP support to png read/write [jcupitt]
|
||||
- deprecate thumbnail auto_rotate, add no_rotate [jcupitt]
|
||||
- implement thumbnail shrink-on-load for openslide images [jcupitt]
|
||||
|
||||
23/9/18 started 8.7.1
|
||||
- update function list in docs [janko-m]
|
||||
|
@ -13,6 +13,7 @@
|
||||
* - add intent option, thanks kleisauke
|
||||
* 31/10/18
|
||||
* - deprecate auto_rotate, add no_rotate
|
||||
* - implement shrink-on-load for openslide images
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -72,6 +73,10 @@
|
||||
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
|
||||
VIPS_TYPE_THUMBNAIL, VipsThumbnailClass ))
|
||||
|
||||
/* Should be plenty.
|
||||
*/
|
||||
#define MAX_LEVELS (256)
|
||||
|
||||
typedef struct _VipsThumbnail {
|
||||
VipsOperation parent_instance;
|
||||
|
||||
@ -88,18 +93,20 @@ typedef struct _VipsThumbnail {
|
||||
char *import_profile;
|
||||
VipsIntent intent;
|
||||
|
||||
/* Set by subclasses to the input image.
|
||||
*/
|
||||
VipsImage *in;
|
||||
|
||||
/* Bits of info we read from the input image when we get the header of
|
||||
* the original.
|
||||
*/
|
||||
const char *loader; /* Eg. "jpegload_buffer" */
|
||||
const char *loader; /* Eg. "VipsForeignLoadJpeg*" */
|
||||
int input_width;
|
||||
int input_height;
|
||||
VipsAngle angle; /* From vips_autorot_get_angle() */
|
||||
|
||||
/* For openslide, we need to read out the size of each level too.
|
||||
*/
|
||||
int level_count;
|
||||
int level_width[MAX_LEVELS];
|
||||
int level_height[MAX_LEVELS];
|
||||
|
||||
} VipsThumbnail;
|
||||
|
||||
typedef struct _VipsThumbnailClass {
|
||||
@ -109,11 +116,13 @@ typedef struct _VipsThumbnailClass {
|
||||
*/
|
||||
int (*get_info)( VipsThumbnail *thumbnail );
|
||||
|
||||
/* Open, giving either a scale or a shrink. @shrink is an integer shrink
|
||||
* factor suitable for vips_jpegload() or equivalent, @scale is a
|
||||
* double scale factor, suitable for vips_svgload() or similar.
|
||||
/* Open with some kind of shrink or scale factor. Exactly what we pass
|
||||
* and to what param depends on the loader. It'll be an integer shrink
|
||||
* factor for vips_jpegload(), a double scale factor for vips_svgload().
|
||||
*
|
||||
* See VipsThumbnail::loader
|
||||
*/
|
||||
VipsImage *(*open)( VipsThumbnail *thumbnail, int shrink, double scale );
|
||||
VipsImage *(*open)( VipsThumbnail *thumbnail, double factor );
|
||||
|
||||
} VipsThumbnailClass;
|
||||
|
||||
@ -143,6 +152,53 @@ vips_thumbnail_finalize( GObject *gobject )
|
||||
G_OBJECT_CLASS( vips_thumbnail_parent_class )->finalize( gobject );
|
||||
}
|
||||
|
||||
/* Fetch an int openslide field from metadata. These are all represented as
|
||||
* strings. Return the defaulyt value if there's any problem.
|
||||
*/
|
||||
static int
|
||||
get_int( VipsImage *image, const char *field, int default_value )
|
||||
{
|
||||
const char *str;
|
||||
|
||||
if( vips_image_get_typeof( image, field ) &&
|
||||
!vips_image_get_string( image, field, &str ) )
|
||||
return( atoi( str ) );
|
||||
|
||||
return( default_value );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_thumbnail_read_header( VipsThumbnail *thumbnail, VipsImage *image )
|
||||
{
|
||||
thumbnail->input_width = image->Xsize;
|
||||
thumbnail->input_height = image->Ysize;
|
||||
thumbnail->angle = vips_autorot_get_angle( image );
|
||||
|
||||
/* For openslide, read out the level structure too.
|
||||
*/
|
||||
if( vips_isprefix( "VipsForeignLoadOpenslide", thumbnail->loader ) ) {
|
||||
int level_count;
|
||||
int level;
|
||||
|
||||
level_count = get_int( image, "openslide.level-count", 1 );
|
||||
level_count = VIPS_CLIP( 1, level_count, MAX_LEVELS );
|
||||
thumbnail->level_count = level_count;
|
||||
|
||||
for( level = 0; level < level_count; level++ ) {
|
||||
char name[256];
|
||||
|
||||
vips_snprintf( name, 256,
|
||||
"openslide.level[%d].width", level );
|
||||
thumbnail->level_width[level] =
|
||||
get_int( image, name, 0 );
|
||||
vips_snprintf( name, 256,
|
||||
"openslide.level[%d].height", level );
|
||||
thumbnail->level_height[level] =
|
||||
get_int( image, name, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the shrink factor, taking into account auto-rotate, the fit mode,
|
||||
* and so on.
|
||||
*
|
||||
@ -225,7 +281,8 @@ vips_thumbnail_calculate_common_shrink( VipsThumbnail *thumbnail,
|
||||
/* Find the best jpeg preload shrink.
|
||||
*/
|
||||
static int
|
||||
vips_thumbnail_find_jpegshrink( VipsThumbnail *thumbnail, int width, int height )
|
||||
vips_thumbnail_find_jpegshrink( VipsThumbnail *thumbnail,
|
||||
int width, int height )
|
||||
{
|
||||
double shrink = vips_thumbnail_calculate_common_shrink( thumbnail,
|
||||
width, height );
|
||||
@ -253,6 +310,26 @@ vips_thumbnail_find_jpegshrink( VipsThumbnail *thumbnail, int width, int height
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
/* Find the best openslide level.
|
||||
*/
|
||||
static int
|
||||
vips_thumbnail_find_openslidelevel( VipsThumbnail *thumbnail,
|
||||
int width, int height )
|
||||
{
|
||||
int level;
|
||||
|
||||
g_assert( thumbnail->level_count > 0 );
|
||||
g_assert( thumbnail->level_count <= MAX_LEVELS );
|
||||
|
||||
for( level = thumbnail->level_count - 1; level >= 0; level-- )
|
||||
if( vips_thumbnail_calculate_common_shrink( thumbnail,
|
||||
thumbnail->level_width[level],
|
||||
thumbnail->level_height[level] ) >= 1.0 )
|
||||
return( level );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Open the image, returning the best version for thumbnailing.
|
||||
*
|
||||
* For example, libjpeg supports fast shrink-on-read, so if we have a JPEG,
|
||||
@ -264,8 +341,7 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
|
||||
VipsThumbnailClass *class = VIPS_THUMBNAIL_GET_CLASS( thumbnail );
|
||||
|
||||
VipsImage *im;
|
||||
double shrink;
|
||||
double scale;
|
||||
double factor;
|
||||
|
||||
if( class->get_info( thumbnail ) )
|
||||
return( NULL );
|
||||
@ -273,32 +349,40 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
|
||||
g_info( "input size is %d x %d",
|
||||
thumbnail->input_width, thumbnail->input_height );
|
||||
|
||||
shrink = 1.0;
|
||||
scale = 1.0;
|
||||
factor = 1.0;
|
||||
|
||||
if( vips_isprefix( "VipsForeignLoadJpeg", thumbnail->loader ) ) {
|
||||
shrink = vips_thumbnail_find_jpegshrink( thumbnail,
|
||||
factor = vips_thumbnail_find_jpegshrink( thumbnail,
|
||||
thumbnail->input_width, thumbnail->input_height );
|
||||
|
||||
g_info( "loading jpeg with factor %g pre-shrink", shrink );
|
||||
g_info( "loading jpeg with factor %g pre-shrink", factor );
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadOpenslide",
|
||||
thumbnail->loader ) ) {
|
||||
factor = vips_thumbnail_find_openslidelevel( thumbnail,
|
||||
thumbnail->input_width, thumbnail->input_height );
|
||||
|
||||
g_info( "loading openslide level %g", factor );
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ||
|
||||
vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ) {
|
||||
scale = 1.0 / vips_thumbnail_calculate_common_shrink( thumbnail,
|
||||
thumbnail->input_width, thumbnail->input_height );
|
||||
factor = 1.0 /
|
||||
vips_thumbnail_calculate_common_shrink( thumbnail,
|
||||
thumbnail->input_width,
|
||||
thumbnail->input_height );
|
||||
|
||||
g_info( "loading PDF/SVG with factor %g pre-scale", scale );
|
||||
g_info( "loading PDF/SVG with factor %g pre-scale", factor );
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ) {
|
||||
shrink = VIPS_MAX( 1.0,
|
||||
factor = VIPS_MAX( 1.0,
|
||||
vips_thumbnail_calculate_common_shrink( thumbnail,
|
||||
thumbnail->input_width,
|
||||
thumbnail->input_height ) );
|
||||
|
||||
g_info( "loading webp with factor %g pre-shrink", shrink );
|
||||
g_info( "loading webp with factor %g pre-shrink", factor );
|
||||
}
|
||||
|
||||
if( !(im = class->open( thumbnail, shrink, scale )) )
|
||||
if( !(im = class->open( thumbnail, factor )) )
|
||||
return( NULL );
|
||||
|
||||
return( im );
|
||||
@ -668,43 +752,47 @@ vips_thumbnail_file_get_info( VipsThumbnail *thumbnail )
|
||||
!(image = vips_image_new_from_file( file->filename, NULL )) )
|
||||
return( -1 );
|
||||
|
||||
thumbnail->input_width = image->Xsize;
|
||||
thumbnail->input_height = image->Ysize;
|
||||
thumbnail->angle = vips_autorot_get_angle( image );
|
||||
vips_thumbnail_read_header( thumbnail, image );
|
||||
|
||||
g_object_unref( image );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Open an image, pre-shrinking as appropriate. Some formats use shrink, some
|
||||
* scale, never both.
|
||||
/* Open an image, pre-shrinking as appropriate.
|
||||
*/
|
||||
static VipsImage *
|
||||
vips_thumbnail_file_open( VipsThumbnail *thumbnail, int shrink, double scale )
|
||||
vips_thumbnail_file_open( VipsThumbnail *thumbnail, double factor )
|
||||
{
|
||||
VipsThumbnailFile *file = (VipsThumbnailFile *) thumbnail;
|
||||
|
||||
/* If both shrink and scale have been set, something is wrong. It
|
||||
* should be one or the other (or neither).
|
||||
*/
|
||||
g_assert( shrink == 1 || scale == 1.0 );
|
||||
|
||||
if( shrink != 1 )
|
||||
if( vips_isprefix( "VipsForeignLoadJpeg", thumbnail->loader ) ) {
|
||||
return( vips_image_new_from_file( file->filename,
|
||||
"access", VIPS_ACCESS_SEQUENTIAL,
|
||||
"shrink", shrink,
|
||||
"shrink", (int) factor,
|
||||
NULL ) );
|
||||
else if( scale != 1.0 )
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadOpenslide",
|
||||
thumbnail->loader ) ) {
|
||||
return( vips_image_new_from_file( file->filename,
|
||||
"access", VIPS_ACCESS_SEQUENTIAL,
|
||||
"scale", scale,
|
||||
"level", (int) factor,
|
||||
NULL ) );
|
||||
else
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ||
|
||||
vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ||
|
||||
vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ) {
|
||||
return( vips_image_new_from_file( file->filename,
|
||||
"access", VIPS_ACCESS_SEQUENTIAL,
|
||||
"scale", factor,
|
||||
NULL ) );
|
||||
}
|
||||
else {
|
||||
return( vips_image_new_from_file( file->filename,
|
||||
"access", VIPS_ACCESS_SEQUENTIAL,
|
||||
NULL ) );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vips_thumbnail_file_class_init( VipsThumbnailClass *class )
|
||||
@ -842,44 +930,51 @@ vips_thumbnail_buffer_get_info( VipsThumbnail *thumbnail )
|
||||
buffer->buf->data, buffer->buf->length, "", NULL )) )
|
||||
return( -1 );
|
||||
|
||||
thumbnail->input_width = image->Xsize;
|
||||
thumbnail->input_height = image->Ysize;
|
||||
thumbnail->angle = vips_autorot_get_angle( image );
|
||||
vips_thumbnail_read_header( thumbnail, image );
|
||||
|
||||
g_object_unref( image );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Open an image, pre-shrinking as appropriate. Some formats use shrink, some
|
||||
* scale, never both.
|
||||
/* Open an image, scaling as appropriate.
|
||||
*/
|
||||
static VipsImage *
|
||||
vips_thumbnail_buffer_open( VipsThumbnail *thumbnail,
|
||||
int shrink, double scale )
|
||||
vips_thumbnail_buffer_open( VipsThumbnail *thumbnail, double factor )
|
||||
{
|
||||
VipsThumbnailBuffer *buffer = (VipsThumbnailBuffer *) thumbnail;
|
||||
|
||||
/* We can't use UNBUFERRED safely on very-many-core systems.
|
||||
*/
|
||||
if( shrink != 1 )
|
||||
if( vips_isprefix( "VipsForeignLoadJpeg", thumbnail->loader ) ) {
|
||||
return( vips_image_new_from_buffer(
|
||||
buffer->buf->data, buffer->buf->length, "",
|
||||
"access", VIPS_ACCESS_SEQUENTIAL,
|
||||
"shrink", shrink,
|
||||
"shrink", (int) factor,
|
||||
NULL ) );
|
||||
else if( scale != 1.0 )
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadOpenslide",
|
||||
thumbnail->loader ) ) {
|
||||
return( vips_image_new_from_buffer(
|
||||
buffer->buf->data, buffer->buf->length, "",
|
||||
"access", VIPS_ACCESS_SEQUENTIAL,
|
||||
"scale", scale,
|
||||
"level", (int) factor,
|
||||
NULL ) );
|
||||
else
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ||
|
||||
vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ||
|
||||
vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ) {
|
||||
return( vips_image_new_from_buffer(
|
||||
buffer->buf->data, buffer->buf->length, "",
|
||||
"access", VIPS_ACCESS_SEQUENTIAL,
|
||||
"scale", factor,
|
||||
NULL ) );
|
||||
}
|
||||
else {
|
||||
return( vips_image_new_from_buffer(
|
||||
buffer->buf->data, buffer->buf->length, "",
|
||||
"access", VIPS_ACCESS_SEQUENTIAL,
|
||||
NULL ) );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vips_thumbnail_buffer_class_init( VipsThumbnailClass *class )
|
||||
@ -978,9 +1073,7 @@ vips_thumbnail_image_get_info( VipsThumbnail *thumbnail )
|
||||
*/
|
||||
thumbnail->loader = "image source";
|
||||
|
||||
thumbnail->input_width = image->in->Xsize;
|
||||
thumbnail->input_height = image->in->Ysize;
|
||||
thumbnail->angle = vips_autorot_get_angle( image->in );
|
||||
vips_thumbnail_read_header( thumbnail, image->in );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -988,8 +1081,7 @@ vips_thumbnail_image_get_info( VipsThumbnail *thumbnail )
|
||||
/* Open an image. We can't pre-shrink with an image source, sadly.
|
||||
*/
|
||||
static VipsImage *
|
||||
vips_thumbnail_image_open( VipsThumbnail *thumbnail,
|
||||
int shrink, double scale )
|
||||
vips_thumbnail_image_open( VipsThumbnail *thumbnail, double factor )
|
||||
{
|
||||
VipsThumbnailImage *image = (VipsThumbnailImage *) thumbnail;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user