/**************************************************************************
 *{@C
 *      Copyright:      1988-2020 Paul Obermeier (obermeier@poSoft.de)
 *
 *      Module:         ImageProcessing
 *      Filename:       IP_Gamma.c
 *
 *      Author:         Paul Obermeier
 *
 *      Description:    Functions to create gamma-correction lookup tables,
 *                      and functions to copy, format-convert and gamma
 *                      correct arrays of pixel data.
 *
 *      Additional documentation:
 *                      None.
 *
 *      Exported functions:
 *                      IP_UByteGammaTable
 *                      IP_UByteGammaTable2
 *                      IP_FloatGammaTable
 *                      IP_CopyUByte2UByte
 *                      IP_CopyFloat2Float
 *                      IP_CopyUByte2Float
 *                      IP_CopyFloat2UByte
 *                      IP_GammaUByte2Float
 *                      IP_GammaFloat2UByte
 *                      IP_GammaFloat2Float
 *
 **************************************************************************/

#include <stdio.h>
#include <math.h>

#include "UT_Compat.h"
#include "UT_Macros.h"
#include "UT_Error.h"

#include "FF_Image.h"

#include "IP_Image.h"
#include "IP_ImagePrivate.h"

/***************************************************************************
 *[@e
 *      Name:           IP_UByteGammaTable
 *
 *      Usage:          Create a lookup table for gamma-correcting pixel
 *                      data in FF_ImgFmtTypeUByte format.
 *
 *      Synopsis:       UT_Bool IP_UByteGammaTable
 *                              (Float32 gamma, UInt8 table[])
 *
 *      Description:    Create a lookup table for gamma-correcting pixel
 *                      data in FF_ImgFmtTypeUByte format. 
 *                      Gamma correction should be performed using the 
 *                      "gcorrect_UByte" macro.
 *                      "table" must be at least of size 256.
 *
 *      Return value:   UT_True if successful, else UT_False.
 *
 *      See also:
 *
 ***************************************************************************/

UT_Bool IP_UByteGammaTable (Float32 gamma, UInt8 table[])
{
    Int32 i, tmp;

    if (gamma < MINGAMMA || gamma > MAXGAMMA) {
        UT_ErrSetNum (UT_ErrParamRange, str_range_inclusive,
                     str_gamma, MINGAMMA, MAXGAMMA);
        return UT_False;
    }
    for (i = 0; i < 256; ++i) {
        tmp = (Int32)(255.0 * pow ((Float32) i / 255.0, gamma) + 0.5);
        table[i] = UT_MAX (0, UT_MIN (tmp, 255));
    }
    return UT_True;
}

/***************************************************************************
 *[@e
 *      Name:           IP_UByteGammaTable2
 *
 *      Usage:          Create a lookup table for gamma-correcting pixel
 *                      data in FF_ImgFmtTypeUByte format, resulting in 
 *                      FF_ImgFmtTypeFloat data.
 *
 *      Synopsis:       UT_Bool IP_UByteGammaTable2
 *                              (Float32 gamma, Float32 table[])
 *
 *      Description:    Create a lookup table for gamma-correcting pixel
 *                      data in FF_ImgFmtTypeUByte format, resulting in 
 *                      FF_ImgFmtTypeFloat data. Gamma correction should be
 *                      performed using the "gcorrect_UByte2" macro.
 *                      "table" must be at least of size 256.
 *
 *      Return value:   UT_True if successful, else UT_False.
 *
 *      See also:
 *
 ***************************************************************************/

UT_Bool IP_UByteGammaTable2 (Float32 gamma, Float32 table[])
{
    Int32 i;

    if (gamma < MINGAMMA || gamma > MAXGAMMA)
    {
        UT_ErrSetNum (UT_ErrParamRange, str_range_inclusive,
                     str_gamma, MINGAMMA, MAXGAMMA);
        return UT_False;
    }
    for (i = 0; i < 256; ++i)
        table[i] = pow ((Float32) i / 255.0, gamma);
    return UT_True;
}

/***************************************************************************
 *[@e
 *      Name:           IP_FloatGammaTable
 *
 *      Usage:          Create a lookup table for gamma-correcting pixel
 *                      data in FF_ImgFmtTypeFloat format.
 *
 *      Synopsis:       UT_Bool IP_FloatGammaTable
 *                              (Float32 gamma, Float32 table[])
 *
 *      Description:    Create a lookup table for gamma-correcting pixel
 *                      data in FF_ImgFmtTypeFloat format. Gamma correction should
 *                      be performed using the "gcorrect_Float" macro.
 *                      "table" must be at least of size GTABSIZE.
 *
 *      Return value:   UT_True if successful, else UT_False.
 *
 *      See also:
 *
 ***************************************************************************/

UT_Bool IP_FloatGammaTable (Float32 gamma, Float32 table[])
{
    Int32 i;

    if (gamma < MINGAMMA || gamma > MAXGAMMA) {
        UT_ErrSetNum (UT_ErrParamRange, str_range_inclusive,
                     str_gamma, MINGAMMA, MAXGAMMA);
        return UT_False;
    }
    for (i = 0; i < GTABSIZE - 1; ++i)
        table[i] = pow ((Float32) i / (Float32) (GTABSIZE - 2), gamma);
    table[GTABSIZE - 1] = 1.0;
    return UT_True;
}

/***************************************************************************
 *[@e
 *      Name:           IP_CopyUByte2UByte
 *
 *      Usage:          Copy an array of pixel data, optionally converting the
 *                      pixels between FF_ImgFmtTypeUByte and FF_ImgFmtTypeFloat
 *                      format.
 *
 *      Synopsis:       void IP_CopyUByte2UByte
 *                              (Int32 n, const UInt8 ub_in[], UInt8 ub_out[])
 *
 *      Description:    IP_CopyUByte2UByte copies "n" elements from UInt8
 *                      array "ub_in" into UInt8 array "ub_out".
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void IP_CopyUByte2UByte (Int32 n, const UInt8 ub_in[], UInt8 ub_out[])
{
    const UInt8 *ubsrc, *ubstop;
    UInt8       *ubdest;

    ubsrc = ub_in;
    ubstop = ub_in + n;
    ubdest = ub_out;
    while (ubsrc < ubstop)
        *(ubdest++) = *(ubsrc++);
    return;
}

/***************************************************************************
 *[@e
 *      Name:           IP_CopyFloat2Float
 *
 *      Usage:          Copy an array of pixel data, optionally converting the
 *                      pixels between FF_ImgFmtTypeUByte and FF_ImgFmtTypeFloat
 *                      format.
 *
 *      Synopsis:       void IP_CopyFloat2Float
 *                              (Int32 n, const Float32 f_in[], Float32 f_out[])
 *
 *      Description:    IP_CopyFloat2Float copies "n" elements from Float32
 *                      array "f_in" into Float32 array "f_out".
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void IP_CopyFloat2Float (Int32 n, const Float32 f_in[], Float32 f_out[])
{
    const Float32 *fsrc, *fstop;
    Float32       *fdest;

    fsrc = f_in;
    fstop = f_in + n;
    fdest = f_out;
    while (fsrc < fstop)
        *(fdest++) = *(fsrc++);
    return;
}

/***************************************************************************
 *[@e
 *      Name:           IP_CopyUByte2FloatAsIs
 *
 *      Usage:          Copy an array of pixel data, converting the
 *                      pixels between FF_ImgFmtTypeUByte and FF_ImgFmtTypeFloat
 *                      format.
 *
 *      Synopsis:       void IP_CopyUByte2FloatAsIs
 *                              (Int32 n, const UInt8 ub_in[], Float32 f_out[])
 *
 *      Description:    IP_CopyUByte2FloatAsIs copies "n" elements from array
 *                      "ub_in" into array "f_out", converting the array
 *                      elements from FF_ImgFmtTypeUByte to FF_ImgFmtTypeFloat
 *                      format.
 *
 *      Return value:   None.
 *
 *      See also:       IP_CopyUByte2Float
 *
 ***************************************************************************/

void IP_CopyUByte2FloatAsIs (Int32 n, const UInt8 ub_in[], Float32 f_out[])
{
    const UInt8 *ubsrc, *ubstop;
    Float32     *fdest;

    ubsrc = ub_in;
    ubstop = ub_in + n;
    fdest = f_out;
    while (ubsrc < ubstop) {
        *(fdest++) = (Float32) *(ubsrc++);
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           IP_CopyUByte2Float
 *
 *      Usage:          Copy an array of pixel data, optionally converting the
 *                      pixels between FF_ImgFmtTypeUByte and FF_ImgFmtTypeFloat
 *                      format.
 *
 *      Synopsis:       void IP_CopyUByte2Float
 *                              (Int32 n, const UInt8 ub_in[], Float32 f_out[])
 *
 *      Description:    IP_CopyUByte2Float copies "n" elements from array
 *                      "ub_in" into array "f_out", converting the array
 *                      elements from FF_ImgFmtTypeUByte to FF_ImgFmtTypeFloat
 *                      format.
 *
 *                      Note: The input values are scaled by 1/255 to fit into
 *                      the range [0.0 1.0].
 *
 *      Return value:   None.
 *
 *      See also:       IP_CopyUByte2FloatAsIs
 *
 ***************************************************************************/

void IP_CopyUByte2Float (Int32 n, const UInt8 ub_in[], Float32 f_out[])
{
    const UInt8 *ubsrc, *ubstop;
    Float32     *fdest;

    ubsrc = ub_in;
    ubstop = ub_in + n;
    fdest = f_out;
    while (ubsrc < ubstop) {
        *(fdest++) = (1.0 / 255.0) * *(ubsrc++);
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           IP_CopyFloat2UByte
 *
 *      Usage:          Copy an array of pixel data, optionally converting the
 *                      pixels between FF_ImgFmtTypeUByte and FF_ImgFmtTypeFloat
 *                      format.
 *
 *      Synopsis:       void IP_CopyFloat2UByte
 *                              (Int32 n, const Float32 f_in[], UInt8 ub_out[])
 *
 *      Description:    IP_CopyFloat2UByte copies "n" elements from array
 *                      "f_in" into array "ub_out", converting the array
 *                      elements from FF_ImgFmtTypeFloat to FF_ImgFmtTypeUByte
 *                      format.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void IP_CopyFloat2UByte (Int32 n, const Float32 f_in[], UInt8 ub_out[])
{
    const Float32 *fsrc, *fstop;
    Float32     ftmp;
    UInt8       *ubdest;
    Int32       itmp;

    fsrc = f_in;
    fstop = f_in + n;
    ubdest = ub_out;
    while (fsrc < fstop) {
        ftmp = *(fsrc++);
        itmp = (Int32)(255.0 * UT_MAX (0.0, UT_MIN (ftmp, 1.0)) + 0.5);
        *(ubdest++) = UT_MAX (0, UT_MIN (itmp, 255));
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           IP_GammaUByte2Float
 *
 *      Usage:          Copy an array of pixel data, performing gamma
 *                      correction and format conversion at the same time.
 *
 *      Synopsis:       void IP_GammaUByte2Float
 *                              (Int32 n,
 *                              const UInt8 ub_in[],
 *                              const Float32 gtable[],
 *                              Float32 f_out[])
 *
 *      Description:    IP_GammaUByte2Float copies the first "n" elements
 *                      from array "ub_in" into array "f_out", converting them
 *                      from FF_ImgFmtTypeUByte to FF_ImgFmtTypeFloat format and
 *                      gamma-correcting the elements according to gamma lookup
 *                      table "gtable".
 *                      "table" must be at least of size GTABSIZE.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void IP_GammaUByte2Float
        (Int32 n,
         const UInt8 ub_in[],
         const Float32 gtable[],
         Float32 f_out[])
{
    const UInt8 *ubsrc, *ubstop;
    Float32     *fdest;

    ubsrc = ub_in;
    ubstop = ub_in + n;
    fdest = f_out;
    while (ubsrc < ubstop) {
        gcorrect_UByte2 (*ubsrc, gtable, *fdest);
        ++fdest;
        ++ubsrc;
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           IP_GammaFloat2UByte
 *
 *      Usage:          Copy an array of pixel data, performing gamma
 *                      correction and format conversion at the same time.
 *
 *      Synopsis:       void IP_GammaFloat2UByte
 *                              (Int32 n,
 *                              const Float32 f_in[],
 *                              const Float32 gtable[],
 *                              UInt8 ub_out[])
 *
 *      Description:    IP_GammaFloat2UByte copies the first "n" elements
 *                      from array "f_in" into array "ub_out", converting them
 *                      from FF_ImgFmtTypeFloat to FF_ImgFmtTypeUByte format and
 *                      gamma-correcting the elements according to gamma lookup
 *                      table "gtable".
 *                      "table" must be at least of size GTABSIZE.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void IP_GammaFloat2UByte
        (Int32 n,
         const Float32 f_in[],
         const Float32 gtable[],
         UInt8 ub_out[])
{
    const Float32 *fsrc, *fstop;
    Float32     ftmp;
    Int32       itmp;
    UInt8       *ubdest;

    fsrc = f_in;
    fstop = f_in + n;
    ubdest = ub_out;
    while (fsrc < fstop) {
        ftmp = UT_MAX (0.0, UT_MIN (*fsrc, 1.0));
        gcorrect_Float (ftmp, gtable, ftmp);
        itmp = (Int32)(ftmp * 255.0 + 0.5);
        *ubdest = UT_MAX (0, UT_MIN (itmp, 255));
        ++ubdest;
        ++fsrc;
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           IP_GammaFloat2Float
 *
 *      Usage:          Copy an array of pixel data, performing gamma
 *                      correction and format conversion at the same time.
 *
 *      Synopsis:       void IP_GammaFloat2Float
 *                              (Int32 n,
 *                              const Float32 f_in[],
 *                              const Float32 gtable[],
 *                              Float32 f_out[])
 *
 *      Description:    IP_GammaFloat2Float copies the first "n" elements
 *                      from array "f_in" into array "f_out", gamma-correcting
 *                      them according to gamma lookup table "gtable".
 *                      "table" must be at least of size GTABSIZE.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void IP_GammaFloat2Float
        (Int32 n,
         const Float32 f_in[],
         const Float32 gtable[],
         Float32 f_out[])
{
    const Float32 *fsrc, *fstop;
    Float32       *fdest, ftmp;

    fsrc = f_in;
    fstop = f_in + n;
    fdest = f_out;
    while (fsrc < fstop) {
        ftmp = UT_MAX (0.0, UT_MIN (*fsrc, 1.0));
        gcorrect_Float (ftmp, gtable, *fdest);
        ++fdest;
        ++fsrc;
    }
    return;
}
