From c8b235950170d20526e3f9f8d805e166b92127b4 Mon Sep 17 00:00:00 2001
From: Martin Vignali <martin.vignali@gmail.com>
Date: Wed, 1 Mar 2017 23:25:40 +0100
Subject: [PATCH] libavcodec/psd : add support for rgb/gray float

Float data is convert to uint16.
---
 libavcodec/psd.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/libavcodec/psd.c b/libavcodec/psd.c
index 66f2ec2..a381160 100644
--- a/libavcodec/psd.c
+++ b/libavcodec/psd.c
@@ -19,8 +19,11 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include <float.h>
+
 #include "bytestream.h"
 #include "internal.h"
+#include "libavutil/opt.h"
 
 enum PsdCompr {
     PSD_RAW,
@@ -61,6 +64,8 @@ typedef struct PSDContext {
     enum PsdCompr compression;
     enum PsdColorMode color_mode;
 
+    float gamma;
+
     uint8_t palette[AVPALETTE_SIZE];
 } PSDContext;
 
@@ -290,6 +295,21 @@ static int decode_rle(PSDContext * s){
     return 0;
 }
 
+
+static inline uint16_t psd_flt2uint(uint32_t v)
+{
+    unsigned int exp = v >> 23;
+    // "HACK": negative values result in exp<  0, so clipping them to 0
+    // is also handled by this condition, avoids explicit check for sign bit.
+    if (exp <= 127 + 7 - 24) // we would shift out all bits anyway
+        return 0;
+    if (exp >= 127)
+        return 0xffff;
+    v &= 0x007fffff;
+    return (v + (1 << 23)) >> (127 + 7 - exp);
+}
+
+
 static int decode_frame(AVCodecContext *avctx, void *data,
                         int *got_frame, AVPacket *avpkt)
 {
@@ -299,6 +319,9 @@ static int decode_frame(AVCodecContext *avctx, void *data,
     int index_out, c, y, x, p;
     uint8_t eq_channel[4] = {2,0,1,3};/* RGBA -> GBRA channel order */
     uint8_t plane_number;
+    uint16_t * buffer_float_to_uint16;
+    union av_intfloat32 t;
+    float one_gamma;
 
     AVFrame *picture = data;
 
@@ -343,6 +366,8 @@ static int decode_frame(AVCodecContext *avctx, void *data,
                 avctx->pix_fmt = AV_PIX_FMT_GBRP;
             } else if (s->channel_depth == 16) {
                 avctx->pix_fmt = AV_PIX_FMT_GBRP16BE;
+            } else if (s->channel_depth == 32) {
+                avctx->pix_fmt = AV_PIX_FMT_GBRP16LE;
             } else {
                 avpriv_report_missing_feature(avctx, "channel depth %d for rgb", s->channel_depth);
                 return AVERROR_PATCHWELCOME;
@@ -369,6 +394,8 @@ static int decode_frame(AVCodecContext *avctx, void *data,
                 avctx->pix_fmt = AV_PIX_FMT_GRAY8;
             } else if (s->channel_depth == 16) {
                 avctx->pix_fmt = AV_PIX_FMT_GRAY16BE;
+            } else if (s->channel_depth == 32) {
+                avctx->pix_fmt = AV_PIX_FMT_GRAY16LE;
             } else {
                 avpriv_report_missing_feature(avctx, "channel depth %d for grayscale", s->channel_depth);
                 return AVERROR_PATCHWELCOME;
@@ -399,6 +426,11 @@ static int decode_frame(AVCodecContext *avctx, void *data,
 
     /* decode picture if need */
     if (s->compression == PSD_RLE) {
+        if (s->channel_depth == 32) {
+            avpriv_report_missing_feature(avctx, "float with rle compression");
+            return AVERROR_PATCHWELCOME;
+        }
+
         s->tmp = av_malloc(s->uncompressed_size);
         if (!s->tmp)
             return AVERROR(ENOMEM);
@@ -419,6 +451,29 @@ static int decode_frame(AVCodecContext *avctx, void *data,
         ptr_data = s->gb.buffer;
     }
 
+    if (s->channel_depth == 32) {/* convert float to uint16 */
+        av_log(s->avctx, AV_LOG_DEBUG, "float data.\n");
+        one_gamma = 1.0f / s->gamma;
+        buffer_float_to_uint16 = av_malloc(s->uncompressed_size >> 1);
+        if (!buffer_float_to_uint16)
+            return AVERROR(ENOMEM);
+
+        if (one_gamma > 0.9999f && one_gamma < 1.0001f) {/* ignore gamma correction */
+            for (p = 0; p < s->width * s->height * s->channel_count; p++) {/* for each RGB pixel */
+                buffer_float_to_uint16[p] = psd_flt2uint(bytestream_get_be32(&ptr_data));
+            }
+        } else {
+            for (p = 0; p < s->width * s->height * s->channel_count; p++) {/* for each RGB pixel */
+                t.i = bytestream_get_be32(&ptr_data);
+                t.f = powf(t.f, one_gamma);
+                buffer_float_to_uint16[p] = psd_flt2uint(t.i);
+            }
+        }
+
+        ptr_data = (uint8_t *)buffer_float_to_uint16;
+        s->line_size = s->line_size >> 1;/* float size to uint16 size */
+    }
+
     /* Store data */
     if ((avctx->pix_fmt == AV_PIX_FMT_YA8)||(avctx->pix_fmt == AV_PIX_FMT_YA16BE)){/* Interleaved */
         ptr = picture->data[0];
@@ -455,12 +510,30 @@ static int decode_frame(AVCodecContext *avctx, void *data,
 
     av_freep(&s->tmp);
 
+    if (s->channel_depth == 32)
+        av_freep(&buffer_float_to_uint16);
+
     picture->pict_type = AV_PICTURE_TYPE_I;
     *got_frame = 1;
 
     return avpkt->size;
 }
 
+#define OFFSET(x) offsetof(PSDContext, x)
+#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+    { "gamma", "Set the float gamma value when decoding float data", OFFSET(gamma),
+        AV_OPT_TYPE_FLOAT, { .dbl = 1.0f }, 0.001, FLT_MAX, VD },
+    { NULL },
+};
+
+static const AVClass psd_class = {
+    .class_name = "PSD",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
 AVCodec ff_psd_decoder = {
     .name             = "psd",
     .long_name        = NULL_IF_CONFIG_SMALL("Photoshop PSD file"),
@@ -469,4 +542,5 @@ AVCodec ff_psd_decoder = {
     .priv_data_size   = sizeof(PSDContext),
     .decode           = decode_frame,
     .capabilities     = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS,
+    .priv_class       = &psd_class,
 };
-- 
1.9.3 (Apple Git-50)

