/**************************************************************************
 *{@C
 *      Copyright:      1988-2024 Paul Obermeier (obermeier@poSoft.de)
 *
 *      Module:         ImageProcessing
 *      Filename:       IP_Interlace.c
 *
 *      Author:         Paul Obermeier
 *
 *      Description:    Functions to interlace two video fields into one
 *                      full frame and to divide interlaced frame into
 *                      two fields.
 *
 *      Additional documentation:
 *                      None.
 *
 *      Exported functions:
 *                      IP_Interlace
 *                      IP_Unlace
 *
 **************************************************************************/

#include <stdio.h>

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

#define USE_IMG_MACROS
#include "FF_Image.h"

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

/* Interpolate between scanlines "s1" and "s2" and write the result
   to scanline "dest". "Width" gives the length of the scanlines. */

static void interp_UByte (Int32 width, UInt8 *s1, UInt8 *s2, UInt8 *dest)
{
    UInt8 *pd, *stop;

    pd = dest;
    stop = pd + width;

    while (pd < stop)
    {
        *pd = (UInt8)(((Int32)*s1 + (Int32)*s2) >> 1);
        s1++;
        s2++;
        pd++;
    }
    return;
}

static void interp_Float (Int32 width, Float32 *s1, Float32 *s2, Float32 *dest)
{
    Float32 *pd, *stop;

    pd = dest;
    stop = pd + width;

    while (pd < stop)
    {
        *pd = (*s1 + *s2) * 0.5;
        s1++;
        s2++;
        pd++;
    }
    return;
}
/***************************************************************************
 *[@e
 *      Name:           IP_Interlace
 *
 *      Usage:          Interlace two video fields into one frame.
 *
 *      Synopsis:       UT_Bool IP_Interlace
 *                              (IP_ImageId fld1,
 *                              IP_ImageId fld2,
 *                              IP_ImageId frm,
 *                              UT_Bool skip)
 *
 *      Description:    Images "fld1" and "fld2" are taken as the first and
 *                      second field of an interlaced video image. The images
 *                      are combined into a full video frame. The result is
 *                      stored in image "frm".
 *
 *                      If the "skip" flag is UT_True, only every second scan line
 *                      for "fld1" and "fld2" is used to construct the
 *                      interlaced frame.
 *                      If the "skip" flag is UT_False, all scan lines from "fld1"
 *                      and "fld2" are used.
 *
 *                      Notes:
 *
 *                      - For interlacing, "fld1", "fld2" and "frm" are aligned
 *                        at the upper left corner. The top scan line in "frm"
 *                        is always taken from image "fld1".
 *
 *                      - IP_Interlace does not write to any channels in "frm" for
 *                        which the current drawing mask is set to UT_False.
 *
 *      Return value:   UT_True if successful, else UT_False.
 *
 *      See also:       IP_Unlace
 *
 ***************************************************************************/

UT_Bool IP_Interlace (IP_ImageId fld1, IP_ImageId fld2, IP_ImageId frm,
                      UT_Bool skip)
{
    Int32       chn, y, yfld, yfrm, width, height;
    IP_ImageId  fld;

    width = frm->pimg.desc.width;
    width = UT_MIN (width, fld1->pimg.desc.width);
    width = UT_MIN (width, fld2->pimg.desc.width);
    if (skip) {
        height = frm->pimg.desc.height;
        height = UT_MIN (height, fld1->pimg.desc.height);
        height = UT_MIN (height, fld2->pimg.desc.height);
        for (y = 0; y < height; ++y) {
            yfrm = frm->pimg.desc.height - 1 - y;
            if (y & 1) {
                fld = fld2;
            } else {
                fld = fld1;
            }
            yfld = fld->pimg.desc.height - 1 - y;
            for (chn = 0; chn < FF_NumImgChanTypes; ++chn) {
                if (IP_StateDrawMask[chn] &&
                    frm->pimg.channel[chn] &&
                    fld->pimg.channel[chn]) {
                    switch (frm->pimg.channel[chn]) {
                        case FF_ImgFmtTypeUByte: {
                            switch (fld->pimg.channel[chn]) {
                                case FF_ImgFmtTypeUByte: {
                                    IP_CopyUByte2UByte
                                        (width,
                                         FF_ImgUByteRow (&fld->pimg, chn, yfld),
                                         FF_ImgUByteRow (&frm->pimg, chn, yfrm));
                                    break;
                                }
                                case FF_ImgFmtTypeFloat: {
                                    IP_CopyFloat2UByte
                                        (width,
                                         FF_ImgFloatRow (&fld->pimg, chn, yfld),
                                         FF_ImgUByteRow (&frm->pimg, chn, yfrm));
                                    break;
                                }
                                default: {
                                    UT_ErrFatal ("IP_Interlace", "invalid channel type");
                                }
                            }
                            break;
                        }
                        case FF_ImgFmtTypeFloat: {
                            switch (fld->pimg.channel[chn]) {
                                case FF_ImgFmtTypeUByte: {
                                    IP_CopyUByte2Float
                                        (width,
                                         FF_ImgUByteRow (&fld->pimg, chn, yfld),
                                         FF_ImgFloatRow (&frm->pimg, chn, yfrm));
                                    break;
                                }
                                case FF_ImgFmtTypeFloat: {
                                    IP_CopyFloat2Float
                                        (width,
                                         FF_ImgFloatRow (&fld->pimg, chn, yfld),
                                         FF_ImgFloatRow (&frm->pimg, chn, yfrm));
                                    break;
                                }
                                default: {
                                    UT_ErrFatal ("IP_Interlace", "invalid channel type");
                                }
                            }
                            break;
                        }
                        default: {
                            UT_ErrFatal ("IP_Interlace", "invalid channel type");
                        }
                    }
                }
            }
        }
    } else {
        height = frm->pimg.desc.height;
        height = UT_MIN (height, (fld1->pimg.desc.height << 1));
        height = UT_MIN (height, (fld2->pimg.desc.height << 1));
        for (y = 0; y < height; ++y) {
            yfrm = frm->pimg.desc.height - 1 - y;
            if (y & 1) {
                fld = fld2;
            } else {
                fld = fld1;
            }
            yfld = fld->pimg.desc.height - 1 - (y >> 1);
            for (chn = 0; chn < FF_NumImgChanTypes; ++chn) {
                if (IP_StateDrawMask[chn] &&
                    frm->pimg.channel[chn] &&
                    fld->pimg.channel[chn]) {
                    switch (frm->pimg.channel[chn]) {
                        case FF_ImgFmtTypeUByte: {
                            switch (fld->pimg.channel[chn]) {
                                case FF_ImgFmtTypeUByte: {
                                    IP_CopyUByte2UByte
                                        (width,
                                         FF_ImgUByteRow (&fld->pimg, chn, yfld),
                                         FF_ImgUByteRow (&frm->pimg, chn, yfrm));
                                    break;
                                }
                                case FF_ImgFmtTypeFloat: {
                                    IP_CopyFloat2UByte
                                        (width,
                                         FF_ImgFloatRow (&fld->pimg, chn, yfld),
                                         FF_ImgUByteRow (&frm->pimg, chn, yfrm));
                                    break;
                                }
                                default: {
                                    UT_ErrFatal ("IP_Interlace", "invalid channel type");
                                }
                            }
                            break;
                        }
                        case FF_ImgFmtTypeFloat: {
                            switch (fld->pimg.channel[chn]) {
                                case FF_ImgFmtTypeUByte: {
                                    IP_CopyUByte2Float
                                        (width,
                                         FF_ImgUByteRow (&fld->pimg, chn, yfld),
                                         FF_ImgFloatRow (&frm->pimg, chn, yfrm));
                                    break;
                                }
                                case FF_ImgFmtTypeFloat: {
                                    IP_CopyFloat2Float
                                        (width,
                                         FF_ImgFloatRow (&fld->pimg, chn, yfld),
                                         FF_ImgFloatRow (&frm->pimg, chn, yfrm));
                                    break;
                                }
                                default: {
                                    UT_ErrFatal ("IP_Interlace", "invalid channel type");
                                }
                            }
                            break;
                        }
                        default: {
                            UT_ErrFatal ("IP_Interlace", "invalid channel type");
                        }
                    }
                }
            }
        }
    }
    return UT_True;
}

/***************************************************************************
 *[@e
 *      Name:           IP_Unlace
 *
 *      Usage:          Extract one video field from an interlaced frame.
 *
 *      Synopsis:       UT_Bool IP_Unlace (IP_ImageId frm, IP_ImageId fld, 
 *                                       UT_Bool odd, IP_UnlaceModeType mode)
 *
 *      Description:    Every second scan line from image "frm" is copied into
 *                      image "fld". Depending on whether the "odd" flag is set
 *                      to "UT_True" or "UT_False", the odd-numbered or the even-
 *                      numbered scan lines are copied.
 *
 *                      "Mode" specifies the field interpolation mode.
 *                      It can have the following values:
 *                          IP_UnlaceModeNone: 
 *                              The scan lines of the selected field are copied
 *                              without any modification. Image "fld" should 
 *                              have half the height of image "frm".
 *                          IP_UnlaceModeCopy:
 *                              Each scan line of the selected field is copied
 *                              twice. Image "fld" should have the same height
 *                              as image "frm".
 *                          IP_UnlaceModeInterpolate:
 *                              Each scanline of the selected field is copied
 *                              into "fld", which should have the same height
 *                              as image "frm". The scanlines of the other
 *                              field are computed by interpolating between 
 *                              the upper and lower scanlines of the selected
 *                              field.
 *
 *                      Notes:
 *
 *                      - For field extraction, "fld" and "frm" are aligned at
 *                        the upper left corner. The top scan line in "frm" is
 *                        always considered even-numbered.
 *
 *                      - IP_Unlace does not write to any channels in "fld"
 *                        for which the current drawing mask is set to UT_False.
 *
 *      Return value:   UT_True if successful, else UT_False.
 *
 *      See also:       IP_Interlace
 *
 ***************************************************************************/

UT_Bool IP_Unlace (IP_ImageId frm, IP_ImageId fld, UT_Bool odd, 
                   IP_UnlaceModeType mode)
{
    Int32 chn, width, yfrm, yfld, yyfld, yfldinc, destinc;
    UT_Bool interp;

    if (mode < 0 || mode >= IP_NumUnlaceModeTypes) {
        UT_ErrSetNum (UT_ErrParamInvalid, str_fipmode);
        return UT_False;
    }

    interp = (mode != IP_UnlaceModeNone);

    width = UT_MIN (frm->pimg.desc.width, fld->pimg.desc.width);
    if (odd && interp)
        yyfld = yfld = fld->pimg.desc.height - 2;
    else
        yyfld = yfld = fld->pimg.desc.height - 1;
    if (odd)
        yfrm = frm->pimg.desc.height - 2;
    else
        yfrm = frm->pimg.desc.height - 1;

    yfldinc = (interp)? 2: 1;

    while (yfrm >= 0 && yfld >= 0) {
        for (chn = 0; chn < FF_NumImgChanTypes; ++chn) {
            if (IP_StateDrawMask[chn]) {
                switch (frm->pimg.channel[chn]) {
                    case FF_ImgFmtTypeNone: {
                        break;
                    }
                    case FF_ImgFmtTypeUByte: {
                        switch (fld->pimg.channel[chn]) {
                            case FF_ImgFmtTypeNone: {
                                break;
                            }
                            case FF_ImgFmtTypeUByte: {
                                IP_CopyUByte2UByte
                                    (width,
                                     FF_ImgUByteRow (&frm->pimg, chn, yfrm),
                                     FF_ImgUByteRow (&fld->pimg, chn, yfld));
                                break;
                            }
                            case FF_ImgFmtTypeFloat: {
                                IP_CopyUByte2Float
                                    (width,
                                     FF_ImgUByteRow (&frm->pimg, chn, yfrm),
                                     FF_ImgFloatRow (&fld->pimg, chn, yfld));
                                break;
                            }
                            default: {
                                UT_ErrFatal ("IP_Unlace", "invalid channel type");
                            }
                        }
                        break;
                    }
                    case FF_ImgFmtTypeFloat: {
                        switch (fld->pimg.channel[chn]) {
                            case FF_ImgFmtTypeNone: {
                                break;
                            }
                            case FF_ImgFmtTypeUByte: {
                                IP_CopyFloat2UByte
                                    (width,
                                     FF_ImgFloatRow (&frm->pimg, chn, yfrm),
                                     FF_ImgUByteRow (&fld->pimg, chn, yfld));
                                break;
                            }
                            case FF_ImgFmtTypeFloat: {
                                IP_CopyFloat2Float
                                    (width,
                                     FF_ImgFloatRow (&frm->pimg, chn, yfrm),
                                     FF_ImgFloatRow (&fld->pimg, chn, yfld));
                                break;
                            }
                            default: {
                                UT_ErrFatal ("IP_Unlace", "invalid channel type");
                            }

                        }
                        break;
                    }
                    default: {
                        UT_ErrFatal ("IP_Unlace", "invalid channel type");
                    }
                }
            }
        }
        yfrm -= 2;
        yfld -= yfldinc;
    }

    /* If only a field should be extracted without 
       any interpolation, we're done. */
    if (!interp) {
        return UT_True;
    }

    yfld = yyfld;
    destinc = odd? 1: -1;

    while (yfld >= 0) {
        for (chn = 0; chn < FF_NumImgChanTypes; ++chn) {
            if (!IP_StateDrawMask[chn]) {
                continue;
            }
            switch (fld->pimg.channel[chn]) {
                case FF_ImgFmtTypeNone: {
                    break;
                }
                case FF_ImgFmtTypeUByte: {
                    switch (mode) {
                        case IP_UnlaceModeInterpolate: {
                            /* If the extracted field is the odd one and the 
                               current scanline is the top most, or the 
                               extracted field is the even one and the current
                               scanline is the lowest, we fall through to 
                               copy mode. */
                            if (!((odd && yfld == yyfld) ||
                                (!odd && (yfld - 2 < 0)))) {
                                interp_UByte (width, 
                                  FF_ImgUByteRow (&fld->pimg, chn, yfld + 2*destinc),
                                  FF_ImgUByteRow (&fld->pimg, chn, yfld),
                                  FF_ImgUByteRow (&fld->pimg, chn, yfld + destinc));
                                break;
                            }
                        }
                        case IP_UnlaceModeCopy: {
                            IP_CopyUByte2UByte
                                (width,
                                 FF_ImgUByteRow (&fld->pimg, chn, yfld),
                                 FF_ImgUByteRow (&fld->pimg, chn, yfld + destinc));
                            break;
                        }
                        default: {
                            UT_ErrFatal ("IP_Unlace", "unknown unlace mode");
                        }
                    }
                    break;
                }
                case FF_ImgFmtTypeFloat: {
                    switch (mode) {
                        case IP_UnlaceModeInterpolate: {
                            /* See comment above. */
                            if (!((odd && yfld == yyfld) || 
                                (!odd && (yfld - 2 < 0)))) {
                                interp_Float (width,
                                  FF_ImgFloatRow (&fld->pimg, chn, yfld + 2*destinc),
                                  FF_ImgFloatRow (&fld->pimg, chn, yfld),
                                  FF_ImgFloatRow (&fld->pimg, chn, yfld + destinc));
                                break;
                            }
                        }
                        case IP_UnlaceModeCopy: {
                            IP_CopyFloat2Float
                                (width,
                                 FF_ImgFloatRow (&fld->pimg, chn, yfld),
                                 FF_ImgFloatRow (&fld->pimg, chn, yfld + destinc));
                            break;
                        }
                        default: {
                            UT_ErrFatal ("IP_Unlace", "unknown unlace mode");
                        }
                    }
                }
                break;
            }
        }
        yfld -= 2;
    }
    return UT_True;
}
