[Libav-user] EXR gamma flag patch
Gonzalo Garramuno
ggarra13 at gmail.com
Sun Apr 27 02:49:07 CEST 2014
Find attached a first proposal for a patch to the exr reader to support
a gamma flag. Currently the half float routine is unoptimized and I
will change it in the near future.
-------------- next part --------------
diff --git a/libavcodec/exr.c b/libavcodec/exr.c
index 084025a..59f7dad 100644
--- a/libavcodec/exr.c
+++ b/libavcodec/exr.c
@@ -31,6 +31,7 @@
*/
#include <zlib.h>
+#include <float.h>
#include "libavutil/imgutils.h"
#include "libavutil/opt.h"
@@ -106,8 +107,34 @@ typedef struct EXRContext {
EXRThreadData *thread_data;
const char *layer;
+
+ float gamma;
+
} EXRContext;
+union FP32
+{
+ uint32_t u;
+ float f;
+ struct
+ {
+ unsigned int Mantissa : 23;
+ unsigned int Exponent : 8;
+ unsigned int Sign : 1;
+ };
+} FP32;
+
+union FP16
+{
+ uint16_t u;
+ struct
+ {
+ unsigned int Mantissa : 10;
+ unsigned int Exponent : 5;
+ unsigned int Sign : 1;
+ };
+} FP16;
+
/**
* Convert from 32-bit float as uint32_t to uint16_t.
*
@@ -128,6 +155,49 @@ static inline uint16_t exr_flt2uint(uint32_t v)
return (v + (1 << 23)) >> (127 + 7 - exp);
}
+static union FP32 half_to_float_full(union FP16 h)
+{
+ union FP32 o = { 0 };
+
+ // From ISPC ref code
+ if (h.Exponent == 0 && h.Mantissa == 0) // (Signed) zero
+ o.Sign = h.Sign;
+ else
+ {
+ if (h.Exponent == 0) // Denormal (will convert to normalized)
+ {
+ // Adjust mantissa so it's normalized (and keep track of exp adjust)
+ int e = -1;
+ uint m = h.Mantissa;
+ do
+ {
+ e++;
+ m <<= 1;
+ } while ((m & 0x400) == 0);
+
+ o.Mantissa = (m & 0x3ff) << 13;
+ o.Exponent = 127 - 15 - e;
+ o.Sign = h.Sign;
+ }
+ else if (h.Exponent == 0x1f) // Inf/NaN
+ {
+ // NOTE: It's safe to treat both with the same code path by just truncating
+ // lower Mantissa bits in NaNs (this is valid).
+ o.Mantissa = h.Mantissa << 13;
+ o.Exponent = 255;
+ o.Sign = h.Sign;
+ }
+ else // Normalized number
+ {
+ o.Mantissa = h.Mantissa << 13;
+ o.Exponent = 127 - 15 + h.Exponent;
+ o.Sign = h.Sign;
+ }
+ }
+
+ return o;
+}
+
/**
* Convert from 16-bit float as uint16_t to uint16_t.
*
@@ -772,6 +842,7 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
int bxmin = s->xmin * 2 * s->desc->nb_components;
int i, x, buf_size = s->buf_size;
int ret;
+ float one_gamma = 1.0f / s->gamma;
line_offset = AV_RL64(s->gb.buffer + jobnr * 8);
// Check if the buffer has the required bytes needed from the offset
@@ -851,18 +922,44 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
if (s->pixel_type == EXR_FLOAT) {
// 32-bit
for (x = 0; x < xdelta; x++) {
- *ptr_x++ = exr_flt2uint(bytestream_get_le32(&r));
- *ptr_x++ = exr_flt2uint(bytestream_get_le32(&g));
- *ptr_x++ = exr_flt2uint(bytestream_get_le32(&b));
+ uint32_t t = bytestream_get_le32(&r);
+ float* p = (float*) &t;
+ *p = powf( *p, one_gamma );
+ *ptr_x++ = exr_flt2uint(t);
+
+ t = bytestream_get_le32(&g);
+ p = (float*) &t;
+ *p = powf( *p, one_gamma );
+ *ptr_x++ = exr_flt2uint(t);
+
+ t = bytestream_get_le32(&b);
+ p = (float*)&t;
+ *p = powf( *p, one_gamma );
+ *ptr_x++ = exr_flt2uint(t);
if (channel_buffer[3])
+ {
*ptr_x++ = exr_flt2uint(bytestream_get_le32(&a));
}
+ }
} else {
// 16-bit
for (x = 0; x < xdelta; x++) {
- *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&r));
- *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&g));
- *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&b));
+ union FP16 t; union FP32 p;
+ t.u = bytestream_get_le16(&r);
+ p = half_to_float_full(t);
+ p.f = powf( p.f, one_gamma );
+ *ptr_x++ = exr_flt2uint(p.u);
+
+ t.u = bytestream_get_le16(&g);
+ p = half_to_float_full(t);
+ p.f = powf( p.f, one_gamma );
+ *ptr_x++ = exr_flt2uint(p.u);
+
+ t.u = bytestream_get_le16(&b);
+ p = half_to_float_full(t);
+ p.f = powf( p.f, one_gamma );
+ *ptr_x++ = exr_flt2uint(p.u);
+
if (channel_buffer[3])
*ptr_x++ = exr_halflt2uint(bytestream_get_le16(&a));
}
@@ -1322,6 +1419,8 @@ static av_cold int decode_end(AVCodecContext *avctx)
static const AVOption options[] = {
{ "layer", "Set the decoding layer", OFFSET(layer),
AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, VD },
+ { "gamma", "Set the float gamma value when decoding", OFFSET(gamma),
+ AV_OPT_TYPE_FLOAT, { .dbl = 2.2 }, 0.001, FLT_MAX, VD },
{ NULL },
};
More information about the Libav-user
mailing list