[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