Merge branch 'add-force-size'
This commit is contained in:
commit
a10787baa3
@ -5,6 +5,7 @@
|
||||
- add new_from_image() to Python as well
|
||||
- slight change to cpp new_from_image() to match py/C behaviour
|
||||
- vips_conv(), vips_compass(), vips_convsep() default to FLOAT precision
|
||||
- add FORCE resize mode to break aspect ratio
|
||||
|
||||
23/4/17 started 8.5.5
|
||||
- doc polishing
|
||||
|
2
TODO
2
TODO
@ -1,6 +1,6 @@
|
||||
- vips_compass() needs docs
|
||||
|
||||
- cpp bandjoin should use bandjoin_const() where possibel ... currently
|
||||
- cpp bandjoin should use bandjoin_const() where possible ... currently
|
||||
uses new_from_image
|
||||
|
||||
- not sure about utf8 error messages on win
|
||||
|
@ -102,7 +102,7 @@ example:
|
||||
$ vips shrink fred.png jim.png 10 10
|
||||
```
|
||||
|
||||
meaning shrink `fred.png` by a factor of 10 in both axies and write as
|
||||
meaning shrink `fred.png` by a factor of 10 in both axes and write as
|
||||
`jim.png`.
|
||||
|
||||
You can imagine this operation running without needing `fred.png` to be
|
||||
|
@ -51,6 +51,7 @@ typedef enum {
|
||||
VIPS_SIZE_BOTH,
|
||||
VIPS_SIZE_UP,
|
||||
VIPS_SIZE_DOWN,
|
||||
VIPS_SIZE_FORCE,
|
||||
VIPS_SIZE_LAST
|
||||
} VipsSize;
|
||||
|
||||
|
@ -831,6 +831,7 @@ vips_size_get_type( void )
|
||||
{VIPS_SIZE_BOTH, "VIPS_SIZE_BOTH", "both"},
|
||||
{VIPS_SIZE_UP, "VIPS_SIZE_UP", "up"},
|
||||
{VIPS_SIZE_DOWN, "VIPS_SIZE_DOWN", "down"},
|
||||
{VIPS_SIZE_FORCE, "VIPS_SIZE_FORCE", "force"},
|
||||
{VIPS_SIZE_LAST, "VIPS_SIZE_LAST", "last"},
|
||||
{0, NULL, NULL}
|
||||
};
|
||||
|
@ -87,9 +87,10 @@
|
||||
* @VIPS_SIZE_BOTH: size both up and down
|
||||
* @VIPS_SIZE_UP: only upsize
|
||||
* @VIPS_SIZE_DOWN: only downsize
|
||||
* @VIPS_SIZE_FORCE: force size, that is, break aspect ratio
|
||||
*
|
||||
* Controls whether an operation should upsize, downsize, or both up and
|
||||
* downsize.
|
||||
* Controls whether an operation should upsize, downsize, both up and
|
||||
* downsize, or force a size.
|
||||
*
|
||||
* See also: vips_thumbnail().
|
||||
*/
|
||||
|
@ -5,6 +5,8 @@
|
||||
* - from vipsthumbnail.c
|
||||
* 6/1/17
|
||||
* - add @size parameter
|
||||
* 4/5/17
|
||||
* - add FORCE
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -135,21 +137,27 @@ vips_thumbnail_finalize( GObject *gobject )
|
||||
|
||||
/* Calculate the shrink factor, taking into account auto-rotate, the fit mode,
|
||||
* and so on.
|
||||
*
|
||||
* The hshrink/vshrink are the amount to shrink the input image axes by in
|
||||
* order for the output axes (ie. after rotation) to match the required
|
||||
* thumbnail->width, thumbnail->height and fit mode.
|
||||
*/
|
||||
static double
|
||||
static void
|
||||
vips_thumbnail_calculate_shrink( VipsThumbnail *thumbnail,
|
||||
int input_width, int input_height )
|
||||
int input_width, int input_height, double *hshrink, double *vshrink )
|
||||
{
|
||||
/* If we will be rotating, swap the target width and height.
|
||||
*/
|
||||
gboolean rotate =
|
||||
thumbnail->angle == VIPS_ANGLE_D90 ||
|
||||
thumbnail->angle == VIPS_ANGLE_D270;
|
||||
int width = thumbnail->auto_rotate && rotate ?
|
||||
input_height : input_width;
|
||||
int height = thumbnail->auto_rotate && rotate ?
|
||||
input_width : input_height;
|
||||
(thumbnail->angle == VIPS_ANGLE_D90 ||
|
||||
thumbnail->angle == VIPS_ANGLE_D270) &&
|
||||
thumbnail->auto_rotate;
|
||||
int target_width = rotate ?
|
||||
thumbnail->height : thumbnail->width;
|
||||
int target_height = rotate ?
|
||||
thumbnail->width : thumbnail->height;
|
||||
|
||||
VipsDirection direction;
|
||||
double shrink;
|
||||
|
||||
/* Calculate the horizontal and vertical shrink we'd need to fit the
|
||||
* image to the bounding box, and pick the biggest.
|
||||
@ -157,33 +165,53 @@ vips_thumbnail_calculate_shrink( VipsThumbnail *thumbnail,
|
||||
* In crop mode, we aim to fill the bounding box, so we must use the
|
||||
* smaller axis.
|
||||
*/
|
||||
double horizontal = (double) width / thumbnail->width;
|
||||
double vertical = (double) height / thumbnail->height;
|
||||
*hshrink = (double) input_width / target_width;
|
||||
*vshrink = (double) input_height / target_height;
|
||||
|
||||
if( thumbnail->crop != VIPS_INTERESTING_NONE ) {
|
||||
if( horizontal < vertical )
|
||||
if( *hshrink < *vshrink )
|
||||
direction = VIPS_DIRECTION_HORIZONTAL;
|
||||
else
|
||||
direction = VIPS_DIRECTION_VERTICAL;
|
||||
}
|
||||
else {
|
||||
if( horizontal < vertical )
|
||||
if( *hshrink < *vshrink )
|
||||
direction = VIPS_DIRECTION_VERTICAL;
|
||||
else
|
||||
direction = VIPS_DIRECTION_HORIZONTAL;
|
||||
}
|
||||
|
||||
shrink = direction == VIPS_DIRECTION_HORIZONTAL ?
|
||||
horizontal : vertical;
|
||||
if( thumbnail->size != VIPS_SIZE_FORCE ) {
|
||||
if( direction == VIPS_DIRECTION_HORIZONTAL )
|
||||
*vshrink = *hshrink;
|
||||
else
|
||||
*hshrink = *vshrink;
|
||||
}
|
||||
|
||||
/* Restrict to only upsize, only downsize, or both.
|
||||
*/
|
||||
if( thumbnail->size == VIPS_SIZE_UP )
|
||||
shrink = VIPS_MIN( 1, shrink );
|
||||
if( thumbnail->size == VIPS_SIZE_DOWN )
|
||||
shrink = VIPS_MAX( 1, shrink );
|
||||
if( thumbnail->size == VIPS_SIZE_UP ) {
|
||||
*hshrink = VIPS_MIN( 1, *hshrink );
|
||||
*vshrink = VIPS_MIN( 1, *vshrink );
|
||||
}
|
||||
else if( thumbnail->size == VIPS_SIZE_DOWN ) {
|
||||
*hshrink = VIPS_MAX( 1, *hshrink );
|
||||
*vshrink = VIPS_MAX( 1, *vshrink );
|
||||
}
|
||||
}
|
||||
|
||||
return( shrink );
|
||||
/* Just the common part of the shrink: the bit by which both axes must be
|
||||
* shrunk.
|
||||
*/
|
||||
static double
|
||||
vips_thumbnail_calculate_common_shrink( VipsThumbnail *thumbnail,
|
||||
int width, int height )
|
||||
{
|
||||
double hshrink;
|
||||
double vshrink;
|
||||
|
||||
vips_thumbnail_calculate_shrink( thumbnail, width, height,
|
||||
&hshrink, &vshrink );
|
||||
|
||||
return( VIPS_MIN( hshrink, vshrink ) );
|
||||
}
|
||||
|
||||
/* Find the best jpeg preload shrink.
|
||||
@ -191,8 +219,8 @@ vips_thumbnail_calculate_shrink( VipsThumbnail *thumbnail,
|
||||
static int
|
||||
vips_thumbnail_find_jpegshrink( VipsThumbnail *thumbnail, int width, int height )
|
||||
{
|
||||
double shrink =
|
||||
vips_thumbnail_calculate_shrink( thumbnail, width, height );
|
||||
double shrink = vips_thumbnail_calculate_common_shrink( thumbnail,
|
||||
width, height );
|
||||
|
||||
/* We can't use pre-shrunk images in linear mode. libjpeg shrinks in Y
|
||||
* (of YCbCR), not linear space.
|
||||
@ -228,7 +256,7 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
|
||||
VipsThumbnailClass *class = VIPS_THUMBNAIL_GET_CLASS( thumbnail );
|
||||
|
||||
VipsImage *im;
|
||||
int shrink;
|
||||
double shrink;
|
||||
double scale;
|
||||
|
||||
if( class->get_info( thumbnail ) )
|
||||
@ -237,24 +265,28 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
|
||||
g_info( "input size is %d x %d",
|
||||
thumbnail->input_width, thumbnail->input_height );
|
||||
|
||||
shrink = 1;
|
||||
shrink = 1.0;
|
||||
scale = 1.0;
|
||||
|
||||
if( vips_isprefix( "VipsForeignLoadJpeg", thumbnail->loader ) ) {
|
||||
shrink = vips_thumbnail_find_jpegshrink( thumbnail,
|
||||
thumbnail->input_width, thumbnail->input_height );
|
||||
g_info( "loading jpeg with factor %d pre-shrink", shrink );
|
||||
|
||||
g_info( "loading jpeg with factor %g pre-shrink", shrink );
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ||
|
||||
vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ) {
|
||||
scale = 1.0 / vips_thumbnail_calculate_shrink( thumbnail,
|
||||
thumbnail->input_width, thumbnail->input_height );
|
||||
shrink = vips_thumbnail_calculate_common_shrink( thumbnail,
|
||||
thumbnail->input_width, thumbnail->input_height );
|
||||
scale = 1.0 / shrink;
|
||||
|
||||
g_info( "loading PDF/SVG with factor %g pre-scale", scale );
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ) {
|
||||
shrink = vips_thumbnail_calculate_shrink( thumbnail,
|
||||
thumbnail->input_width, thumbnail->input_height );
|
||||
g_info( "loading webp with factor %d pre-shrink", shrink );
|
||||
shrink = vips_thumbnail_calculate_common_shrink( thumbnail,
|
||||
thumbnail->input_width, thumbnail->input_height );
|
||||
|
||||
g_info( "loading webp with factor %g pre-shrink", shrink );
|
||||
}
|
||||
|
||||
if( !(im = class->open( thumbnail, shrink, scale )) )
|
||||
@ -272,7 +304,8 @@ vips_thumbnail_build( VipsObject *object )
|
||||
VIPS_INTERPRETATION_scRGB : VIPS_INTERPRETATION_sRGB;
|
||||
|
||||
VipsImage *in;
|
||||
double shrink;
|
||||
double hshrink;
|
||||
double vshrink;
|
||||
|
||||
/* TRUE if we've done the import of an ICC transform and still need to
|
||||
* export.
|
||||
@ -373,12 +406,13 @@ vips_thumbnail_build( VipsObject *object )
|
||||
in = t[3];
|
||||
}
|
||||
|
||||
shrink = vips_thumbnail_calculate_shrink( thumbnail,
|
||||
in->Xsize, in->Ysize );
|
||||
vips_thumbnail_calculate_shrink( thumbnail,
|
||||
in->Xsize, in->Ysize, &hshrink, &vshrink );
|
||||
|
||||
/* Use centre convention to better match imagemagick.
|
||||
*/
|
||||
if( vips_resize( in, &t[4], 1.0 / shrink,
|
||||
if( vips_resize( in, &t[4], 1.0 / hshrink,
|
||||
"vscale", 1.0 / vshrink,
|
||||
"centre", TRUE,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
@ -639,8 +673,6 @@ vips_thumbnail_file_open( VipsThumbnail *thumbnail, int shrink, double scale )
|
||||
{
|
||||
VipsThumbnailFile *file = (VipsThumbnailFile *) thumbnail;
|
||||
|
||||
/* We can't use UNBUFERRED safely on very-many-core systems.
|
||||
*/
|
||||
if( shrink != 1 )
|
||||
return( vips_image_new_from_file( file->filename,
|
||||
"access", VIPS_ACCESS_SEQUENTIAL,
|
||||
@ -697,7 +729,7 @@ vips_thumbnail_file_init( VipsThumbnailFile *file )
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @height: %gint, target height in pixels
|
||||
* * @size: #VipsSize, upsize, downsize or both
|
||||
* * @size: #VipsSize, upsize, downsize, both or force
|
||||
* * @auto_rotate: %gboolean, rotate upright using orientation tag
|
||||
* * @crop: #VipsInteresting, shrink and crop to fill target
|
||||
* * @linear: %gboolean, perform shrink in linear light
|
||||
@ -719,19 +751,22 @@ vips_thumbnail_file_init( VipsThumbnailFile *file )
|
||||
* @height rectangle, with any excess cropped away. See vips_smartcrop() for
|
||||
* details on the cropping strategy.
|
||||
*
|
||||
* Normally the operation will upsize or downsize as required. If @size is set
|
||||
* Normally the operation will upsize or downsize as required to fit the image
|
||||
* inside or outside the target size. If @size is set
|
||||
* to #VIPS_SIZE_UP, the operation will only upsize and will just
|
||||
* copy if asked to downsize.
|
||||
* If @size is set
|
||||
* to #VIPS_SIZE_DOWN, the operation will only downsize and will just
|
||||
* copy if asked to upsize.
|
||||
* If @size is #VIPS_SIZE_FORCE, the image aspect ratio will be broken and the
|
||||
* image will be forced to fit the target.
|
||||
*
|
||||
* Normally any orientation tags on the input image (such as EXIF tags) are
|
||||
* interpreted to rotate the image upright. If you set @auto_rotate to %FALSE,
|
||||
* these tags will not be interpreted.
|
||||
*
|
||||
* Shrinking is normally done in sRGB colourspace. Set @linear to shrink in
|
||||
* linear light colourspace instead --- this can give better results, but can
|
||||
* linear light colourspace instead. This can give better results, but can
|
||||
* also be far slower, since tricks like JPEG shrink-on-load cannot be used in
|
||||
* linear space.
|
||||
*
|
||||
@ -866,7 +901,7 @@ vips_thumbnail_buffer_init( VipsThumbnailBuffer *buffer )
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @height: %gint, target height in pixels
|
||||
* * @size: #VipsSize, upsize, downsize or both
|
||||
* * @size: #VipsSize, upsize, downsize, both or force
|
||||
* * @auto_rotate: %gboolean, rotate upright using orientation tag
|
||||
* * @crop: #VipsInteresting, shrink and crop to fill target
|
||||
* * @linear: %gboolean, perform shrink in linear light
|
||||
|
@ -94,7 +94,7 @@ option.
|
||||
.TP
|
||||
.B -c, --crop
|
||||
Crop the output image down. The image is shrunk so as to completely fill the
|
||||
bounding box in both axies, then any excess is cropped off.
|
||||
bounding box in both axes, then any excess is cropped off.
|
||||
|
||||
.TP
|
||||
.B -d, --delete
|
||||
|
BIN
test/images/Landscape_6.jpg
Normal file
BIN
test/images/Landscape_6.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 127 KiB |
@ -107,6 +107,7 @@ class TestResample(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.jpeg_file = "images/йцук.jpg"
|
||||
self.rotated_jpeg_file = "images/Landscape_6.jpg"
|
||||
|
||||
def test_affine(self):
|
||||
im = Vips.Image.new_from_file(self.jpeg_file)
|
||||
@ -216,12 +217,38 @@ class TestResample(unittest.TestCase):
|
||||
self.assertNotEqual(im.width, 300)
|
||||
self.assertEqual(im.height, 100)
|
||||
|
||||
# with @crop, should fit both width and height
|
||||
im = Vips.Image.thumbnail(self.jpeg_file, 100,
|
||||
height = 300, crop = True)
|
||||
# force should fit width and height ... although this jpg has an
|
||||
# orientation tag, we ignore it unless autorot is on
|
||||
im = Vips.Image.thumbnail(self.rotated_jpeg_file, 100, height = 300,
|
||||
size = "force")
|
||||
self.assertEqual(im.width, 100)
|
||||
self.assertEqual(im.height, 300)
|
||||
|
||||
# with force + autorot, we spin the image, but the output size should
|
||||
# not change
|
||||
im = Vips.Image.thumbnail(self.rotated_jpeg_file, 100, height = 300,
|
||||
size = "force", auto_rotate = True)
|
||||
self.assertEqual(im.width, 100)
|
||||
self.assertEqual(im.height, 300)
|
||||
|
||||
# with @crop, should fit both width and height
|
||||
im = Vips.Image.thumbnail(self.jpeg_file, 100,
|
||||
height = 300, crop = "centre")
|
||||
self.assertEqual(im.width, 100)
|
||||
self.assertEqual(im.height, 300)
|
||||
|
||||
# with size up, should not downsize
|
||||
im = Vips.Image.thumbnail(self.jpeg_file, 100, size = "up")
|
||||
self.assertEqual(im.width, im_orig.width)
|
||||
im = Vips.Image.thumbnail(self.jpeg_file, 10000, size = "up")
|
||||
self.assertEqual(im.width, 10000)
|
||||
|
||||
# with size down, should not upsize
|
||||
im = Vips.Image.thumbnail(self.jpeg_file, 100, size = "down")
|
||||
self.assertEqual(im.width, 100)
|
||||
im = Vips.Image.thumbnail(self.jpeg_file, 10000, size = "down")
|
||||
self.assertEqual(im.width, im_orig.width)
|
||||
|
||||
im1 = Vips.Image.thumbnail(self.jpeg_file, 100)
|
||||
with open(self.jpeg_file, 'rb') as f:
|
||||
buf = f.read()
|
||||
|
@ -90,6 +90,8 @@
|
||||
* 6/1/17
|
||||
* - fancy geometry strings
|
||||
* - support VipSize restrictions
|
||||
* 4/5/17
|
||||
* - add ! geo modifier
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
@ -333,7 +335,7 @@ thumbnail_parse_geometry( const char *geometry )
|
||||
p++;
|
||||
}
|
||||
|
||||
/* Get the final < or >.
|
||||
/* Get the final <>!
|
||||
*/
|
||||
while( isspace( *p ) )
|
||||
p++;
|
||||
@ -341,6 +343,8 @@ thumbnail_parse_geometry( const char *geometry )
|
||||
size_restriction = VIPS_SIZE_UP;
|
||||
else if( *p == '>' )
|
||||
size_restriction = VIPS_SIZE_DOWN;
|
||||
else if( *p == '!' )
|
||||
size_restriction = VIPS_SIZE_FORCE;
|
||||
else if( *p != '\0' ||
|
||||
(thumbnail_width == VIPS_MAX_COORD &&
|
||||
thumbnail_height == VIPS_MAX_COORD) ) {
|
||||
@ -348,18 +352,30 @@ thumbnail_parse_geometry( const char *geometry )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* If --crop is set, both width and height must be specified,
|
||||
* since we'll need a complete bounding box to fill.
|
||||
/* If force is set and one of width or height isn't set, copy from the
|
||||
* one that is.
|
||||
*/
|
||||
if( (crop_image || smartcrop_image) &&
|
||||
(thumbnail_width == VIPS_MAX_COORD ||
|
||||
thumbnail_height == VIPS_MAX_COORD) ) {
|
||||
vips_error( "thumbnail",
|
||||
"both width and height must be given if "
|
||||
"crop is enabled" );
|
||||
return( -1 );
|
||||
if( size_restriction == VIPS_SIZE_FORCE ) {
|
||||
if( thumbnail_width == VIPS_MAX_COORD )
|
||||
thumbnail_width = thumbnail_height;
|
||||
if( thumbnail_height == VIPS_MAX_COORD )
|
||||
thumbnail_height = thumbnail_width;
|
||||
}
|
||||
|
||||
/* If --crop is set or force is set, both width and height must be
|
||||
* specified, since we'll need a complete bounding box to fill.
|
||||
*/
|
||||
if( crop_image ||
|
||||
smartcrop_image ||
|
||||
size_restriction == VIPS_SIZE_FORCE )
|
||||
if( thumbnail_width == VIPS_MAX_COORD ||
|
||||
thumbnail_height == VIPS_MAX_COORD ) {
|
||||
vips_error( "thumbnail",
|
||||
"both width and height must be given if "
|
||||
"crop is enabled" );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user