Merge branch 'master' of github.com:jcupitt/libvips
Conflicts: TODO
This commit is contained in:
commit
29fc735f90
@ -1,4 +1,8 @@
|
|||||||
7/5/15 started 8.1.0
|
7/5/15 started 8.1.0
|
||||||
|
- add vips_premultiply(), vips_unpremultiply()
|
||||||
|
- change the alpha range rules for vips_flatten() to match vips_premultiply()
|
||||||
|
- vipsthumbnail uses vips_resize() rather than its own code
|
||||||
|
- vipsthumbnail uses vips_premultiply() for better alpha quality
|
||||||
|
|
||||||
4/5/15 started 8.0.2
|
4/5/15 started 8.0.2
|
||||||
- fix a refcount error in C++ wrapper, thanks huskier
|
- fix a refcount error in C++ wrapper, thanks huskier
|
||||||
|
36
TODO
36
TODO
@ -1,39 +1,3 @@
|
|||||||
- how odd, why is resize much faster?
|
|
||||||
|
|
||||||
$ time vips resize wtc.tif small.tif 0.25
|
|
||||||
memory: high-water mark 138.09 MB
|
|
||||||
real 0m0.430s
|
|
||||||
user 0m2.700s
|
|
||||||
sys 0m0.172s
|
|
||||||
$ time vipsthumbnail wtc.tif -o small.tif -s 2500
|
|
||||||
memory: high-water mark 210.05 MB
|
|
||||||
real 0m0.959s
|
|
||||||
user 0m9.984s
|
|
||||||
sys 0m0.184s
|
|
||||||
|
|
||||||
about the same speed for jpg
|
|
||||||
|
|
||||||
$ time vips resize big.jpg[shrink=8] small.jpg 0.6666
|
|
||||||
memory: high-water mark 47.21 MB
|
|
||||||
real 0m1.320s
|
|
||||||
user 0m2.216s
|
|
||||||
sys 0m0.060s
|
|
||||||
$ time vipsthumbnail big.jpg -s 2500 -o small.jpg
|
|
||||||
memory: high-water mark 58.65 MB
|
|
||||||
real 0m1.360s
|
|
||||||
user 0m2.392s
|
|
||||||
sys 0m0.100s
|
|
||||||
|
|
||||||
seems to be because vipsthumbnail is not int shrinking enough:
|
|
||||||
|
|
||||||
$ vipsthumbnail wtc.tif -o small.tif -s 2500 --vips-info
|
|
||||||
info: vipsthumbnail: integer shrink by 1
|
|
||||||
info: vipsthumbnail: residual scale by 0.266752
|
|
||||||
|
|
||||||
should int shrink by 2! vips resize is doing this right, what's the
|
|
||||||
difference?
|
|
||||||
|
|
||||||
|
|
||||||
- are the mosaic functions calling vips_fastcor()? it must be very slow
|
- are the mosaic functions calling vips_fastcor()? it must be very slow
|
||||||
|
|
||||||
add vips_fastcor_direct()
|
add vips_fastcor_direct()
|
||||||
|
@ -7,6 +7,8 @@ libconversion_la_SOURCES = \
|
|||||||
gamma.c \
|
gamma.c \
|
||||||
sequential.c \
|
sequential.c \
|
||||||
flatten.c \
|
flatten.c \
|
||||||
|
premultiply.c \
|
||||||
|
unpremultiply.c \
|
||||||
cache.c \
|
cache.c \
|
||||||
copy.c \
|
copy.c \
|
||||||
embed.c \
|
embed.c \
|
||||||
|
@ -238,6 +238,8 @@ vips_conversion_operation_init( void )
|
|||||||
extern GType vips_recomb_get_type( void );
|
extern GType vips_recomb_get_type( void );
|
||||||
extern GType vips_bandmean_get_type( void );
|
extern GType vips_bandmean_get_type( void );
|
||||||
extern GType vips_flatten_get_type( void );
|
extern GType vips_flatten_get_type( void );
|
||||||
|
extern GType vips_premultiply_get_type( void );
|
||||||
|
extern GType vips_unpremultiply_get_type( void );
|
||||||
extern GType vips_bandbool_get_type( void );
|
extern GType vips_bandbool_get_type( void );
|
||||||
extern GType vips_gaussnoise_get_type( void );
|
extern GType vips_gaussnoise_get_type( void );
|
||||||
extern GType vips_grid_get_type( void );
|
extern GType vips_grid_get_type( void );
|
||||||
@ -277,6 +279,8 @@ vips_conversion_operation_init( void )
|
|||||||
vips_recomb_get_type();
|
vips_recomb_get_type();
|
||||||
vips_bandmean_get_type();
|
vips_bandmean_get_type();
|
||||||
vips_flatten_get_type();
|
vips_flatten_get_type();
|
||||||
|
vips_premultiply_get_type();
|
||||||
|
vips_unpremultiply_get_type();
|
||||||
vips_bandbool_get_type();
|
vips_bandbool_get_type();
|
||||||
vips_gaussnoise_get_type();
|
vips_gaussnoise_get_type();
|
||||||
vips_grid_get_type();
|
vips_grid_get_type();
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
*
|
*
|
||||||
* 4/1/14
|
* 4/1/14
|
||||||
* - better rounding
|
* - better rounding
|
||||||
|
* 9/5/15
|
||||||
|
* - add max_alpha to match vips_premultiply() etc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -67,6 +69,10 @@ typedef struct _VipsFlatten {
|
|||||||
*/
|
*/
|
||||||
VipsPel *ink;
|
VipsPel *ink;
|
||||||
|
|
||||||
|
/* Use this to scale alpha to 0 - 1.
|
||||||
|
*/
|
||||||
|
double max_alpha;
|
||||||
|
|
||||||
} VipsFlatten;
|
} VipsFlatten;
|
||||||
|
|
||||||
typedef VipsConversionClass VipsFlattenClass;
|
typedef VipsConversionClass VipsFlattenClass;
|
||||||
@ -75,7 +81,7 @@ G_DEFINE_TYPE( VipsFlatten, vips_flatten, VIPS_TYPE_CONVERSION );
|
|||||||
|
|
||||||
/* Flatten with black background.
|
/* Flatten with black background.
|
||||||
*/
|
*/
|
||||||
#define VIPS_FLATTEN_BLACK( TYPE, MAX ) { \
|
#define VIPS_FLATTEN_BLACK( TYPE ) { \
|
||||||
TYPE * restrict p = (TYPE *) in; \
|
TYPE * restrict p = (TYPE *) in; \
|
||||||
TYPE * restrict q = (TYPE *) out; \
|
TYPE * restrict q = (TYPE *) out; \
|
||||||
\
|
\
|
||||||
@ -84,7 +90,7 @@ G_DEFINE_TYPE( VipsFlatten, vips_flatten, VIPS_TYPE_CONVERSION );
|
|||||||
int b; \
|
int b; \
|
||||||
\
|
\
|
||||||
for( b = 0; b < bands - 1; b++ ) \
|
for( b = 0; b < bands - 1; b++ ) \
|
||||||
q[b] = (p[b] * alpha) / (MAX); \
|
q[b] = (p[b] * alpha) / max_alpha; \
|
||||||
\
|
\
|
||||||
p += bands; \
|
p += bands; \
|
||||||
q += bands - 1; \
|
q += bands - 1; \
|
||||||
@ -93,19 +99,19 @@ G_DEFINE_TYPE( VipsFlatten, vips_flatten, VIPS_TYPE_CONVERSION );
|
|||||||
|
|
||||||
/* Flatten with any background.
|
/* Flatten with any background.
|
||||||
*/
|
*/
|
||||||
#define VIPS_FLATTEN( TYPE, MAX ) { \
|
#define VIPS_FLATTEN( TYPE ) { \
|
||||||
TYPE * restrict p = (TYPE *) in; \
|
TYPE * restrict p = (TYPE *) in; \
|
||||||
TYPE * restrict q = (TYPE *) out; \
|
TYPE * restrict q = (TYPE *) out; \
|
||||||
\
|
\
|
||||||
for( x = 0; x < width; x++ ) { \
|
for( x = 0; x < width; x++ ) { \
|
||||||
TYPE alpha = p[bands - 1]; \
|
TYPE alpha = p[bands - 1]; \
|
||||||
TYPE nalpha = (MAX) - alpha; \
|
TYPE nalpha = max_alpha - alpha; \
|
||||||
TYPE * restrict bg = (TYPE *) flatten->ink; \
|
TYPE * restrict bg = (TYPE *) flatten->ink; \
|
||||||
int b; \
|
int b; \
|
||||||
\
|
\
|
||||||
for( b = 0; b < bands - 1; b++ ) \
|
for( b = 0; b < bands - 1; b++ ) \
|
||||||
q[b] = (p[b] * alpha) / (MAX) + \
|
q[b] = (p[b] * alpha) / max_alpha + \
|
||||||
(bg[b] * nalpha) / (MAX); \
|
(bg[b] * nalpha) / max_alpha; \
|
||||||
\
|
\
|
||||||
p += bands; \
|
p += bands; \
|
||||||
q += bands - 1; \
|
q += bands - 1; \
|
||||||
@ -115,7 +121,7 @@ G_DEFINE_TYPE( VipsFlatten, vips_flatten, VIPS_TYPE_CONVERSION );
|
|||||||
/* Same, but with float arithmetic. Necessary for int/uint to prevent
|
/* Same, but with float arithmetic. Necessary for int/uint to prevent
|
||||||
* overflow.
|
* overflow.
|
||||||
*/
|
*/
|
||||||
#define VIPS_FLATTEN_BLACK_FLOAT( TYPE, MAX ) { \
|
#define VIPS_FLATTEN_BLACK_FLOAT( TYPE ) { \
|
||||||
TYPE * restrict p = (TYPE *) in; \
|
TYPE * restrict p = (TYPE *) in; \
|
||||||
TYPE * restrict q = (TYPE *) out; \
|
TYPE * restrict q = (TYPE *) out; \
|
||||||
\
|
\
|
||||||
@ -124,26 +130,26 @@ G_DEFINE_TYPE( VipsFlatten, vips_flatten, VIPS_TYPE_CONVERSION );
|
|||||||
int b; \
|
int b; \
|
||||||
\
|
\
|
||||||
for( b = 0; b < bands - 1; b++ ) \
|
for( b = 0; b < bands - 1; b++ ) \
|
||||||
q[b] = ((double) p[b] * alpha) / (MAX); \
|
q[b] = ((double) p[b] * alpha) / max_alpha; \
|
||||||
\
|
\
|
||||||
p += bands; \
|
p += bands; \
|
||||||
q += bands - 1; \
|
q += bands - 1; \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define VIPS_FLATTEN_FLOAT( TYPE, MAX ) { \
|
#define VIPS_FLATTEN_FLOAT( TYPE ) { \
|
||||||
TYPE * restrict p = (TYPE *) in; \
|
TYPE * restrict p = (TYPE *) in; \
|
||||||
TYPE * restrict q = (TYPE *) out; \
|
TYPE * restrict q = (TYPE *) out; \
|
||||||
\
|
\
|
||||||
for( x = 0; x < width; x++ ) { \
|
for( x = 0; x < width; x++ ) { \
|
||||||
TYPE alpha = p[bands - 1]; \
|
TYPE alpha = p[bands - 1]; \
|
||||||
TYPE nalpha = (MAX) - alpha; \
|
TYPE nalpha = max_alpha - alpha; \
|
||||||
TYPE * restrict bg = (TYPE *) flatten->ink; \
|
TYPE * restrict bg = (TYPE *) flatten->ink; \
|
||||||
int b; \
|
int b; \
|
||||||
\
|
\
|
||||||
for( b = 0; b < bands - 1; b++ ) \
|
for( b = 0; b < bands - 1; b++ ) \
|
||||||
q[b] = ((double) p[b] * alpha) / (MAX) + \
|
q[b] = ((double) p[b] * alpha) / max_alpha + \
|
||||||
((double) bg[b] * nalpha) / (MAX); \
|
((double) bg[b] * nalpha) / max_alpha; \
|
||||||
\
|
\
|
||||||
p += bands; \
|
p += bands; \
|
||||||
q += bands - 1; \
|
q += bands - 1; \
|
||||||
@ -159,6 +165,7 @@ vips_flatten_black_gen( VipsRegion *or, void *vseq, void *a, void *b,
|
|||||||
VipsRect *r = &or->valid;
|
VipsRect *r = &or->valid;
|
||||||
int width = r->width;
|
int width = r->width;
|
||||||
int bands = ir->im->Bands;
|
int bands = ir->im->Bands;
|
||||||
|
double max_alpha = flatten->max_alpha;
|
||||||
|
|
||||||
int x, y;
|
int x, y;
|
||||||
|
|
||||||
@ -171,37 +178,35 @@ vips_flatten_black_gen( VipsRegion *or, void *vseq, void *a, void *b,
|
|||||||
|
|
||||||
switch( flatten->in->BandFmt ) {
|
switch( flatten->in->BandFmt ) {
|
||||||
case VIPS_FORMAT_UCHAR:
|
case VIPS_FORMAT_UCHAR:
|
||||||
VIPS_FLATTEN_BLACK( unsigned char, UCHAR_MAX );
|
VIPS_FLATTEN_BLACK( unsigned char );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_CHAR:
|
case VIPS_FORMAT_CHAR:
|
||||||
/* Alpha is 0 - 127? No idea, really.
|
VIPS_FLATTEN_BLACK( signed char );
|
||||||
*/
|
|
||||||
VIPS_FLATTEN_BLACK( signed char, CHAR_MAX );
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_USHORT:
|
case VIPS_FORMAT_USHORT:
|
||||||
VIPS_FLATTEN_BLACK( unsigned short, USHRT_MAX );
|
VIPS_FLATTEN_BLACK( unsigned short );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_SHORT:
|
case VIPS_FORMAT_SHORT:
|
||||||
VIPS_FLATTEN_BLACK( signed short, SHRT_MAX );
|
VIPS_FLATTEN_BLACK( signed short );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_UINT:
|
case VIPS_FORMAT_UINT:
|
||||||
VIPS_FLATTEN_BLACK_FLOAT( unsigned int, UINT_MAX );
|
VIPS_FLATTEN_BLACK_FLOAT( unsigned int );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_INT:
|
case VIPS_FORMAT_INT:
|
||||||
VIPS_FLATTEN_BLACK_FLOAT( signed int, INT_MAX );
|
VIPS_FLATTEN_BLACK_FLOAT( signed int );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_FLOAT:
|
case VIPS_FORMAT_FLOAT:
|
||||||
VIPS_FLATTEN_BLACK_FLOAT( float, 1.0 );
|
VIPS_FLATTEN_BLACK_FLOAT( float );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_DOUBLE:
|
case VIPS_FORMAT_DOUBLE:
|
||||||
VIPS_FLATTEN_BLACK_FLOAT( double, 1.0 );
|
VIPS_FLATTEN_BLACK_FLOAT( double );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_COMPLEX:
|
case VIPS_FORMAT_COMPLEX:
|
||||||
@ -225,6 +230,7 @@ vips_flatten_gen( VipsRegion *or, void *vseq, void *a, void *b,
|
|||||||
VipsRect *r = &or->valid;
|
VipsRect *r = &or->valid;
|
||||||
int width = r->width;
|
int width = r->width;
|
||||||
int bands = ir->im->Bands;
|
int bands = ir->im->Bands;
|
||||||
|
double max_alpha = flatten->max_alpha;
|
||||||
|
|
||||||
int x, y;
|
int x, y;
|
||||||
|
|
||||||
@ -237,37 +243,35 @@ vips_flatten_gen( VipsRegion *or, void *vseq, void *a, void *b,
|
|||||||
|
|
||||||
switch( flatten->in->BandFmt ) {
|
switch( flatten->in->BandFmt ) {
|
||||||
case VIPS_FORMAT_UCHAR:
|
case VIPS_FORMAT_UCHAR:
|
||||||
VIPS_FLATTEN( unsigned char, UCHAR_MAX );
|
VIPS_FLATTEN( unsigned char );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_CHAR:
|
case VIPS_FORMAT_CHAR:
|
||||||
/* Alpha is 0 - 127? No idea, really.
|
VIPS_FLATTEN( signed char );
|
||||||
*/
|
|
||||||
VIPS_FLATTEN( signed char, CHAR_MAX );
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_USHORT:
|
case VIPS_FORMAT_USHORT:
|
||||||
VIPS_FLATTEN( unsigned short, USHRT_MAX );
|
VIPS_FLATTEN( unsigned short );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_SHORT:
|
case VIPS_FORMAT_SHORT:
|
||||||
VIPS_FLATTEN( signed short, SHRT_MAX );
|
VIPS_FLATTEN( signed short );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_UINT:
|
case VIPS_FORMAT_UINT:
|
||||||
VIPS_FLATTEN_FLOAT( unsigned int, UINT_MAX );
|
VIPS_FLATTEN_FLOAT( unsigned int );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_INT:
|
case VIPS_FORMAT_INT:
|
||||||
VIPS_FLATTEN_FLOAT( signed int, INT_MAX );
|
VIPS_FLATTEN_FLOAT( signed int );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_FLOAT:
|
case VIPS_FORMAT_FLOAT:
|
||||||
VIPS_FLATTEN_FLOAT( float, 1.0 );
|
VIPS_FLATTEN_FLOAT( float );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_DOUBLE:
|
case VIPS_FORMAT_DOUBLE:
|
||||||
VIPS_FLATTEN_FLOAT( double, 1.0 );
|
VIPS_FLATTEN_FLOAT( double );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_COMPLEX:
|
case VIPS_FORMAT_COMPLEX:
|
||||||
@ -304,7 +308,7 @@ vips_flatten_build( VipsObject *object )
|
|||||||
|
|
||||||
/* Trivial case: fall back to copy().
|
/* Trivial case: fall back to copy().
|
||||||
*/
|
*/
|
||||||
if( flatten->in->Bands == 1 )
|
if( in->Bands == 1 )
|
||||||
return( vips_image_write( in, conversion->out ) );
|
return( vips_image_write( in, conversion->out ) );
|
||||||
|
|
||||||
if( vips_check_noncomplex( class->nickname, in ) )
|
if( vips_check_noncomplex( class->nickname, in ) )
|
||||||
@ -380,12 +384,21 @@ vips_flatten_class_init( VipsFlattenClass *class )
|
|||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
G_STRUCT_OFFSET( VipsFlatten, background ),
|
G_STRUCT_OFFSET( VipsFlatten, background ),
|
||||||
VIPS_TYPE_ARRAY_DOUBLE );
|
VIPS_TYPE_ARRAY_DOUBLE );
|
||||||
|
|
||||||
|
VIPS_ARG_DOUBLE( class, "max_alpha", 115,
|
||||||
|
_( "Maximum alpha" ),
|
||||||
|
_( "Maximum value of alpha channel" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsFlatten, max_alpha ),
|
||||||
|
0, 100000000, 255 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vips_flatten_init( VipsFlatten *flatten )
|
vips_flatten_init( VipsFlatten *flatten )
|
||||||
{
|
{
|
||||||
flatten->background = vips_array_double_newv( 1, 0.0 );
|
flatten->background = vips_array_double_newv( 1, 0.0 );
|
||||||
|
flatten->max_alpha= 255.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -397,19 +410,24 @@ vips_flatten_init( VipsFlatten *flatten )
|
|||||||
* Optional arguments:
|
* Optional arguments:
|
||||||
*
|
*
|
||||||
* @background: #VipsArrayDouble colour for new pixels
|
* @background: #VipsArrayDouble colour for new pixels
|
||||||
|
* @max_alpha: %gdouble, maximum value for alpha
|
||||||
*
|
*
|
||||||
* Take the last band of @in as an alpha and use it to blend the
|
* Take the last band of @in as an alpha and use it to blend the
|
||||||
* remaining channels with @background.
|
* remaining channels with @background.
|
||||||
*
|
*
|
||||||
* The alpha channel is 0 - MAX for
|
* The alpha channel is 0 - @max_alpha,
|
||||||
* integer images and 0 - 1 for float images, where MAX means 100% image and 0
|
* where 1 means 100% image and 0
|
||||||
* means 100% background. Non-complex images only.
|
* means 100% background.
|
||||||
* @background defaults to zero (black). MAX is the largest possible positive
|
* Non-complex images only.
|
||||||
* value for that int type.
|
* @background defaults to zero (black).
|
||||||
|
*
|
||||||
|
* @max_alpha has the default value 255. You will need to set this to 65535
|
||||||
|
* for images with a 16-bit alpha, or perhaps 1.0 for images with a float
|
||||||
|
* alpha.
|
||||||
*
|
*
|
||||||
* Useful for flattening PNG images to RGB.
|
* Useful for flattening PNG images to RGB.
|
||||||
*
|
*
|
||||||
* See also: pngload().
|
* See also: vips_premultiply(), vips_pngload().
|
||||||
*
|
*
|
||||||
* Returns: 0 on success, -1 on error
|
* Returns: 0 on success, -1 on error
|
||||||
*/
|
*/
|
||||||
|
316
libvips/conversion/premultiply.c
Normal file
316
libvips/conversion/premultiply.c
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
/* premultiply alpha
|
||||||
|
*
|
||||||
|
* Author: John Cupitt
|
||||||
|
* Written on: 7/5/15
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define VIPS_DEBUG
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif /*HAVE_CONFIG_H*/
|
||||||
|
#include <vips/intl.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <vips/vips.h>
|
||||||
|
#include <vips/internal.h>
|
||||||
|
#include <vips/debug.h>
|
||||||
|
|
||||||
|
#include "pconversion.h"
|
||||||
|
|
||||||
|
typedef struct _VipsPremultiply {
|
||||||
|
VipsConversion parent_instance;
|
||||||
|
|
||||||
|
VipsImage *in;
|
||||||
|
|
||||||
|
double max_alpha;
|
||||||
|
|
||||||
|
} VipsPremultiply;
|
||||||
|
|
||||||
|
typedef VipsConversionClass VipsPremultiplyClass;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE( VipsPremultiply, vips_premultiply, VIPS_TYPE_CONVERSION );
|
||||||
|
|
||||||
|
/* Premultiply an n-band image.
|
||||||
|
*/
|
||||||
|
#define PRE_MANY( IN, OUT ) { \
|
||||||
|
IN * restrict p = (IN *) in; \
|
||||||
|
OUT * restrict q = (OUT *) out; \
|
||||||
|
\
|
||||||
|
for( x = 0; x < width; x++ ) { \
|
||||||
|
IN alpha = p[bands - 1]; \
|
||||||
|
IN clip_alpha = VIPS_CLIP( 0, alpha, max_alpha ); \
|
||||||
|
double nalpha = (double) clip_alpha / max_alpha; \
|
||||||
|
\
|
||||||
|
for( i = 0; i < bands - 1; i++ ) \
|
||||||
|
q[i] = p[i] * nalpha; \
|
||||||
|
q[i] = alpha; \
|
||||||
|
\
|
||||||
|
p += bands; \
|
||||||
|
q += bands; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Special case for RGBA, it's very common.
|
||||||
|
*/
|
||||||
|
#define PRE_RGB( IN, OUT ) { \
|
||||||
|
IN * restrict p = (IN *) in; \
|
||||||
|
OUT * restrict q = (OUT *) out; \
|
||||||
|
\
|
||||||
|
for( x = 0; x < width; x++ ) { \
|
||||||
|
IN alpha = p[3]; \
|
||||||
|
IN clip_alpha = VIPS_CLIP( 0, alpha, max_alpha ); \
|
||||||
|
double nalpha = (double) clip_alpha / max_alpha; \
|
||||||
|
\
|
||||||
|
q[0] = p[0] * nalpha; \
|
||||||
|
q[1] = p[1] * nalpha; \
|
||||||
|
q[2] = p[2] * nalpha; \
|
||||||
|
q[3] = alpha; \
|
||||||
|
\
|
||||||
|
p += 4; \
|
||||||
|
q += 4; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PRE( IN, OUT ) { \
|
||||||
|
if( bands == 3 ) { \
|
||||||
|
PRE_RGB( IN, OUT ); \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
PRE_MANY( IN, OUT ); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_premultiply_gen( VipsRegion *or, void *vseq, void *a, void *b,
|
||||||
|
gboolean *stop )
|
||||||
|
{
|
||||||
|
VipsPremultiply *premultiply = (VipsPremultiply *) b;
|
||||||
|
VipsRegion *ir = (VipsRegion *) vseq;
|
||||||
|
VipsImage *im = ir->im;
|
||||||
|
VipsRect *r = &or->valid;
|
||||||
|
int width = r->width;
|
||||||
|
int bands = im->Bands;
|
||||||
|
double max_alpha = premultiply->max_alpha;
|
||||||
|
|
||||||
|
int x, y, i;
|
||||||
|
|
||||||
|
if( vips_region_prepare( ir, r ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
for( y = 0; y < r->height; y++ ) {
|
||||||
|
VipsPel *in = VIPS_REGION_ADDR( ir, r->left, r->top + y );
|
||||||
|
VipsPel *out = VIPS_REGION_ADDR( or, r->left, r->top + y );
|
||||||
|
|
||||||
|
switch( im->BandFmt ) {
|
||||||
|
case VIPS_FORMAT_UCHAR:
|
||||||
|
PRE( unsigned char, float );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_CHAR:
|
||||||
|
PRE( signed char, float );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_USHORT:
|
||||||
|
PRE( unsigned short, float );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_SHORT:
|
||||||
|
PRE( signed short, float );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_UINT:
|
||||||
|
PRE( unsigned int, float );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_INT:
|
||||||
|
PRE( signed int, float );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_FLOAT:
|
||||||
|
PRE( float, float );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_DOUBLE:
|
||||||
|
PRE( double, double );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_COMPLEX:
|
||||||
|
case VIPS_FORMAT_DPCOMPLEX:
|
||||||
|
default:
|
||||||
|
g_assert( 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_premultiply_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||||
|
VipsConversion *conversion = VIPS_CONVERSION( object );
|
||||||
|
VipsPremultiply *premultiply = (VipsPremultiply *) object;
|
||||||
|
VipsImage **t = (VipsImage **) vips_object_local_array( object, 1 );
|
||||||
|
|
||||||
|
VipsImage *in;
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_premultiply_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
in = premultiply->in;
|
||||||
|
|
||||||
|
if( vips_image_decode( in, &t[0] ) )
|
||||||
|
return( -1 );
|
||||||
|
in = t[0];
|
||||||
|
|
||||||
|
/* Trivial case: fall back to copy().
|
||||||
|
*/
|
||||||
|
if( in->Bands == 1 )
|
||||||
|
return( vips_image_write( in, conversion->out ) );
|
||||||
|
|
||||||
|
if( vips_check_noncomplex( class->nickname, in ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( vips_image_pipelinev( conversion->out,
|
||||||
|
VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( in->BandFmt == VIPS_FORMAT_DOUBLE )
|
||||||
|
conversion->out->BandFmt = VIPS_FORMAT_DOUBLE;
|
||||||
|
else
|
||||||
|
conversion->out->BandFmt = VIPS_FORMAT_FLOAT;
|
||||||
|
|
||||||
|
if( vips_image_generate( conversion->out,
|
||||||
|
vips_start_one, vips_premultiply_gen, vips_stop_one,
|
||||||
|
in, premultiply ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_premultiply_class_init( VipsPremultiplyClass *class )
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
|
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
|
||||||
|
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
|
||||||
|
|
||||||
|
VIPS_DEBUG_MSG( "vips_premultiply_class_init\n" );
|
||||||
|
|
||||||
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
vobject_class->nickname = "premultiply";
|
||||||
|
vobject_class->description = _( "premultiply image alpha" );
|
||||||
|
vobject_class->build = vips_premultiply_build;
|
||||||
|
|
||||||
|
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
|
||||||
|
|
||||||
|
VIPS_ARG_IMAGE( class, "in", 1,
|
||||||
|
_( "Input" ),
|
||||||
|
_( "Input image" ),
|
||||||
|
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsPremultiply, in ) );
|
||||||
|
|
||||||
|
VIPS_ARG_DOUBLE( class, "max_alpha", 115,
|
||||||
|
_( "Maximum alpha" ),
|
||||||
|
_( "Maximum value of alpha channel" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsPremultiply, max_alpha ),
|
||||||
|
0, 100000000, 255 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_premultiply_init( VipsPremultiply *premultiply )
|
||||||
|
{
|
||||||
|
premultiply->max_alpha = 255.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_premultiply:
|
||||||
|
* @in: input image
|
||||||
|
* @out: output image
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* @max_alpha: %gdouble, maximum value for alpha
|
||||||
|
*
|
||||||
|
* Premultiplies any alpha channel.
|
||||||
|
* The final band is taken to be the alpha
|
||||||
|
* and the bands are transformed as:
|
||||||
|
*
|
||||||
|
* |[
|
||||||
|
* alpha = clip( 0, in[in.bands - 1], @max_alpha );
|
||||||
|
* norm = alpha / @max_alpha;
|
||||||
|
* out = [in[0] * norm, ..., in[in.bands - 1] * norm, alpha];
|
||||||
|
* |]
|
||||||
|
*
|
||||||
|
* So for an N-band image, the first N - 1 bands are multiplied by the clipped
|
||||||
|
* and normalised final band, the final band is clipped.
|
||||||
|
* If there is only a single band,
|
||||||
|
* the image is passed through unaltered.
|
||||||
|
*
|
||||||
|
* The result is
|
||||||
|
* #VIPS_FORMAT_FLOAT unless the input format is #VIPS_FORMAT_DOUBLE, in which
|
||||||
|
* case the output is double as well.
|
||||||
|
*
|
||||||
|
* @max_alpha has the default value 255. You will need to set this to 65535
|
||||||
|
* for images with a 16-bit alpha, or perhaps 1.0 for images with a float
|
||||||
|
* alpha.
|
||||||
|
*
|
||||||
|
* Non-complex images only.
|
||||||
|
*
|
||||||
|
* See also: vips_unpremultiply(), vips_flatten().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_premultiply( VipsImage *in, VipsImage **out, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
va_start( ap, out );
|
||||||
|
result = vips_call_split( "premultiply", ap, in, out );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
331
libvips/conversion/unpremultiply.c
Normal file
331
libvips/conversion/unpremultiply.c
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
/* unpremultiply alpha
|
||||||
|
*
|
||||||
|
* Author: John Cupitt
|
||||||
|
* Written on: 7/5/15
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define VIPS_DEBUG
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif /*HAVE_CONFIG_H*/
|
||||||
|
#include <vips/intl.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <vips/vips.h>
|
||||||
|
#include <vips/internal.h>
|
||||||
|
#include <vips/debug.h>
|
||||||
|
|
||||||
|
#include "pconversion.h"
|
||||||
|
|
||||||
|
typedef struct _VipsUnpremultiply {
|
||||||
|
VipsConversion parent_instance;
|
||||||
|
|
||||||
|
VipsImage *in;
|
||||||
|
|
||||||
|
double max_alpha;
|
||||||
|
|
||||||
|
} VipsUnpremultiply;
|
||||||
|
|
||||||
|
typedef VipsConversionClass VipsUnpremultiplyClass;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION );
|
||||||
|
|
||||||
|
/* Unpremultiply a greyscale (two band) image.
|
||||||
|
*/
|
||||||
|
#define UNPRE_MANY( IN, OUT ) { \
|
||||||
|
IN * restrict p = (IN *) in; \
|
||||||
|
OUT * restrict q = (OUT *) out; \
|
||||||
|
\
|
||||||
|
for( x = 0; x < width; x++ ) { \
|
||||||
|
IN alpha = p[bands - 1]; \
|
||||||
|
int clip_alpha = VIPS_CLIP( 0, alpha, max_alpha ); \
|
||||||
|
double nalpha = (double) clip_alpha / max_alpha; \
|
||||||
|
\
|
||||||
|
if( clip_alpha == 0 ) \
|
||||||
|
for( i = 0; i < bands - 1; i++ ) \
|
||||||
|
q[i] = 0; \
|
||||||
|
else \
|
||||||
|
for( i = 0; i < bands - 1; i++ ) \
|
||||||
|
q[i] = p[i] / nalpha; \
|
||||||
|
q[i] = clip_alpha; \
|
||||||
|
\
|
||||||
|
p += bands; \
|
||||||
|
q += bands; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unpremultiply an RGB (four band) image.
|
||||||
|
*/
|
||||||
|
#define UNPRE_RGBA( IN, OUT ) { \
|
||||||
|
IN * restrict p = (IN *) in; \
|
||||||
|
OUT * restrict q = (OUT *) out; \
|
||||||
|
\
|
||||||
|
for( x = 0; x < width; x++ ) { \
|
||||||
|
IN alpha = p[3]; \
|
||||||
|
int clip_alpha = VIPS_CLIP( 0, alpha, max_alpha ); \
|
||||||
|
double nalpha = (double) clip_alpha / max_alpha; \
|
||||||
|
\
|
||||||
|
if( clip_alpha == 0 ) { \
|
||||||
|
q[0] = 0; \
|
||||||
|
q[1] = 0; \
|
||||||
|
q[2] = 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
q[0] = p[0] / nalpha; \
|
||||||
|
q[1] = p[1] / nalpha; \
|
||||||
|
q[2] = p[2] / nalpha; \
|
||||||
|
} \
|
||||||
|
q[3] = clip_alpha; \
|
||||||
|
\
|
||||||
|
p += 4; \
|
||||||
|
q += 4; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define UNPRE( IN, OUT ) { \
|
||||||
|
if( bands == 4 ) { \
|
||||||
|
UNPRE_RGBA( IN, OUT ); \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
UNPRE_MANY( IN, OUT ); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_unpremultiply_gen( VipsRegion *or, void *vseq, void *a, void *b,
|
||||||
|
gboolean *stop )
|
||||||
|
{
|
||||||
|
VipsUnpremultiply *unpremultiply = (VipsUnpremultiply *) b;
|
||||||
|
VipsRegion *ir = (VipsRegion *) vseq;
|
||||||
|
VipsImage *im = ir->im;
|
||||||
|
VipsRect *r = &or->valid;
|
||||||
|
int width = r->width;
|
||||||
|
int bands = im->Bands;
|
||||||
|
double max_alpha = unpremultiply->max_alpha;
|
||||||
|
|
||||||
|
int x, y, i;
|
||||||
|
|
||||||
|
if( vips_region_prepare( ir, r ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
for( y = 0; y < r->height; y++ ) {
|
||||||
|
VipsPel *in = VIPS_REGION_ADDR( ir, r->left, r->top + y );
|
||||||
|
VipsPel *out = VIPS_REGION_ADDR( or, r->left, r->top + y );
|
||||||
|
|
||||||
|
switch( im->BandFmt ) {
|
||||||
|
case VIPS_FORMAT_UCHAR:
|
||||||
|
UNPRE( unsigned char, float );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_CHAR:
|
||||||
|
UNPRE( signed char, float );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_USHORT:
|
||||||
|
UNPRE( unsigned short, float );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_SHORT:
|
||||||
|
UNPRE( signed short, float );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_UINT:
|
||||||
|
UNPRE( unsigned int, float );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_INT:
|
||||||
|
UNPRE( signed int, float );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_FLOAT:
|
||||||
|
UNPRE( float, float );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_DOUBLE:
|
||||||
|
UNPRE( double, double );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_COMPLEX:
|
||||||
|
case VIPS_FORMAT_DPCOMPLEX:
|
||||||
|
default:
|
||||||
|
g_assert( 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_unpremultiply_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||||
|
VipsConversion *conversion = VIPS_CONVERSION( object );
|
||||||
|
VipsUnpremultiply *unpremultiply = (VipsUnpremultiply *) object;
|
||||||
|
VipsImage **t = (VipsImage **) vips_object_local_array( object, 1 );
|
||||||
|
|
||||||
|
VipsImage *in;
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_unpremultiply_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
in = unpremultiply->in;
|
||||||
|
|
||||||
|
if( vips_image_decode( in, &t[0] ) )
|
||||||
|
return( -1 );
|
||||||
|
in = t[0];
|
||||||
|
|
||||||
|
/* Trivial case: fall back to copy().
|
||||||
|
*/
|
||||||
|
if( in->Bands == 1 )
|
||||||
|
return( vips_image_write( in, conversion->out ) );
|
||||||
|
|
||||||
|
if( vips_check_noncomplex( class->nickname, in ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( vips_image_pipelinev( conversion->out,
|
||||||
|
VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( in->BandFmt == VIPS_FORMAT_DOUBLE )
|
||||||
|
conversion->out->BandFmt = VIPS_FORMAT_DOUBLE;
|
||||||
|
else
|
||||||
|
conversion->out->BandFmt = VIPS_FORMAT_FLOAT;
|
||||||
|
|
||||||
|
if( vips_image_generate( conversion->out,
|
||||||
|
vips_start_one, vips_unpremultiply_gen, vips_stop_one,
|
||||||
|
in, unpremultiply ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_unpremultiply_class_init( VipsUnpremultiplyClass *class )
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
|
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
|
||||||
|
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
|
||||||
|
|
||||||
|
VIPS_DEBUG_MSG( "vips_unpremultiply_class_init\n" );
|
||||||
|
|
||||||
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
vobject_class->nickname = "unpremultiply";
|
||||||
|
vobject_class->description = _( "unpremultiply image alpha" );
|
||||||
|
vobject_class->build = vips_unpremultiply_build;
|
||||||
|
|
||||||
|
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
|
||||||
|
|
||||||
|
VIPS_ARG_IMAGE( class, "in", 1,
|
||||||
|
_( "Input" ),
|
||||||
|
_( "Input image" ),
|
||||||
|
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsUnpremultiply, in ) );
|
||||||
|
|
||||||
|
VIPS_ARG_DOUBLE( class, "max_alpha", 115,
|
||||||
|
_( "Maximum alpha" ),
|
||||||
|
_( "Maximum value of alpha channel" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsUnpremultiply, max_alpha ),
|
||||||
|
0, 100000000, 255 );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_unpremultiply_init( VipsUnpremultiply *unpremultiply )
|
||||||
|
{
|
||||||
|
unpremultiply->max_alpha = 255.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_unpremultiply:
|
||||||
|
* @in: input image
|
||||||
|
* @out: output image
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* @max_alpha: %gdouble, maximum value for alpha
|
||||||
|
*
|
||||||
|
* Unpremultiplies any alpha channel.
|
||||||
|
* The final band is taken to be the alpha
|
||||||
|
* and the bands are transformed as:
|
||||||
|
*
|
||||||
|
* |[
|
||||||
|
* alpha = (int) clip( 0, in[in.bands - 1], @max_alpha );
|
||||||
|
* norm = (double) alpha / @max_alpha;
|
||||||
|
* if( alpha == 0 )
|
||||||
|
* out = [0, ..., 0, alpha];
|
||||||
|
* else
|
||||||
|
* out = [in[0] / norm, ..., in[in.bands - 1] / norm, alpha];
|
||||||
|
* |]
|
||||||
|
*
|
||||||
|
* So for an N-band image, the first N - 1 bands are divided by the clipped
|
||||||
|
* and normalised final band, the final band is clipped.
|
||||||
|
* If there is only a single band,
|
||||||
|
* the image is passed through unaltered.
|
||||||
|
*
|
||||||
|
* The result is
|
||||||
|
* #VIPS_FORMAT_FLOAT unless the input format is #VIPS_FORMAT_DOUBLE, in which
|
||||||
|
* case the output is double as well.
|
||||||
|
*
|
||||||
|
* @max_alpha has the default value 255. You will need to set this to 65535
|
||||||
|
* for images with a 16-bit alpha, or perhaps 1.0 for images with a float
|
||||||
|
* alpha.
|
||||||
|
*
|
||||||
|
* Non-complex images only.
|
||||||
|
*
|
||||||
|
* See also: vips_premultiply(), vips_flatten().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_unpremultiply( VipsImage *in, VipsImage **out, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
va_start( ap, out );
|
||||||
|
result = vips_call_split( "unpremultiply", ap, in, out );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
@ -1202,6 +1202,9 @@ vips_foreign_convert_saveable( VipsForeignSave *save )
|
|||||||
|
|
||||||
if( vips_flatten( in, &out,
|
if( vips_flatten( in, &out,
|
||||||
"background", save->background,
|
"background", save->background,
|
||||||
|
"max_alpha",
|
||||||
|
in->BandFmt == VIPS_FORMAT_USHORT ?
|
||||||
|
65535.0 : 255.0,
|
||||||
NULL ) ) {
|
NULL ) ) {
|
||||||
g_object_unref( in );
|
g_object_unref( in );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
@ -184,8 +184,13 @@ int vips_recomb( VipsImage *in, VipsImage **out, VipsImage *m, ... )
|
|||||||
int vips_ifthenelse( VipsImage *cond, VipsImage *in1, VipsImage *in2,
|
int vips_ifthenelse( VipsImage *cond, VipsImage *in1, VipsImage *in2,
|
||||||
VipsImage **out, ... )
|
VipsImage **out, ... )
|
||||||
__attribute__((sentinel));
|
__attribute__((sentinel));
|
||||||
|
|
||||||
int vips_flatten( VipsImage *in, VipsImage **out, ... )
|
int vips_flatten( VipsImage *in, VipsImage **out, ... )
|
||||||
__attribute__((sentinel));
|
__attribute__((sentinel));
|
||||||
|
int vips_premultiply( VipsImage *in, VipsImage **out, ... )
|
||||||
|
__attribute__((sentinel));
|
||||||
|
int vips_unpremultiply( VipsImage *in, VipsImage **out, ... )
|
||||||
|
__attribute__((sentinel));
|
||||||
|
|
||||||
int vips_falsecolour( VipsImage *in, VipsImage **out, ... )
|
int vips_falsecolour( VipsImage *in, VipsImage **out, ... )
|
||||||
__attribute__((sentinel));
|
__attribute__((sentinel));
|
||||||
|
@ -348,23 +348,12 @@ class TestConversion(unittest.TestCase):
|
|||||||
self.assertAlmostEqualObjects(pixel, [20, 0, 41])
|
self.assertAlmostEqualObjects(pixel, [20, 0, 41])
|
||||||
|
|
||||||
def test_flatten(self):
|
def test_flatten(self):
|
||||||
max_value = {Vips.BandFormat.UCHAR: 0xff,
|
for fmt in unsigned_formats + [Vips.BandFormat.SHORT,
|
||||||
Vips.BandFormat.USHORT: 0xffff,
|
Vips.BandFormat.INT] + float_formats:
|
||||||
Vips.BandFormat.UINT: 0xffffffff,
|
mx = 255
|
||||||
Vips.BandFormat.CHAR: 0x7f,
|
|
||||||
Vips.BandFormat.SHORT: 0x7fff,
|
|
||||||
Vips.BandFormat.INT: 0x7fffffff,
|
|
||||||
Vips.BandFormat.FLOAT: 1.0,
|
|
||||||
Vips.BandFormat.DOUBLE: 1.0,
|
|
||||||
Vips.BandFormat.COMPLEX: 1.0,
|
|
||||||
Vips.BandFormat.DPCOMPLEX: 1.0}
|
|
||||||
black = self.mono * 0.0
|
|
||||||
|
|
||||||
for fmt in noncomplex_formats:
|
|
||||||
mx = max_value[fmt]
|
|
||||||
alpha = mx / 2.0
|
alpha = mx / 2.0
|
||||||
nalpha = mx - alpha
|
nalpha = mx - alpha
|
||||||
test = self.colour.bandjoin(black + alpha).cast(fmt)
|
test = self.colour.bandjoin(alpha).cast(fmt)
|
||||||
pixel = test(30, 30)
|
pixel = test(30, 30)
|
||||||
|
|
||||||
predict = [int(x) * alpha / mx for x in pixel[:-1]]
|
predict = [int(x) * alpha / mx for x in pixel[:-1]]
|
||||||
@ -389,6 +378,46 @@ class TestConversion(unittest.TestCase):
|
|||||||
for x, y in zip(pixel, predict):
|
for x, y in zip(pixel, predict):
|
||||||
self.assertLess(abs(x - y), 2)
|
self.assertLess(abs(x - y), 2)
|
||||||
|
|
||||||
|
def test_premultiply(self):
|
||||||
|
for fmt in unsigned_formats + [Vips.BandFormat.SHORT,
|
||||||
|
Vips.BandFormat.INT] + float_formats:
|
||||||
|
mx = 255
|
||||||
|
alpha = mx / 2.0
|
||||||
|
nalpha = mx - alpha
|
||||||
|
test = self.colour.bandjoin(alpha).cast(fmt)
|
||||||
|
pixel = test(30, 30)
|
||||||
|
|
||||||
|
predict = [int(x) * alpha / mx for x in pixel[:-1]] + [alpha]
|
||||||
|
|
||||||
|
im = test.premultiply()
|
||||||
|
|
||||||
|
self.assertEqual(im.bands, test.bands)
|
||||||
|
pixel = im(30, 30)
|
||||||
|
for x, y in zip(pixel, predict):
|
||||||
|
# we use float arithetic for int and uint, so the rounding
|
||||||
|
# differs ... don't require huge accuracy
|
||||||
|
self.assertLess(abs(x - y), 2)
|
||||||
|
|
||||||
|
def test_unpremultiply(self):
|
||||||
|
for fmt in unsigned_formats + [Vips.BandFormat.SHORT,
|
||||||
|
Vips.BandFormat.INT] + float_formats:
|
||||||
|
mx = 255
|
||||||
|
alpha = mx / 2.0
|
||||||
|
nalpha = mx - alpha
|
||||||
|
test = self.colour.bandjoin(alpha).cast(fmt)
|
||||||
|
pixel = test(30, 30)
|
||||||
|
|
||||||
|
predict = [int(x) / (alpha / mx) for x in pixel[:-1]] + [alpha]
|
||||||
|
|
||||||
|
im = test.unpremultiply()
|
||||||
|
|
||||||
|
self.assertEqual(im.bands, test.bands)
|
||||||
|
pixel = im(30, 30)
|
||||||
|
for x, y in zip(pixel, predict):
|
||||||
|
# we use float arithetic for int and uint, so the rounding
|
||||||
|
# differs ... don't require huge accuracy
|
||||||
|
self.assertLess(abs(x - y), 2)
|
||||||
|
|
||||||
def test_flip(self):
|
def test_flip(self):
|
||||||
for fmt in all_formats:
|
for fmt in all_formats:
|
||||||
test = self.colour.cast(fmt)
|
test = self.colour.cast(fmt)
|
||||||
|
@ -65,6 +65,9 @@
|
|||||||
* - exit with an error code if one or more conversions failed
|
* - exit with an error code if one or more conversions failed
|
||||||
* 20/1/15
|
* 20/1/15
|
||||||
* - rename -o as -f, keep -o as a hidden flag
|
* - rename -o as -f, keep -o as a hidden flag
|
||||||
|
* 9/5/15
|
||||||
|
* - use vips_resize() instead of our own code
|
||||||
|
* - premultiply alpha
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
@ -94,7 +97,7 @@ static char *output_format = "tn_%s.jpg";
|
|||||||
static char *interpolator = "bilinear";
|
static char *interpolator = "bilinear";
|
||||||
static char *export_profile = NULL;
|
static char *export_profile = NULL;
|
||||||
static char *import_profile = NULL;
|
static char *import_profile = NULL;
|
||||||
static char *convolution_mask = "mild";
|
static char *convolution_mask = "none";
|
||||||
static gboolean delete_profile = FALSE;
|
static gboolean delete_profile = FALSE;
|
||||||
static gboolean linear_processing = FALSE;
|
static gboolean linear_processing = FALSE;
|
||||||
static gboolean crop_image = FALSE;
|
static gboolean crop_image = FALSE;
|
||||||
@ -159,25 +162,16 @@ static GOptionEntry options[] = {
|
|||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Calculate the shrink factors.
|
/* Calculate the shrink factor, taking into account auto-rotate, the fit mode,
|
||||||
*
|
* and so on.
|
||||||
* We shrink in two stages: first, a shrink with a block average. This can
|
|
||||||
* only accurately shrink by integer factors. We then do a second shrink with
|
|
||||||
* a supplied interpolator to get the exact size we want.
|
|
||||||
*
|
|
||||||
* We aim to do the second shrink by roughly half the interpolator's
|
|
||||||
* window_size.
|
|
||||||
*/
|
*/
|
||||||
static int
|
static double
|
||||||
calculate_shrink( VipsImage *im, double *residual,
|
calculate_shrink( VipsImage *im )
|
||||||
VipsInterpolate *interp )
|
|
||||||
{
|
{
|
||||||
VipsAngle angle = vips_autorot_get_angle( im );
|
VipsAngle angle = vips_autorot_get_angle( im );
|
||||||
gboolean rotate = angle == VIPS_ANGLE_D90 || angle == VIPS_ANGLE_D270;
|
gboolean rotate = angle == VIPS_ANGLE_D90 || angle == VIPS_ANGLE_D270;
|
||||||
int width = rotate_image && rotate ? im->Ysize : im->Xsize;
|
int width = rotate_image && rotate ? im->Ysize : im->Xsize;
|
||||||
int height = rotate_image && rotate ? im->Xsize : im->Ysize;
|
int height = rotate_image && rotate ? im->Xsize : im->Ysize;
|
||||||
const int window_size =
|
|
||||||
interp ? vips_interpolate_get_window_size( interp ) : 2;
|
|
||||||
|
|
||||||
VipsDirection direction;
|
VipsDirection direction;
|
||||||
|
|
||||||
@ -203,42 +197,8 @@ calculate_shrink( VipsImage *im, double *residual,
|
|||||||
direction = VIPS_DIRECTION_HORIZONTAL;
|
direction = VIPS_DIRECTION_HORIZONTAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
double factor = direction == VIPS_DIRECTION_HORIZONTAL ?
|
return( direction == VIPS_DIRECTION_HORIZONTAL ?
|
||||||
horizontal : vertical;
|
horizontal : vertical );
|
||||||
|
|
||||||
/* If the shrink factor is <= 1.0, we need to zoom rather than shrink.
|
|
||||||
* Just set the factor to 1 in this case.
|
|
||||||
*/
|
|
||||||
double factor2 = factor < 1.0 ? 1.0 : factor;
|
|
||||||
|
|
||||||
/* Int component of factor2.
|
|
||||||
*
|
|
||||||
* We want to shrink by less for interpolators with larger windows.
|
|
||||||
*/
|
|
||||||
int shrink = VIPS_MAX( 1,
|
|
||||||
floor( factor2 ) / VIPS_MAX( 1, window_size / 2 ) );
|
|
||||||
|
|
||||||
if( residual &&
|
|
||||||
direction == VIPS_DIRECTION_HORIZONTAL ) {
|
|
||||||
/* Size after int shrink.
|
|
||||||
*/
|
|
||||||
int iwidth = width / shrink;
|
|
||||||
|
|
||||||
/* Therefore residual scale factor is.
|
|
||||||
*/
|
|
||||||
double hresidual = (width / factor) / iwidth;
|
|
||||||
|
|
||||||
*residual = hresidual;
|
|
||||||
}
|
|
||||||
else if( residual &&
|
|
||||||
direction == VIPS_DIRECTION_VERTICAL ) {
|
|
||||||
int iheight = height / shrink;
|
|
||||||
double vresidual = (height / factor) / iheight;
|
|
||||||
|
|
||||||
*residual = vresidual;
|
|
||||||
}
|
|
||||||
|
|
||||||
return( shrink );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find the best jpeg preload shrink.
|
/* Find the best jpeg preload shrink.
|
||||||
@ -246,19 +206,22 @@ calculate_shrink( VipsImage *im, double *residual,
|
|||||||
static int
|
static int
|
||||||
thumbnail_find_jpegshrink( VipsImage *im )
|
thumbnail_find_jpegshrink( VipsImage *im )
|
||||||
{
|
{
|
||||||
int shrink = calculate_shrink( im, NULL, NULL );
|
double shrink = calculate_shrink( im );
|
||||||
|
|
||||||
/* We can't use pre-shrunk images in linear mode. libjpeg shrinks in Y
|
/* We can't use pre-shrunk images in linear mode. libjpeg shrinks in Y
|
||||||
* (of YCbCR), not linear space.
|
* (of YCbCR), not linear space.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if( linear_processing )
|
if( linear_processing )
|
||||||
return( 1 );
|
return( 1 );
|
||||||
else if( shrink >= 8 )
|
|
||||||
|
/* We want to leave a bit of shrinking for our interpolator, we don't
|
||||||
|
* want to do all the shrinking with libjpeg.
|
||||||
|
*/
|
||||||
|
if( shrink >= 16 )
|
||||||
return( 8 );
|
return( 8 );
|
||||||
else if( shrink >= 4 )
|
else if( shrink >= 8 )
|
||||||
return( 4 );
|
return( 4 );
|
||||||
else if( shrink >= 2 )
|
else if( shrink >= 4 )
|
||||||
return( 2 );
|
return( 2 );
|
||||||
else
|
else
|
||||||
return( 1 );
|
return( 1 );
|
||||||
@ -326,17 +289,16 @@ thumbnail_open( VipsObject *process, const char *filename )
|
|||||||
static VipsInterpolate *
|
static VipsInterpolate *
|
||||||
thumbnail_interpolator( VipsObject *process, VipsImage *in )
|
thumbnail_interpolator( VipsObject *process, VipsImage *in )
|
||||||
{
|
{
|
||||||
double residual;
|
double shrink = calculate_shrink( in );
|
||||||
VipsInterpolate *interp;
|
|
||||||
|
|
||||||
calculate_shrink( in, &residual, NULL );
|
VipsInterpolate *interp;
|
||||||
|
|
||||||
/* For images smaller than the thumbnail, we upscale with nearest
|
/* For images smaller than the thumbnail, we upscale with nearest
|
||||||
* neighbor. Otherwise we make thumbnails that look fuzzy and awful.
|
* neighbor. Otherwise we make thumbnails that look fuzzy and awful.
|
||||||
*/
|
*/
|
||||||
if( !(interp = VIPS_INTERPOLATE( vips_object_new_from_string(
|
if( !(interp = VIPS_INTERPOLATE( vips_object_new_from_string(
|
||||||
g_type_class_ref( VIPS_TYPE_INTERPOLATE ),
|
g_type_class_ref( VIPS_TYPE_INTERPOLATE ),
|
||||||
residual > 1.0 ? "nearest" : interpolator ) )) )
|
shrink <= 1.0 ? "nearest" : interpolator ) )) )
|
||||||
return( NULL );
|
return( NULL );
|
||||||
|
|
||||||
vips_object_local( process, interp );
|
vips_object_local( process, interp );
|
||||||
@ -385,12 +347,15 @@ thumbnail_shrink( VipsObject *process, VipsImage *in,
|
|||||||
*/
|
*/
|
||||||
gboolean have_imported;
|
gboolean have_imported;
|
||||||
|
|
||||||
int shrink;
|
/* TRUE if we've premultiplied and need to unpremultiply.
|
||||||
double residual;
|
*/
|
||||||
int tile_width;
|
gboolean have_premultiplied;
|
||||||
int tile_height;
|
|
||||||
int nlines;
|
/* Sniff the incoming image and try to guess what the alpha max is.
|
||||||
double sigma;
|
*/
|
||||||
|
double max_alpha;
|
||||||
|
|
||||||
|
double shrink;
|
||||||
|
|
||||||
/* RAD needs special unpacking.
|
/* RAD needs special unpacking.
|
||||||
*/
|
*/
|
||||||
@ -404,6 +369,12 @@ thumbnail_shrink( VipsObject *process, VipsImage *in,
|
|||||||
in = t[0];
|
in = t[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Try to guess what the maximum alpha might be.
|
||||||
|
*/
|
||||||
|
max_alpha = 255;
|
||||||
|
if( in->BandFmt == VIPS_FORMAT_USHORT )
|
||||||
|
max_alpha = 65535;
|
||||||
|
|
||||||
/* In linear mode, we import right at the start.
|
/* In linear mode, we import right at the start.
|
||||||
*
|
*
|
||||||
* We also have to import the whole image if it's CMYK, since
|
* We also have to import the whole image if it's CMYK, since
|
||||||
@ -448,71 +419,42 @@ thumbnail_shrink( VipsObject *process, VipsImage *in,
|
|||||||
return( NULL );
|
return( NULL );
|
||||||
in = t[2];
|
in = t[2];
|
||||||
|
|
||||||
shrink = calculate_shrink( in, &residual, interp );
|
/* If there's an alpha, we have to premultiply before shrinking. See
|
||||||
|
* https://github.com/jcupitt/libvips/issues/291
|
||||||
vips_info( "vipsthumbnail", "integer shrink by %d", shrink );
|
*/
|
||||||
|
have_premultiplied = FALSE;
|
||||||
if( vips_shrink( in, &t[3], shrink, shrink, NULL ) )
|
if( in->Bands == 2 ||
|
||||||
|
(in->Bands == 4 && in->Type != VIPS_INTERPRETATION_CMYK) ||
|
||||||
|
in->Bands == 5 ) {
|
||||||
|
vips_info( "vipsthumbnail", "premultiplying alpha" );
|
||||||
|
if( vips_premultiply( in, &t[3],
|
||||||
|
"max_alpha", max_alpha,
|
||||||
|
NULL ) )
|
||||||
return( NULL );
|
return( NULL );
|
||||||
in = t[3];
|
in = t[3];
|
||||||
|
have_premultiplied = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/* We want to make sure we read the image sequentially.
|
shrink = calculate_shrink( in );
|
||||||
* However, the convolution we may be doing later will force us
|
|
||||||
* into SMALLTILE or maybe FATSTRIP mode and that will break
|
|
||||||
* sequentiality.
|
|
||||||
*
|
|
||||||
* So ... read into a cache where tiles are scanlines, and make sure
|
|
||||||
* we keep enough scanlines to be able to serve a line of tiles.
|
|
||||||
*
|
|
||||||
* We use a threaded tilecache to avoid a deadlock: suppose thread1,
|
|
||||||
* evaluating the top block of the output, is delayed, and thread2,
|
|
||||||
* evaluating the second block, gets here first (this can happen on
|
|
||||||
* a heavily-loaded system).
|
|
||||||
*
|
|
||||||
* With an unthreaded tilecache (as we had before), thread2 will get
|
|
||||||
* the cache lock and start evaling the second block of the shrink.
|
|
||||||
* When it reaches the png reader it will stall until the first block
|
|
||||||
* has been used ... but it never will, since thread1 will block on
|
|
||||||
* this cache lock.
|
|
||||||
*/
|
|
||||||
|
|
||||||
vips_get_tile_size( in,
|
vips_info( "vipsthumbnail", "shrink by %g", shrink );
|
||||||
&tile_width, &tile_height, &nlines );
|
vips_info( "vipsthumbnail", "%s interpolation",
|
||||||
if( vips_tilecache( in, &t[4],
|
VIPS_OBJECT_GET_CLASS( interp )->nickname );
|
||||||
"tile_width", in->Xsize,
|
|
||||||
"tile_height", 10,
|
if( vips_resize( in, &t[4], 1.0 / shrink,
|
||||||
"max_tiles", 1 + (nlines * 2) / 10,
|
"interpolate", interp,
|
||||||
"access", VIPS_ACCESS_SEQUENTIAL,
|
|
||||||
"threaded", TRUE,
|
|
||||||
NULL ) )
|
NULL ) )
|
||||||
return( NULL );
|
return( NULL );
|
||||||
in = t[4];
|
in = t[4];
|
||||||
|
|
||||||
/* If the final affine will be doing a large downsample, we can get
|
if( have_premultiplied ) {
|
||||||
* nasty aliasing on hard edges. Blur before affine to smooth this out.
|
vips_info( "vipsthumbnail", "unpremultiplying alpha" );
|
||||||
*
|
if( vips_unpremultiply( in, &t[5],
|
||||||
* Don't blur for very small shrinks, blur with radius 1 for x1.5
|
"max_alpha", max_alpha,
|
||||||
* shrinks, blur radius 2 for x2.5 shrinks and above, etc.
|
|
||||||
*/
|
|
||||||
sigma = ((1.0 / residual) - 0.5) / 1.5;
|
|
||||||
if( residual < 1.0 &&
|
|
||||||
sigma > 0.1 ) {
|
|
||||||
if( vips_gaussblur( in, &t[5], sigma, NULL ) )
|
|
||||||
return( NULL );
|
|
||||||
vips_info( "vipsthumbnail", "anti-alias, sigma %g",
|
|
||||||
sigma );
|
|
||||||
in = t[5];
|
|
||||||
}
|
|
||||||
|
|
||||||
if( vips_affine( in, &t[6], residual, 0, 0, residual,
|
|
||||||
"interpolate", interp,
|
|
||||||
NULL ) )
|
NULL ) )
|
||||||
return( NULL );
|
return( NULL );
|
||||||
in = t[6];
|
in = t[5];
|
||||||
|
}
|
||||||
vips_info( "vipsthumbnail", "residual scale by %g", residual );
|
|
||||||
vips_info( "vipsthumbnail", "%s interpolation",
|
|
||||||
VIPS_OBJECT_GET_CLASS( interp )->nickname );
|
|
||||||
|
|
||||||
/* Colour management.
|
/* Colour management.
|
||||||
*
|
*
|
||||||
@ -593,8 +535,7 @@ thumbnail_shrink( VipsObject *process, VipsImage *in,
|
|||||||
/* If we are upsampling, don't sharpen, since nearest looks dumb
|
/* If we are upsampling, don't sharpen, since nearest looks dumb
|
||||||
* sharpened.
|
* sharpened.
|
||||||
*/
|
*/
|
||||||
if( shrink >= 1 &&
|
if( shrink > 1.0 &&
|
||||||
residual <= 1.0 &&
|
|
||||||
sharpen ) {
|
sharpen ) {
|
||||||
vips_info( "vipsthumbnail", "sharpening thumbnail" );
|
vips_info( "vipsthumbnail", "sharpening thumbnail" );
|
||||||
if( vips_conv( in, &t[8], sharpen, NULL ) )
|
if( vips_conv( in, &t[8], sharpen, NULL ) )
|
||||||
|
Loading…
Reference in New Issue
Block a user