better support for TIFFs with many alphas

The premultiplied alpha can be in any position, and it checks if there
is more than one ASSOCALPHA.

See https://github.com/libvips/libvips/issues/1471
This commit is contained in:
John Cupitt 2019-11-18 16:48:34 +00:00
parent cb7bc24b2a
commit 712157cd16
3 changed files with 67 additions and 21 deletions

View File

@ -20,6 +20,7 @@
- add iiif layout to dzsave
- fix use of resolution-unit metadata on tiff save [kayarre]
- support TIFF CIELAB images with alpha [angelmixu]
- support TIFF with premultiplied alpha in any band
17/9/19 started 8.8.4

View File

@ -62,6 +62,7 @@ typedef struct _VipsUnpremultiply {
VipsImage *in;
double max_alpha;
int alpha_band;
} VipsUnpremultiply;
@ -76,17 +77,21 @@ G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION );
OUT * restrict q = (OUT *) out; \
\
for( x = 0; x < width; x++ ) { \
IN alpha = p[bands - 1]; \
IN alpha = p[alpha_band]; \
IN clip_alpha = VIPS_CLIP( 0, alpha, max_alpha ); \
OUT nalpha = (OUT) clip_alpha / max_alpha; \
\
if( nalpha == 0 ) \
for( i = 0; i < bands - 1; i++ ) \
for( i = 0; i < alpha_band + 1; i++ ) \
q[i] = 0; \
else \
for( i = 0; i < bands - 1; i++ ) \
else { \
for( i = 0; i < alpha_band; i++ ) \
q[i] = p[i] / nalpha; \
q[i] = clip_alpha; \
q[alpha_band] = clip_alpha; \
} \
\
for( i = alpha_band + 1; i < bands; i++ ) \
q[i] = p[i]; \
\
p += bands; \
q += bands; \
@ -108,13 +113,14 @@ G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION );
q[0] = 0; \
q[1] = 0; \
q[2] = 0; \
q[3] = 0; \
} \
else { \
q[0] = p[0] / nalpha; \
q[1] = p[1] / nalpha; \
q[2] = p[2] / nalpha; \
q[3] = clip_alpha; \
} \
q[3] = clip_alpha; \
\
p += 4; \
q += 4; \
@ -141,6 +147,7 @@ vips_unpremultiply_gen( VipsRegion *or, void *vseq, void *a, void *b,
int width = r->width;
int bands = im->Bands;
double max_alpha = unpremultiply->max_alpha;
int alpha_band = unpremultiply->alpha_band;
int x, y, i;
@ -235,6 +242,11 @@ vips_unpremultiply_build( VipsObject *object )
in->Type == VIPS_INTERPRETATION_RGB16 )
unpremultiply->max_alpha = 65535;
/* Is alpha-band unset? Default to the final band for this image.
*/
if( !vips_object_argument_isset( object, "alpha_band" ) )
unpremultiply->alpha_band = in->Bands - 1;
if( in->BandFmt == VIPS_FORMAT_DOUBLE )
conversion->out->BandFmt = VIPS_FORMAT_DOUBLE;
else
@ -279,6 +291,13 @@ vips_unpremultiply_class_init( VipsUnpremultiplyClass *class )
G_STRUCT_OFFSET( VipsUnpremultiply, max_alpha ),
0, 100000000, 255 );
VIPS_ARG_INT( class, "alpha_band", 116,
_( "Alpha band" ),
_( "Unpremultiply with this alpha" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsUnpremultiply, alpha_band ),
0, 100000000, 3 );
}
static void
@ -296,10 +315,11 @@ vips_unpremultiply_init( VipsUnpremultiply *unpremultiply )
* Optional arguments:
*
* * @max_alpha: %gdouble, maximum value for alpha
* * @alpha_band: %gint, band containing alpha data
*
* Unpremultiplies any alpha channel.
* The final band is taken to be the alpha
* and the bands are transformed as:
* Band @alpha_band (by default the final band) contains the alpha and all
* other bands are transformed as:
*
* |[
* alpha = (int) clip( 0, in[in.bands - 1], @max_alpha );
@ -312,8 +332,7 @@ vips_unpremultiply_init( VipsUnpremultiply *unpremultiply )
*
* 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.
* 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

View File

@ -189,6 +189,8 @@
* 7/6/19
* - istiff reads the first directory rather than just testing the magic
* number, so it ignores more TIFF-like, but not TIFF images
* 18/11/19
* - support ASSOCALPHA in any alpha band
*/
/*
@ -219,6 +221,7 @@
*/
/*
#define DEBUG_VERBOSE
#define DEBUG
*/
@ -253,7 +256,10 @@ typedef struct _RtiffHeader {
int sample_format;
gboolean separate;
int orientation;
gboolean premultiplied;
/* If there's a premultiplied alpha, the band we need to
* unpremultiply with. -1 for no unpremultiplication.
*/
int alpha_band;
uint16 compression;
/* Result of TIFFIsTiled().
@ -549,9 +555,9 @@ rtiff_strip_read( Rtiff *rtiff, int strip, tdata_t buf )
{
tsize_t length;
#ifdef DEBUG
#ifdef DEBUG_VERBOSE
printf( "rtiff_strip_read: reading strip %d\n", strip );
#endif /*DEBUG*/
#endif /*DEBUG_VERBOSE*/
if( rtiff->header.read_scanlinewise )
length = TIFFReadScanline( rtiff->tiff,
@ -1542,10 +1548,10 @@ rtiff_fill_region_aligned( VipsRegion *out, void *seq, void *a, void *b )
g_assert( r->height == rtiff->header.tile_height );
g_assert( VIPS_REGION_LSKIP( out ) == VIPS_REGION_SIZEOF_LINE( out ) );
#ifdef DEBUG
#ifdef DEBUG_VERBOSE
printf( "rtiff_fill_region_aligned: left = %d, top = %d\n",
r->left, r->top );
#endif /*DEBUG*/
#endif /*DEBUG_VERBOSE*/
VIPS_GATE_START( "rtiff_fill_region_aligned: work" );
@ -1729,10 +1735,13 @@ rtiff_autorotate( Rtiff *rtiff, VipsImage *in, VipsImage **out )
static int
rtiff_unpremultiply( Rtiff *rtiff, VipsImage *in, VipsImage **out )
{
if( rtiff->header.premultiplied ) {
if( rtiff->header.alpha_band != -1 ) {
VipsImage *x;
if( vips_unpremultiply( in, &x, NULL ) ||
if(
vips_unpremultiply( in, &x,
"alpha_band", rtiff->header.alpha_band,
NULL ) ||
vips_cast( x, out, in->BandFmt, NULL ) ) {
g_object_unref( x );
return( -1 );
@ -1881,11 +1890,11 @@ rtiff_stripwise_generate( VipsRegion *or,
int y;
#ifdef DEBUG
#ifdef DEBUG_VERBOSE
printf( "rtiff_stripwise_generate: top = %d, height = %d\n",
r->top, r->height );
printf( "rtiff_stripwise_generate: y_top = %d\n", rtiff->y_pos );
#endif /*DEBUG*/
#endif /*DEBUG_VERBOSE*/
/* We're inside a tilecache where tiles are the full image width, so
* this should always be true.
@ -2315,8 +2324,25 @@ rtiff_header_read( Rtiff *rtiff, RtiffHeader *header )
TIFFGetFieldDefaulted( rtiff->tiff, TIFFTAG_EXTRASAMPLES,
&extra_samples_count, &extra_samples_types );
header->premultiplied = extra_samples_count > 0 &&
extra_samples_types[0] == EXTRASAMPLE_ASSOCALPHA;
header->alpha_band = -1;
if( extra_samples_count > 0 ) {
/* There must be exactly one band which is
* EXTRASAMPLE_ASSOCALPHA. Note which one it is so we can
* unpremultiply with the right channel.
*/
int i;
for( i = 0; i < extra_samples_count; i++ )
if( extra_samples_types[i] == EXTRASAMPLE_ASSOCALPHA ) {
if( header->alpha_band != -1 )
g_warning( "%s", _( "more than one "
"alpha -- ignoring" ) );
header->alpha_band = header->samples_per_pixel -
extra_samples_count + i;
}
}
return( 0 );
}