first try, not very good

This commit is contained in:
John Cupitt 2014-10-16 19:16:45 +01:00
parent 2acd185ee2
commit 14d7ce1e91
8 changed files with 176 additions and 54 deletions

15
TODO
View File

@ -1,11 +1,14 @@
- the jpeg loader needs _header() and _load() to return the same dimensions
- python:
this is going to be tough with read_jpeg_image(): it uses the read-header
thing to set up the image that it generates
- update examples with new image constant rules
update blog post with new examples/try12.py
- do more tests
- update blog post with new examples/try12.py
- fix up aconv
- put exif autorotate into jpeg load
@ -13,12 +16,12 @@
https://github.com/jcupitt/ruby-vips/issues/53
- more python tests
- can we pick the vipsthumbnail int shrink factor more intelligently?
- vips_resize() should use vipsthumbnail's block/cache/affine/sharpen chain
- fix up aconv
- rewrite im_conv() etc. as vips_conv(), also the mosaicing functions
finally finish --disable-deprecated option

View File

@ -113,7 +113,7 @@ jpeg2vips( const char *name, IMAGE *out, gboolean header_only )
#ifdef HAVE_JPEG
if( vips__jpeg_read_file( filename, out,
header_only, shrink, fail_on_warn, TRUE ) )
header_only, shrink, fail_on_warn, TRUE, FALSE ) )
return( -1 );
#else
vips_error( "im_jpeg2vips",

View File

@ -682,36 +682,6 @@ vips_foreign_load_new_from_string( const char *string )
return( VIPS_OBJECT( load ) );
}
static guint64
vips_get_disc_threshold( void )
{
static gboolean done = FALSE;
static guint64 threshold;
if( !done ) {
const char *env;
done = TRUE;
/* 100mb default.
*/
threshold = 100 * 1024 * 1024;
if( (env = g_getenv( "VIPS_DISC_THRESHOLD" )) ||
(env = g_getenv( "IM_DISC_THRESHOLD" )) )
threshold = vips__parse_size( env );
if( vips__disc_threshold )
threshold = vips__parse_size( vips__disc_threshold );
#ifdef DEBUG
printf( "vips_get_disc_threshold: %zd bytes\n", threshold );
#endif /*DEBUG*/
}
return( threshold );
}
static VipsImage *
vips_foreign_load_temp( VipsForeignLoad *load )
{
@ -742,12 +712,10 @@ vips_foreign_load_temp( VipsForeignLoad *load )
/* We open via disc if:
* - 'disc' is set
* - disc-threshold has not been set to zero
* - the uncompressed image will be larger than
* vips_get_disc_threshold()
*/
if( load->disc &&
disc_threshold &&
image_size > disc_threshold ) {
#ifdef DEBUG
printf( "vips_foreign_load_temp: disc temp\n" );

View File

@ -59,6 +59,8 @@
* - don't write to our input buffer, thanks Lovell
* 9/9/14
* - support "none" as a resolution unit
* 16/10/14
* - add "autorotate" option
*/
/*
@ -131,6 +133,12 @@
typedef struct _ReadJpeg {
VipsImage *out;
/* The raw image size: out may have width and height swapped if we are
* autorotating.
*/
int width;
int height;
/* Shrink by this much during load. 1, 2, 4, 8.
*/
int shrink;
@ -158,6 +166,10 @@ typedef struct _ReadJpeg {
/* Track the y pos during a read with this.
*/
int y_pos;
/* Use exif tags to automatically rotate and flip image.
*/
gboolean autorotate;
} ReadJpeg;
static int
@ -211,7 +223,8 @@ readjpeg_close( VipsObject *object, ReadJpeg *jpeg )
}
static ReadJpeg *
readjpeg_new( VipsImage *out, int shrink, gboolean fail, gboolean readbehind )
readjpeg_new( VipsImage *out,
int shrink, gboolean fail, gboolean readbehind, gboolean autorotate )
{
ReadJpeg *jpeg;
@ -224,13 +237,12 @@ readjpeg_new( VipsImage *out, int shrink, gboolean fail, gboolean readbehind )
jpeg->readbehind = readbehind;
jpeg->filename = NULL;
jpeg->decompressing = FALSE;
jpeg->cinfo.err = jpeg_std_error( &jpeg->eman.pub );
jpeg->eman.pub.error_exit = vips__new_error_exit;
jpeg->eman.pub.output_message = vips__new_output_message;
jpeg->eman.fp = NULL;
jpeg->y_pos = 0;
jpeg->autorotate = autorotate;
/* jpeg_create_decompress() can fail on some sanity checks. Don't
* readjpeg_free() since we don't want to jpeg_destroy_decompress().
@ -678,6 +690,35 @@ attach_blob( VipsImage *im, const char *field, void *data, int data_length )
return( 0 );
}
#define ORIENTATION ("exif-ifd0-Orientation")
static VipsAngle
get_angle( VipsImage *im )
{
VipsAngle angle;
const char *orientation;
angle = VIPS_ANGLE_D0;
if( vips_image_get_typeof( im, ORIENTATION ) &&
!vips_image_get_string( im, ORIENTATION, &orientation ) ) {
if( vips_isprefix( "6", orientation ) )
angle = VIPS_ANGLE_D90;
else if( vips_isprefix( "8", orientation ) )
angle = VIPS_ANGLE_D270;
else if( vips_isprefix( "3", orientation ) )
angle = VIPS_ANGLE_D180;
/* Other values do rotate + mirror, don't bother handling them
* though, how common can mirroring be.
*
* See:
*
* http://www.80sidea.com/archives/2316
*/
}
return( angle );
}
/* Number of app2 sections we can capture. Each one can be 64k, so 6400k should
* be enough for anyone (haha).
*/
@ -892,6 +933,20 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out )
(VipsCallbackFn) vips_free, data, data_length );
}
/* Swap width and height if we're going to rotate this image. Keep the
* unrotated dimensions around too.
*/
jpeg->width = out->Xsize;
jpeg->height = out->Ysize;
if( jpeg->autorotate ) {
VipsAngle angle = get_angle( out );
if( angle == VIPS_ANGLE_D90 ||
angle == VIPS_ANGLE_D270 )
VIPS_SWAP( int, out->Xsize, out->Ysize )
}
return( 0 );
}
@ -966,6 +1021,35 @@ read_jpeg_generate( VipsRegion *or,
return( 0 );
}
/* Auto-rotate, if rotate_image is set.
*/
static VipsImage *
read_jpeg_rotate( VipsObject *process, VipsImage *im )
{
VipsImage **t = (VipsImage **) vips_object_local_array( process, 2 );
VipsAngle angle = get_angle( im );
if( angle != VIPS_ANGLE_D0 ) {
/* Need to copy to memory or disc, we have to stay seq.
*/
const guint64 image_size = VIPS_IMAGE_SIZEOF_IMAGE( im );
const guint64 disc_threshold = vips_get_disc_threshold();
if( image_size > disc_threshold )
t[0] = vips_image_new_temp_file( "%s.v" );
else
t[0] = vips_image_new_memory();
if( vips_image_write( im, t[0] ) ||
vips_rot( t[0], &t[1], angle, NULL ) )
return( NULL );
im = t[1];
(void) vips_image_remove( im, ORIENTATION );
}
return( im );
}
/* Read a cinfo to a VIPS image.
*/
static int
@ -975,6 +1059,8 @@ read_jpeg_image( ReadJpeg *jpeg, VipsImage *out )
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( out ), 3 );
VipsImage *im;
/* Here for longjmp() from vips__new_error_exit().
*/
if( setjmp( jpeg->eman.jmp ) )
@ -1002,8 +1088,14 @@ read_jpeg_image( ReadJpeg *jpeg, VipsImage *out )
"access", jpeg->readbehind ?
VIPS_ACCESS_SEQUENTIAL :
VIPS_ACCESS_SEQUENTIAL_UNBUFFERED,
NULL ) ||
vips_image_write( t[1], out ) )
NULL ) )
return( -1 );
im = t[1];
if( jpeg->autorotate )
im = read_jpeg_rotate( VIPS_OBJECT( out ), im );
if( vips_image_write( im, out ) )
return( -1 );
return( 0 );
@ -1013,12 +1105,14 @@ read_jpeg_image( ReadJpeg *jpeg, VipsImage *out )
*/
int
vips__jpeg_read_file( const char *filename, VipsImage *out,
gboolean header_only, int shrink, gboolean fail, gboolean readbehind )
gboolean header_only, int shrink, gboolean fail, gboolean readbehind,
gboolean autorotate )
{
ReadJpeg *jpeg;
int result;
if( !(jpeg = readjpeg_new( out, shrink, fail, readbehind )) )
if( !(jpeg = readjpeg_new( out,
shrink, fail, readbehind, autorotate )) )
return( -1 );
/* Here for longjmp() from vips__new_error_exit() during startup.
@ -1231,12 +1325,14 @@ readjpeg_buffer (ReadJpeg *jpeg, void *buf, size_t len)
int
vips__jpeg_read_buffer( void *buf, size_t len, VipsImage *out,
gboolean header_only, int shrink, int fail, gboolean readbehind )
gboolean header_only, int shrink, int fail, gboolean readbehind,
gboolean autorotate )
{
ReadJpeg *jpeg;
int result;
if( !(jpeg = readjpeg_new( out, shrink, fail, readbehind )) )
if( !(jpeg = readjpeg_new( out,
shrink, fail, readbehind, autorotate )) )
return( -1 );
if( setjmp( jpeg->eman.jmp ) ) {

View File

@ -81,6 +81,10 @@ typedef struct _VipsForeignLoadJpeg {
*/
gboolean fail;
/* Autorotate using exif orientation tag.
*/
gboolean autorotate;
} VipsForeignLoadJpeg;
typedef VipsForeignLoadClass VipsForeignLoadJpegClass;
@ -146,6 +150,13 @@ vips_foreign_load_jpeg_class_init( VipsForeignLoadJpegClass *class )
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadJpeg, fail ),
FALSE );
VIPS_ARG_BOOL( class, "autorotate", 12,
_( "Autorotate" ),
_( "Automatically rotate image using exif orientation" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadJpeg, autorotate ),
FALSE );
}
static void
@ -189,7 +200,7 @@ vips_foreign_load_jpeg_file_header( VipsForeignLoad *load )
VipsForeignLoadJpegFile *file = (VipsForeignLoadJpegFile *) load;
if( vips__jpeg_read_file( file->filename, load->out,
TRUE, jpeg->shrink, jpeg->fail, FALSE ) )
TRUE, jpeg->shrink, jpeg->fail, FALSE, jpeg->autorotate ) )
return( -1 );
VIPS_SETSTR( load->out->filename, file->filename );
@ -205,7 +216,7 @@ vips_foreign_load_jpeg_file_load( VipsForeignLoad *load )
if( vips__jpeg_read_file( file->filename, load->real,
FALSE, jpeg->shrink, jpeg->fail,
load->access == VIPS_ACCESS_SEQUENTIAL ) )
load->access == VIPS_ACCESS_SEQUENTIAL, jpeg->autorotate ) )
return( -1 );
return( 0 );
@ -269,7 +280,8 @@ vips_foreign_load_jpeg_buffer_header( VipsForeignLoad *load )
VipsForeignLoadJpegBuffer *buffer = (VipsForeignLoadJpegBuffer *) load;
if( vips__jpeg_read_buffer( buffer->buf->data, buffer->buf->length,
load->out, TRUE, jpeg->shrink, jpeg->fail, FALSE ) )
load->out, TRUE, jpeg->shrink, jpeg->fail, FALSE,
jpeg->autorotate ) )
return( -1 );
return( 0 );
@ -283,7 +295,7 @@ vips_foreign_load_jpeg_buffer_load( VipsForeignLoad *load )
if( vips__jpeg_read_buffer( buffer->buf->data, buffer->buf->length,
load->real, FALSE, jpeg->shrink, jpeg->fail,
load->access == VIPS_ACCESS_SEQUENTIAL ) )
load->access == VIPS_ACCESS_SEQUENTIAL, jpeg->autorotate ) )
return( -1 );
return( 0 );

View File

@ -49,9 +49,11 @@ int vips__jpeg_write_buffer( VipsImage *in,
int vips__isjpeg_buffer( void *buf, size_t len );
int vips__isjpeg( const char *filename );
int vips__jpeg_read_file( const char *name, VipsImage *out,
gboolean header_only, int shrink, gboolean fail, gboolean readbehind );
gboolean header_only, int shrink, gboolean fail, gboolean readbehind,
gboolean autorotate );
int vips__jpeg_read_buffer( void *buf, size_t len, VipsImage *out,
gboolean header_only, int shrink, int fail, gboolean readbehind );
gboolean header_only, int shrink, int fail, gboolean readbehind,
gboolean autorotate );
#ifdef __cplusplus
}

View File

@ -427,6 +427,7 @@ VipsImage *vips_image_new_matrix_from_array( int width, int height,
double *array, int size );
void vips_image_set_delete_on_close( VipsImage *image,
gboolean delete_on_close );
guint64 vips_get_disc_threshold( void );
VipsImage *vips_image_new_temp_file( const char *format );
int vips_image_write( VipsImage *image, VipsImage *out );

View File

@ -2204,6 +2204,46 @@ vips_image_set_delete_on_close( VipsImage *image, gboolean delete_on_close )
VIPS_SETSTR( image->delete_on_close_filename, image->filename );
}
/**
* vips_get_disc_threshold:
*
* Return the number of bytes at which we flip between open via memory and
* open via disc. This defaults to 100mb, but can be changed with the
* VIPS_DISC_THRESHOLD environment variable or the --vips-disc-threshold
* command-line flag. See vips_image_new_from_file().
*
* Returns: disc threshold in bytes.
*/
guint64
vips_get_disc_threshold( void )
{
static gboolean done = FALSE;
static guint64 threshold;
if( !done ) {
const char *env;
done = TRUE;
/* 100mb default.
*/
threshold = 100 * 1024 * 1024;
if( (env = g_getenv( "VIPS_DISC_THRESHOLD" )) ||
(env = g_getenv( "IM_DISC_THRESHOLD" )) )
threshold = vips__parse_size( env );
if( vips__disc_threshold )
threshold = vips__parse_size( vips__disc_threshold );
#ifdef DEBUG
printf( "vips_get_disc_threshold: %zd bytes\n", threshold );
#endif /*DEBUG*/
}
return( threshold );
}
/**
* vips_image_new_temp_file:
* @format: format of file