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:
parent
cb7bc24b2a
commit
712157cd16
|
@ -20,6 +20,7 @@
|
||||||
- add iiif layout to dzsave
|
- add iiif layout to dzsave
|
||||||
- fix use of resolution-unit metadata on tiff save [kayarre]
|
- fix use of resolution-unit metadata on tiff save [kayarre]
|
||||||
- support TIFF CIELAB images with alpha [angelmixu]
|
- support TIFF CIELAB images with alpha [angelmixu]
|
||||||
|
- support TIFF with premultiplied alpha in any band
|
||||||
|
|
||||||
17/9/19 started 8.8.4
|
17/9/19 started 8.8.4
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ typedef struct _VipsUnpremultiply {
|
||||||
VipsImage *in;
|
VipsImage *in;
|
||||||
|
|
||||||
double max_alpha;
|
double max_alpha;
|
||||||
|
int alpha_band;
|
||||||
|
|
||||||
} VipsUnpremultiply;
|
} VipsUnpremultiply;
|
||||||
|
|
||||||
|
@ -76,17 +77,21 @@ G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION );
|
||||||
OUT * restrict q = (OUT *) out; \
|
OUT * restrict q = (OUT *) out; \
|
||||||
\
|
\
|
||||||
for( x = 0; x < width; x++ ) { \
|
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 ); \
|
IN clip_alpha = VIPS_CLIP( 0, alpha, max_alpha ); \
|
||||||
OUT nalpha = (OUT) clip_alpha / max_alpha; \
|
OUT nalpha = (OUT) clip_alpha / max_alpha; \
|
||||||
\
|
\
|
||||||
if( nalpha == 0 ) \
|
if( nalpha == 0 ) \
|
||||||
for( i = 0; i < bands - 1; i++ ) \
|
for( i = 0; i < alpha_band + 1; i++ ) \
|
||||||
q[i] = 0; \
|
q[i] = 0; \
|
||||||
else \
|
else { \
|
||||||
for( i = 0; i < bands - 1; i++ ) \
|
for( i = 0; i < alpha_band; i++ ) \
|
||||||
q[i] = p[i] / nalpha; \
|
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; \
|
p += bands; \
|
||||||
q += bands; \
|
q += bands; \
|
||||||
|
@ -108,13 +113,14 @@ G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION );
|
||||||
q[0] = 0; \
|
q[0] = 0; \
|
||||||
q[1] = 0; \
|
q[1] = 0; \
|
||||||
q[2] = 0; \
|
q[2] = 0; \
|
||||||
|
q[3] = 0; \
|
||||||
} \
|
} \
|
||||||
else { \
|
else { \
|
||||||
q[0] = p[0] / nalpha; \
|
q[0] = p[0] / nalpha; \
|
||||||
q[1] = p[1] / nalpha; \
|
q[1] = p[1] / nalpha; \
|
||||||
q[2] = p[2] / nalpha; \
|
q[2] = p[2] / nalpha; \
|
||||||
|
q[3] = clip_alpha; \
|
||||||
} \
|
} \
|
||||||
q[3] = clip_alpha; \
|
|
||||||
\
|
\
|
||||||
p += 4; \
|
p += 4; \
|
||||||
q += 4; \
|
q += 4; \
|
||||||
|
@ -141,6 +147,7 @@ vips_unpremultiply_gen( VipsRegion *or, void *vseq, void *a, void *b,
|
||||||
int width = r->width;
|
int width = r->width;
|
||||||
int bands = im->Bands;
|
int bands = im->Bands;
|
||||||
double max_alpha = unpremultiply->max_alpha;
|
double max_alpha = unpremultiply->max_alpha;
|
||||||
|
int alpha_band = unpremultiply->alpha_band;
|
||||||
|
|
||||||
int x, y, i;
|
int x, y, i;
|
||||||
|
|
||||||
|
@ -235,6 +242,11 @@ vips_unpremultiply_build( VipsObject *object )
|
||||||
in->Type == VIPS_INTERPRETATION_RGB16 )
|
in->Type == VIPS_INTERPRETATION_RGB16 )
|
||||||
unpremultiply->max_alpha = 65535;
|
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 )
|
if( in->BandFmt == VIPS_FORMAT_DOUBLE )
|
||||||
conversion->out->BandFmt = VIPS_FORMAT_DOUBLE;
|
conversion->out->BandFmt = VIPS_FORMAT_DOUBLE;
|
||||||
else
|
else
|
||||||
|
@ -279,6 +291,13 @@ vips_unpremultiply_class_init( VipsUnpremultiplyClass *class )
|
||||||
G_STRUCT_OFFSET( VipsUnpremultiply, max_alpha ),
|
G_STRUCT_OFFSET( VipsUnpremultiply, max_alpha ),
|
||||||
0, 100000000, 255 );
|
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
|
static void
|
||||||
|
@ -296,10 +315,11 @@ vips_unpremultiply_init( VipsUnpremultiply *unpremultiply )
|
||||||
* Optional arguments:
|
* Optional arguments:
|
||||||
*
|
*
|
||||||
* * @max_alpha: %gdouble, maximum value for alpha
|
* * @max_alpha: %gdouble, maximum value for alpha
|
||||||
|
* * @alpha_band: %gint, band containing alpha data
|
||||||
*
|
*
|
||||||
* Unpremultiplies any alpha channel.
|
* Unpremultiplies any alpha channel.
|
||||||
* The final band is taken to be the alpha
|
* Band @alpha_band (by default the final band) contains the alpha and all
|
||||||
* and the bands are transformed as:
|
* other bands are transformed as:
|
||||||
*
|
*
|
||||||
* |[
|
* |[
|
||||||
* alpha = (int) clip( 0, in[in.bands - 1], @max_alpha );
|
* 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
|
* 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.
|
* and normalised final band, the final band is clipped.
|
||||||
* If there is only a single band,
|
* If there is only a single band, the image is passed through unaltered.
|
||||||
* the image is passed through unaltered.
|
|
||||||
*
|
*
|
||||||
* The result is
|
* The result is
|
||||||
* #VIPS_FORMAT_FLOAT unless the input format is #VIPS_FORMAT_DOUBLE, in which
|
* #VIPS_FORMAT_FLOAT unless the input format is #VIPS_FORMAT_DOUBLE, in which
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
* 7/6/19
|
* 7/6/19
|
||||||
* - istiff reads the first directory rather than just testing the magic
|
* - istiff reads the first directory rather than just testing the magic
|
||||||
* number, so it ignores more TIFF-like, but not TIFF images
|
* 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
|
#define DEBUG
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -253,7 +256,10 @@ typedef struct _RtiffHeader {
|
||||||
int sample_format;
|
int sample_format;
|
||||||
gboolean separate;
|
gboolean separate;
|
||||||
int orientation;
|
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;
|
uint16 compression;
|
||||||
|
|
||||||
/* Result of TIFFIsTiled().
|
/* Result of TIFFIsTiled().
|
||||||
|
@ -549,9 +555,9 @@ rtiff_strip_read( Rtiff *rtiff, int strip, tdata_t buf )
|
||||||
{
|
{
|
||||||
tsize_t length;
|
tsize_t length;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG_VERBOSE
|
||||||
printf( "rtiff_strip_read: reading strip %d\n", strip );
|
printf( "rtiff_strip_read: reading strip %d\n", strip );
|
||||||
#endif /*DEBUG*/
|
#endif /*DEBUG_VERBOSE*/
|
||||||
|
|
||||||
if( rtiff->header.read_scanlinewise )
|
if( rtiff->header.read_scanlinewise )
|
||||||
length = TIFFReadScanline( rtiff->tiff,
|
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( r->height == rtiff->header.tile_height );
|
||||||
g_assert( VIPS_REGION_LSKIP( out ) == VIPS_REGION_SIZEOF_LINE( out ) );
|
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",
|
printf( "rtiff_fill_region_aligned: left = %d, top = %d\n",
|
||||||
r->left, r->top );
|
r->left, r->top );
|
||||||
#endif /*DEBUG*/
|
#endif /*DEBUG_VERBOSE*/
|
||||||
|
|
||||||
VIPS_GATE_START( "rtiff_fill_region_aligned: work" );
|
VIPS_GATE_START( "rtiff_fill_region_aligned: work" );
|
||||||
|
|
||||||
|
@ -1729,10 +1735,13 @@ rtiff_autorotate( Rtiff *rtiff, VipsImage *in, VipsImage **out )
|
||||||
static int
|
static int
|
||||||
rtiff_unpremultiply( Rtiff *rtiff, VipsImage *in, VipsImage **out )
|
rtiff_unpremultiply( Rtiff *rtiff, VipsImage *in, VipsImage **out )
|
||||||
{
|
{
|
||||||
if( rtiff->header.premultiplied ) {
|
if( rtiff->header.alpha_band != -1 ) {
|
||||||
VipsImage *x;
|
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 ) ) {
|
vips_cast( x, out, in->BandFmt, NULL ) ) {
|
||||||
g_object_unref( x );
|
g_object_unref( x );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
@ -1881,11 +1890,11 @@ rtiff_stripwise_generate( VipsRegion *or,
|
||||||
|
|
||||||
int y;
|
int y;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG_VERBOSE
|
||||||
printf( "rtiff_stripwise_generate: top = %d, height = %d\n",
|
printf( "rtiff_stripwise_generate: top = %d, height = %d\n",
|
||||||
r->top, r->height );
|
r->top, r->height );
|
||||||
printf( "rtiff_stripwise_generate: y_top = %d\n", rtiff->y_pos );
|
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
|
/* We're inside a tilecache where tiles are the full image width, so
|
||||||
* this should always be true.
|
* this should always be true.
|
||||||
|
@ -2315,8 +2324,25 @@ rtiff_header_read( Rtiff *rtiff, RtiffHeader *header )
|
||||||
|
|
||||||
TIFFGetFieldDefaulted( rtiff->tiff, TIFFTAG_EXTRASAMPLES,
|
TIFFGetFieldDefaulted( rtiff->tiff, TIFFTAG_EXTRASAMPLES,
|
||||||
&extra_samples_count, &extra_samples_types );
|
&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 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue