/**************************************************************************
 *{@C
 *      Copyright:      1988-2024 Paul Obermeier (obermeier@poSoft.de)
 *
 *      Module:         ImageProcessing
 *      Filename:       IP_Pixel.c
 *
 *      Author:         Paul Obermeier
 *
 *      Description:    Functions to extract pixel data from an image.
 *
 *      Additional documentation:
 *                      None.
 *
 *      Exported functions:
 *                      IP_GetUByteRow
 *                      IP_GetFloatRow
 *                      IP_ConvertUByteRow
 *                      IP_GetSample
 *
 **************************************************************************/

#include <stdio.h>

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

#define  USE_IMG_MACROS
#include "FF_Image.h"

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

/***************************************************************************
 *[@e
 *      Name:           IP_ConvertUByteRow
 *
 *      Usage:          Extract one scan line of pixel data in
 *                      FF_ImgFmtTypeUByte format from an image.
 *
 *      Synopsis:       void IP_ConvertUByteRow
 *                              (IP_ImageId img, Int32 y,
 *                               UInt8 *buf[],
 *                               UInt8 *data[])
 *
 *      Description:    The pixel data for all pixels in scan line number "y"
 *                      of image "img" are made accessible to the function
 *                      invoking IP_ConvertUByteRow. If necessary, pixel data
 *                      are converted to FF_ImgFmtTypeUByte format.
 *                      Copying data is avoided where possible.
 *
 *                      Let "w" and "h" be the width and height of image "img",
 *                      in pixels.
 *                      
 *                      "Buf" is an array of pointers to data buffers, which
 *                      IP_ConvertUByteRow may fill with pixel data, if
 *                      necessary. "Buf" must be supplied by the function
 *                      invoking IP_ConvertUByteRow.
 *                      For every possible data channel "chn", buf[chn] must
 *                      either be a pointer to an array of "w" UInt8's, or a
 *                      NULL pointer.
 *                      If buf[chn] is not a NULL pointer, IP_ConvertUByteRow
 *                      does the following:
 *
 *                      - If "y" is not less than "h" or less than 0, or if
 *                        channel "chn" in "img" is in FF_ImgFmtTypeNone format,
 *                        then fill the data buffer pointed to by buf[chn] with
 *                        "w" zeroes and copy buf[chn] to data[chn].
 *
 *                      - If "y" is less than "h" and not less than 0, and if
 *                        channel "chn" in "img" is in FF_ImgFmtTypeFloat format,
 *                        then convert channel "chn" of scan line "y" of image
 *                        "img" to FF_ImgFmtTypeUByte format and write the result
 *                        into the data buffer pointed to by buf[chn]. Copy
 *                        buf[chn] to data[chn].
 *
 *                      - If "y" is less than "h" and not less than 0, and if
 *                        channel "chn" in "img" is in FF_ImgFmtTypeUByte format,
 *                        then write a pointer to the the location where the
 *                        data for channel "chn" of scan line "y" of "img" are
 *                        stored, to data[chn].
 *
 *                      When IP_ConvertUByteRow returns to its caller,
 *                      data[chn] contains a pointer to the data for channel
 *                      "chn" in scan line "y" of image "img", if buf[chn] is
 *                      not NULL. The data are always in FF_ImgFmtTypeUByte format.
 *
 *                      Note: IP_ConvertUByteRow is intended for extracting
 *                      pixel data from an image, usually in order to transfer
 *                      the pixels to some display device. Do not attempt to
 *                      write to the pixels in an image using the "data"
 *                      pointers obtained from IP_ConvertUByteRow!
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void IP_ConvertUByteRow (IP_ImageId img, Int32 y, UInt8 *buf[], UInt8 *data[])
{
    Int32       chn;
    UInt8       *ubdest, *ubstop;

    for (chn = 0; chn < FF_NumImgChanTypes; ++chn) {
        if (!buf[chn])
            continue;
        if (y < 0 ||
            y >= img->pimg.desc.height ||
            img->pimg.channel[chn] == FF_ImgFmtTypeNone) {
            ubdest = buf[chn];
            ubstop = ubdest + img->pimg.desc.width;
            while (ubdest < ubstop)
                *(ubdest++) = 0;
            data[chn] = buf[chn];
        } else if (img->pimg.channel[chn] == FF_ImgFmtTypeUByte) {
            data[chn] = FF_ImgUByteRow (&img->pimg, chn, y);
        } else if (img->pimg.channel[chn] == FF_ImgFmtTypeFloat) {
            IP_CopyFloat2UByte (img->pimg.desc.width,
                 FF_ImgFloatRow (&img->pimg, chn, y), buf[chn]);
            data[chn] = buf[chn];
        } else {
            UT_ErrFatal ("IP_ConvertUByteRow", "invalid pixel format");
        }
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           IP_GetSample
 *
 *      Usage:          Sample the value of a single data channel in
 *                      an image.
 *
 *      Synopsis:       Float32 IP_GetSample
 *                              (IP_ImageId img,
 *                               FF_ImgChanType chn, 
 *                               Float32 x,
 *                               Float32 y)
 *
 *      Description:    Return the value of channel "chn" in image "img" at
 *                      position (x, y), converted to FF_ImgFmtTypeFloat format.
 *                      If (x, y) does not coincide with a pixel's center, the
 *                      value at position (x, y) is found by bilinear 
 *                      interpolation from the four nearest pixel centers.
 *
 *      Return value:   The value of channel "chn" in image "img" at position
 *                      (x, y), converted to FF_ImgFmtTypeFloat format.
 *                      If channel "chn" is of type FF_ImgFmtTypeNone, the
 *                      return value is always 0.0.
 *
 *      See also:
 *
 ***************************************************************************/

Float32 IP_GetSample (IP_ImageId img, FF_ImgChanType chn, Float32 x, Float32 y)
{
    Int32       x0, x1, y0, y1;
    Float32     LL = 0.0, LR = 0.0, UL = 0.0, UR = 0.0;
    Float32     s, t;

    if (x <= 0.0) {
        x0 = x1 = 0;
        s = 0.0;
    } else if (x >= img->pimg.desc.width - 1.0) {
        x0 = x1 = img->pimg.desc.width - 1;
        s = 0.0;
    } else {
        x0 = (Int32)x;
        x1 = x0 + 1;
        s = x1 - x;
    }
    if (y <= 0.0) {
        y0 = y1 = 0;
        t = 0.0;
    } else if (y >= img->pimg.desc.height - 1.0) {
        y0 = y1 = img->pimg.desc.height - 1;
        t = 0.0;
    } else {
        y0 = (Int32)y;
        y1 = y0 + 1;
        t = y1 - y;
    }

    switch (img->pimg.channel[chn]) {
        case FF_ImgFmtTypeNone:
            return 0.0;

        case FF_ImgFmtTypeUByte:
            LL = (1.0 / 255.0) * *FF_ImgUBytePixel (&img->pimg, chn, x0, y0);
            LR = (1.0 / 255.0) * *FF_ImgUBytePixel (&img->pimg, chn, x1, y0);
            UL = (1.0 / 255.0) * *FF_ImgUBytePixel (&img->pimg, chn, x0, y1);
            UR = (1.0 / 255.0) * *FF_ImgUBytePixel (&img->pimg, chn, x1, y1);
            break;

        case FF_ImgFmtTypeFloat:
            LL = *FF_ImgFloatPixel (&img->pimg, chn, x0, y0);
            LR = *FF_ImgFloatPixel (&img->pimg, chn, x1, y0);
            UL = *FF_ImgFloatPixel (&img->pimg, chn, x0, y1);
            UR = *FF_ImgFloatPixel (&img->pimg, chn, x1, y1);
            break;

        default:
            UT_ErrFatal ("IP_GetSample", "invalid pixel format");
    }

    return t * (s * LL + (1.0-s) * LR) + (1.0-t) * (s * UL + (1.0-s) * UR);
}

/***************************************************************************
 *[@e
 *      Name:           IP_GetUByteRow
 *
 *      Usage:          Return the address of a UByte image row.
 *
 *      Synopsis:       UInt8 *IP_GetUByteRow 
 *                              (IP_ImageId img, FF_ImgChanType chn, Int32 y)
 *
 *      Description:    Return the address of a UByte image row.
 *
 *      Return value:   The UByte row address.
 *
 *      See also:       IP_GetFloatRow
 *
 ***************************************************************************/

UInt8 *IP_GetUByteRow (IP_ImageId img, FF_ImgChanType chn, Int32 y) {
    return FF_ImgUByteRow (&img->pimg, chn, y);
}

/***************************************************************************
 *[@e
 *      Name:           IP_GetFloatRow
 *
 *      Usage:          Return the address of a Float image row.
 *
 *      Synopsis:       Float32 *IP_GetFloatRow 
 *                              (IP_ImageId img, FF_ImgChanType chn, Int32 y)
 *
 *      Description:    Return the address of a Float image row.
 *
 *      Return value:   The Float row address.
 *
 *      See also:       IP_GetUByteRow
 *
 ***************************************************************************/

Float32 *IP_GetFloatRow (IP_ImageId img, FF_ImgChanType chn, Int32 y) {
    return FF_ImgFloatRow (&img->pimg, chn, y);
}
