/**************************************************************************
 *{@C
 *      Copyright:      1988-2020 Paul Obermeier (obermeier@poSoft.de)
 *
 *      Module:         ImageProcessing
 *      Filename:       IP_Flip.c
 *
 *      Author:         Paul Obermeier
 *
 *      Description:    Functions to flip images in various directions.
 *
 *      Additional documentation:
 *                      None.
 *
 *      Exported functions:
 *                      IP_FlipDiagonal
 *                      IP_FlipHorizontal
 *                      IP_FlipVertical
 *                      IP_Rotate
 *
 **************************************************************************/

#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"

/***************************************************************************
 *[@e
 *      Name:           IP_FlipDiagonal
 *
 *      Usage:          Flip an image diagonally.
 *
 *      Synopsis:       void IP_FlipDiagonal (IP_ImageId img)
 *
 *      Description:    Image "img" is mirrored about the (x == y) axis, i.e.
 *                      pixel rows change into columns and columns change into
 *                      rows.
 *                      If width and height of "img" in pixels are not equal,
 *                      the original and the mirror image do not cover each
 *                      other completely, and pixels in the original image for
 *                      which no couterpart exists in the mirror image are not
 *                      changed by IP_FlipDiagonal.
 *
 *      Return value:   None.
 *
 *      See also:       IP_FlipHorizontal
 *                      IP_FlipVertical
 *
 ***************************************************************************/

void IP_FlipDiagonal (IP_ImageId img)
{
    Int32       i, j, stop, chn;
    UInt8       *ub1, *ub2;
    Float32     *f1, *f2;

    stop = UT_MIN (img->pimg.desc.width, img->pimg.desc.height);
    for (chn = 0; chn < FF_NumImgChanTypes; ++chn) {
        switch (img->pimg.channel[chn]) {
            case FF_ImgFmtTypeNone: {
                break;
            }
            case FF_ImgFmtTypeUByte: {
                for (i = 0; i < stop; ++i) {
                    for (j = i + 1; j < stop; ++j) {
                        ub1 = FF_ImgUBytePixel (&img->pimg, chn, i, j);
                        ub2 = FF_ImgUBytePixel (&img->pimg, chn, j, i);
                        UT_SWAP (*ub1, *ub2, UInt8);
                    }
                }
                break;
            }
            case FF_ImgFmtTypeFloat: {
                for (i = 0; i < stop; ++i) {
                    for (j = i + 1; j < stop; ++j) {
                        f1 = FF_ImgFloatPixel (&img->pimg, chn, i, j);
                        f2 = FF_ImgFloatPixel (&img->pimg, chn, j, i);
                        UT_SWAP (*f1, *f2, Float32);
                    }
                }
                break;
            }
        }
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           IP_FlipVertical
 *
 *      Usage:          Flip an image around the vertical mid axis.
 *
 *      Synopsis:       void IP_FlipVertical (IP_ImageId img)
 *
 *      Description:    Flip an image around the vertical (y) axis.
 *      
 *      Return value:   None.
 *
 *      See also:       IP_FlipDiagonal
 *                      IP_FlipHorizontal
 *
 ***************************************************************************/

void IP_FlipVertical (IP_ImageId img)
{
    Int32       y, chn;
    UInt8       *ub1, *ub2;
    Float32     *f1, *f2;

    for (y = 0; y < img->pimg.desc.height; ++y) {
        for (chn = 0; chn < FF_NumImgChanTypes; ++chn) {
            switch (img->pimg.channel[chn]) {
                case FF_ImgFmtTypeNone: {
                    break;
                }
                case FF_ImgFmtTypeUByte: {
                    ub1 = FF_ImgUByteRow (&img->pimg, chn, y);
                    ub2 = ub1 + img->pimg.desc.width - 1;
                    while (ub1 < ub2) {
                        UT_SWAP (*ub1, *ub2, UInt8);
                        ++ub1;
                        --ub2;
                    }
                    break;
                }
                case FF_ImgFmtTypeFloat: {
                    f1 = FF_ImgFloatRow (&img->pimg, chn, y);
                    f2 = f1 + img->pimg.desc.width - 1;
                    while (f1 < f2) {
                        UT_SWAP (*f1, *f2, Float32);
                        ++f1;
                        --f2;
                    }
                    break;
                }
            }
        }
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           IP_FlipHorizontal
 *
 *      Usage:          Flip an image around the horizontal mid axis.
 *
 *      Synopsis:       void IP_FlipHorizontal (IP_ImageId img)
 *
 *      Description:    Flip an image around the horizontal (x) axis.
 *
 *      Return value:   None.
 *
 *      See also:       IP_FlipDiagonal
 *                      IP_FlipVertical
 *
 ***************************************************************************/

void IP_FlipHorizontal (IP_ImageId img)
{
    Int32       y1, y2, chn;
    UInt8       *ub1, *ub2, *ubstop;
    Float32     *f1, *f2, *fstop;

    y1 = 0;
    y2 = img->pimg.desc.height - 1;
    while (y1 < y2) {
        for (chn = 0; chn < FF_NumImgChanTypes; ++chn) {
            switch (img->pimg.channel[chn]) {
                case FF_ImgFmtTypeNone: {
                    break;
                }
                case FF_ImgFmtTypeUByte: {
                    ub1 = FF_ImgUByteRow (&img->pimg, chn, y1);
                    ub2 = FF_ImgUByteRow (&img->pimg, chn, y2);
                    ubstop = ub1 + img->pimg.desc.width;
                    while (ub1 < ubstop)
                    {
                        UT_SWAP (*ub1, *ub2, UInt8);
                        ++ub1;
                        ++ub2;
                    }
                    break;
                }
                case FF_ImgFmtTypeFloat: {
                    f1 = FF_ImgFloatRow (&img->pimg, chn, y1);
                    f2 = FF_ImgFloatRow (&img->pimg, chn, y2);
                    fstop = f1 + img->pimg.desc.width;
                    while (f1 < fstop)
                    {
                        UT_SWAP (*f1, *f2, Float32);
                        ++f1;
                        ++f2;
                    }
                    break;
                }
            }
        }
        ++y1;
        --y2;
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           IP_Rotate
 *
 *      Usage:          Rotate an image by -90, 90, 180 or 270 degrees.
 *
 *      Synopsis:       void IP_Rotate (IP_ImageId srcImg, 
 *                                      IP_ImageId destImg, Int32 angle)
 *
 *      Description:    Rotate an image by -90, 90, 180 or 270 degrees.
 *
 *      Return value:   UT_True, if a valid angle was specified. Otherwise
 *                      UT_False.
 *
 *      See also:       IP_WarpFunct
 *                      IP_FlipHorizontal
 *
 ***************************************************************************/

UT_Bool IP_Rotate (IP_ImageId srcImg, IP_ImageId destImg, Int32 angle)
{
    Int32   x, x0, y, dx, dy, y1, y2, chn;
    UInt8   *ub, *ubstop;
    Float32 *f1, *fstop;

    if (angle != 90 && angle != 180 && angle != 270 && angle != -90) {
        printf ("IP_Rotate: %d is not a valid angle\n", angle);
        return UT_False;
    }

    if (angle == 180) {
        IP_CopyImage (srcImg, destImg);
        IP_FlipHorizontal (destImg);
        IP_FlipVertical   (destImg);
        return UT_True;
    }
    y1 = 0;
    y2 = srcImg->pimg.desc.height;
    if (angle == -90 || angle == 270) {
        y  = 0;
        dx = -1;
        dy = 1;
    } else {
        y  = y2-1;
        dx = 1;
        dy = -1;
    }
    if (angle == -90 || angle == 270) {
        x0 = srcImg->pimg.desc.width - 1;
    } else {
        x0 = 0;
    }
    while (y1 < y2) {
        for (chn = 0; chn < FF_NumImgChanTypes; ++chn) {
            switch (srcImg->pimg.channel[chn]) {
                case FF_ImgFmtTypeNone: {
                    break;
                }
                case FF_ImgFmtTypeUByte: {
                    ub = FF_ImgUByteRow (&srcImg->pimg, chn, y1);
                    ubstop = ub + srcImg->pimg.desc.width;
                    x = x0;
                    while (ub < ubstop) {
                        *FF_ImgUBytePixel (&destImg->pimg, chn, y, x) = *ub;
                        ++ub;
                        x += dx;
                    }
                    break;
                }
                case FF_ImgFmtTypeFloat: {
                    f1 = FF_ImgFloatRow (&srcImg->pimg, chn, y1);
                    fstop = f1 + srcImg->pimg.desc.width;
                    x = x0;
                    while (f1 < fstop) {
                        *FF_ImgFloatPixel (&destImg->pimg, chn, y, x) = *f1;
                        ++f1;
                        x += dx;
                    }
                    break;
                }
            }
        }
        y += dy;
        ++y1;
    }
    return UT_True;
}
