Skip to content

Commit d3ccadf

Browse files
committed
revise unpremultiply, again
We were not detecting division by zero carefully enough, nor clipping the alpha range sufficiently in unpremultiply. see #1941 also see #1675 for another difficult test case
1 parent 3c60e9d commit d3ccadf

File tree

3 files changed

+28
-25
lines changed

3 files changed

+28
-25
lines changed

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- fix includes of glib headers in C++ [lovell]
88
- fix build with more modern librsvg [lovell]
99
- fix a possible segv with very wide images [f1ac]
10+
- revise premultiply, again [jjonesrs]
1011

1112
18/12/20 started 8.10.5
1213
- fix potential /0 in animated webp load [lovell]

libvips/conversion/unpremultiply.c

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
* - max_alpha defaults to 65535 for RGB16/GREY16
88
* 24/11/17 lovell
99
* - match normalised alpha to output type
10+
* 27/2/21 jjonesrs
11+
* - revise range clipping and 1/x, again
1012
*/
1113

1214
/*
@@ -78,18 +80,12 @@ G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION );
7880
\
7981
for( x = 0; x < width; x++ ) { \
8082
IN alpha = p[alpha_band]; \
83+
IN clip_alpha = VIPS_CLIP( 0.0, alpha, max_alpha ); \
84+
OUT factor = max_alpha * vips_recip( clip_alpha ); \
8185
\
82-
if( alpha != 0 ) { \
83-
OUT factor = max_alpha / alpha; \
84-
\
85-
for( i = 0; i < alpha_band; i++ ) \
86-
q[i] = factor * p[i]; \
87-
q[alpha_band] = alpha; \
88-
} \
89-
else \
90-
for( i = 0; i < alpha_band + 1; i++ ) \
91-
q[i] = 0; \
92-
\
86+
for( i = 0; i < alpha_band; i++ ) \
87+
q[i] = factor * p[i]; \
88+
q[alpha_band] = clip_alpha; \
9389
for( i = alpha_band + 1; i < bands; i++ ) \
9490
q[i] = p[i]; \
9591
\
@@ -106,21 +102,13 @@ G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION );
106102
\
107103
for( x = 0; x < width; x++ ) { \
108104
IN alpha = p[3]; \
105+
IN clip_alpha = VIPS_CLIP( 0.0, alpha, max_alpha ); \
106+
OUT factor = max_alpha * vips_recip( clip_alpha ); \
109107
\
110-
if( alpha != 0 ) { \
111-
OUT factor = max_alpha / alpha; \
112-
\
113-
q[0] = factor * p[0]; \
114-
q[1] = factor * p[1]; \
115-
q[2] = factor * p[2]; \
116-
q[3] = alpha; \
117-
} \
118-
else { \
119-
q[0] = 0; \
120-
q[1] = 0; \
121-
q[2] = 0; \
122-
q[3] = 0; \
123-
} \
108+
q[0] = factor * p[0]; \
109+
q[1] = factor * p[1]; \
110+
q[2] = factor * p[2]; \
111+
q[3] = clip_alpha; \
124112
\
125113
p += 4; \
126114
q += 4; \

libvips/include/vips/util.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ extern "C" {
8484
#define VIPS_FMIN( A, B ) VIPS_MIN( A, B )
8585
#endif
8686

87+
/* Calculate 1.0 / x, avoiding division by zero when near zero.
88+
*/
89+
static inline double
90+
vips_recip( double x )
91+
{
92+
static const double epsilon = 1e-10;
93+
94+
int sign = x < 0.0 ? -1.0 : 1.0;
95+
96+
return( sign * x >= epsilon ?
97+
1.0 / x :
98+
sign / epsilon );
99+
}
100+
87101
/* Testing status before the function call saves a lot of time.
88102
*/
89103
#define VIPS_ONCE( ONCE, FUNC, CLIENT ) \

0 commit comments

Comments
 (0)