//================================================================================
// lib2D image library
//
// Image header file
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Jun-22 18:33:05
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================================
#ifndef _EImageOps_h
#define _EImageOps_h

#include <stdio.h>

#ifndef _EClass_h
#include <EClass.h>
#endif

#ifndef _EImage_h
#include <Image/Image.h>
#endif

#include <EUndo.h>


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
extern "C" {
#endif


//================================================================
// Perform 2D convolution with special handling at the edges
//================================================================
#define Ep_ConvolveWithBorders(pImageSizeX, pImageSizeY,  pKernel,  pKernelSizeX, pKernelSizeY,  pTmpVals, pAddFunc, pShouldProcessPixel, pSetPixelFunc, pMax) do\
{\
	const float*  lKernel = pKernel;\
	const EIndex  lKernelSizeXH = (pKernelSizeX) >> 1, lKernelSizeYH = (pKernelSizeY) >> 1;\
	const EIndex  lSizeX = pImageSizeX, lSizeY = pImageSizeY;\
	const EIndex  lMaxX = lSizeX - lKernelSizeXH, lMaxY = lSizeY - lKernelSizeYH;\
\
/*--------------------------------------------------------*/\
/* Convolve Image 'interior' (no kernel mirroring)        */\
/*--------------------------------------------------------*/\
	{\
		EIndex  lY = lKernelSizeYH, lRowOffset = lY * lSizeX;for(;lY < lMaxY;lY++, lRowOffset += lSizeX)\
		{\
			EIndex  lX = lKernelSizeXH;for(;lX < lMaxX;lX++)\
			{\
				const EIndex  lPixelOffset = lX + lRowOffset;if(pShouldProcessPixel(lPixelOffset))\
				{\
					pTmpVals;\
\
					EIndex  lKY = -lKernelSizeYH, lKC = 0;for(;lKY <= lKernelSizeYH;lKY++)\
					{\
						EIndex  lKX = -lKernelSizeXH;for(;lKX <= lKernelSizeXH;lKX++, lKC++)\
						{\
							const float  lK = lKernel[lKC];pAddFunc(lPixelOffset + lKX + lKY * lSizeX, lK);\
						}\
					}\
					pSetPixelFunc(lPixelOffset, pMax);\
				}\
				else LDstPixels[lPixelOffset] = LSrcPixels[lPixelOffset];\
			}\
		}\
	}\
\
/*--------------------------------------------------------*/\
/* Convolve Image 'border' (kernel mirroring)             */\
/*--------------------------------------------------------*/\
/* Top edge  */\
\
	{\
		EIndex  lY = 0, lRowOffset = lY * lSizeX;for(;lY < lKernelSizeYH;lY++, lRowOffset += lSizeX)\
		{\
			EIndex  lX = 0;for(;lX < lSizeX;lX++)\
			{\
				const EIndex  lPixelOffset = lX + lRowOffset;if(pShouldProcessPixel(lPixelOffset))\
				{\
					pTmpVals;\
\
					EIndex  lKY = -lKernelSizeYH, lKC = 0;for(;lKY <= lKernelSizeYH;lKY++)\
					{\
						EIndex  lKX = -lKernelSizeXH;for(;lKX <= lKernelSizeXH;lKX++, lKC++)\
						{\
							EIndex  LXN = lX + lKX, LYN = lY + lKY;\
							if(LXN < 0) LXN *= -1;else if(LXN >= lSizeX) LXN = lSizeX - LXN + lSizeX - 2;\
							if(LYN < 0) LYN *= -1;else if(LYN >= lSizeY) LYN = lSizeY - LYN + lSizeY - 2;\
							const float  lK = lKernel[lKC];pAddFunc(LXN + LYN * lSizeX, lK);\
						}\
					}\
					pSetPixelFunc(lPixelOffset, pMax);\
				}\
				else LDstPixels[lPixelOffset] = LSrcPixels[lPixelOffset];\
			}\
		}\
	}\
\
\
/* Bottom edge  */\
\
	{\
		EIndex  lY = lSizeY - lKernelSizeYH, lRowOffset = lY * lSizeX;for(;lY < lSizeY;lY++, lRowOffset += lSizeX)\
		{\
			EIndex  lX = 0;for(;lX < lSizeX;lX++)\
			{\
				const EIndex  lPixelOffset = lX + lRowOffset;if(pShouldProcessPixel(lPixelOffset))\
				{\
					pTmpVals;\
\
					EIndex  lKY = -lKernelSizeYH, lKC = 0;for(;lKY <= lKernelSizeYH;lKY++)\
					{\
						EIndex  lKX = -lKernelSizeXH;for(;lKX <= lKernelSizeXH;lKX++, lKC++)\
						{\
							EIndex  LXN = lX + lKX, LYN = lY + lKY;\
							if(LXN < 0) LXN *= -1;else if(LXN >= lSizeX) LXN = lSizeX - LXN + lSizeX - 2;\
							if(LYN < 0) LYN *= -1;else if(LYN >= lSizeY) LYN = lSizeY - LYN + lSizeY - 2;\
							const float  lK = lKernel[lKC];pAddFunc(LXN + LYN * lSizeX, lK);\
						}\
					}\
					pSetPixelFunc(lPixelOffset, pMax);\
				}\
				else LDstPixels[lPixelOffset] = LSrcPixels[lPixelOffset];\
			}\
		}\
	}\
\
\
/* Left edge  */\
\
	{\
		EIndex  lY = lKernelSizeYH, lRowOffset = lY * lSizeX;for(;lY < lMaxY;lY++, lRowOffset += lSizeX)\
		{\
			EIndex  lX = 0;for(;lX < lKernelSizeXH;lX++)\
			{\
				const EIndex  lPixelOffset = lX + lRowOffset;if(pShouldProcessPixel(lPixelOffset))\
				{\
					pTmpVals;\
\
					EIndex  lKY = -lKernelSizeYH, lKC = 0;for(;lKY <= lKernelSizeYH;lKY++)\
					{\
						EIndex  lKX = -lKernelSizeXH;for(;lKX <= lKernelSizeXH;lKX++, lKC++)\
						{\
							EIndex  LXN = lX + lKX, LYN = lY + lKY;\
							if(LXN < 0) LXN *= -1;else if(LXN >= lSizeX) LXN = lSizeX - LXN + lSizeX - 2;\
							if(LYN < 0) LYN *= -1;else if(LYN >= lSizeY) LYN = lSizeY - LYN + lSizeY - 2;\
							const float  lK = lKernel[lKC];pAddFunc(LXN + LYN * lSizeX, lK);\
						}\
					}\
					pSetPixelFunc(lPixelOffset, pMax);\
				}\
				else LDstPixels[lPixelOffset] = LSrcPixels[lPixelOffset];\
			}\
		}\
	}\
\
\
/* Right edge  */\
\
	{\
		EIndex  lY = lKernelSizeYH, lRowOffset = lY * lSizeX;for(;lY < lMaxY;lY++, lRowOffset += lSizeX)\
		{\
			EIndex  lX = lSizeX - lKernelSizeXH;for(;lX < lSizeX;lX++)\
			{\
				const EIndex  lPixelOffset = lX + lRowOffset;if(pShouldProcessPixel(lPixelOffset))\
				{\
					pTmpVals;\
\
					EIndex  lKY = -lKernelSizeYH, lKC = 0;for(;lKY <= lKernelSizeYH;lKY++)\
					{\
						EIndex  lKX = -lKernelSizeXH;for(;lKX <= lKernelSizeXH;lKX++, lKC++)\
						{\
							EIndex  LXN = lX + lKX, LYN = lY + lKY;\
							if(LXN < 0) LXN *= -1;else if(LXN >= lSizeX) LXN = lSizeX - LXN + lSizeX - 2;\
							if(LYN < 0) LYN *= -1;else if(LYN >= lSizeY) LYN = lSizeY - LYN + lSizeY - 2;\
							const float  lK = lKernel[lKC];pAddFunc(LXN + LYN * lSizeX, lK);\
						}\
					}\
					pSetPixelFunc(lPixelOffset, pMax);\
				}\
				else LDstPixels[lPixelOffset] = LSrcPixels[lPixelOffset];\
			}\
		}\
	}\
} while(0)


//================================================
// Function prototypes
//================================================

//--------------------------------
// ImageOps.c
//--------------------------------
extern EOperatorClass*  Ep_OperatorClassByName(const char* PName);
extern EOperatorClass*  Ep_OperatorClassRegister(EOperatorClass* PClassDescriptor);
extern void             Ep_OperatorClassDeactivate(EOperatorClass* PClass);
extern void             Ep_OperatorClassesFree(void);

extern void             Ep_OpsFree(void);

extern int      EpImage_FlipH(EpImage* PSImage, EpImage* PDImage);
extern int      EpImage_FlipV(EpImage* PSImage, EpImage* PDImage);

extern int      EpImage_Crop(const EpImage* PSImage, EpImage* PDImage, const EIndex PLeft, const EIndex PRight, const EIndex PTop, const EIndex PBottom,  const int PPixelType, const int PComponentsPerPixel, const int PPixelComponentType,  const EcRGBAiColor* PMarginColor);

#define EpPixelStatRANGES               0x0001
#define EpPixelStatMEANS                0x0002
#define EpPixelStatMEANS_ALPHA_MASKED   0x0004

extern int      EpImage_PixelStatsAdv(const EpImage* PImage, const EpPixelBuffer* PPixelBuffer,  EpPixelStats* PStats, const uint32_t PFlags);
extern int      EpImage_Intensity(EpImage* PSImage, const short PMeteringMode);

extern int      EpImage_LumaRange(const EpImage* PImage, int* PLumaMinRet, int* PLumaMaxRet);
extern int      EpImage_LumaRangef(const EpImage* PImage, float* PLumaMin, float* PLumaMax);

extern void     EpImage_LumaHistogram(EpImage* PImage, int* PLumaHistogram);
extern void     EpImage_Histograms(const EpImage* PImage, const EpPixelBuffer* PPixelBuffer,  void* PMin, void* PMax,  int** PHistograms, const EIndex PNBins, const int PNChannels);


extern int      EpImage_HistogramEqualize(EpImage* PSImage, EpImage* PDImage, const float PStrength, const float PIntensity);

extern int      EpImage_ColorSaturation(const EpImage* PSImage, EpImage* PDImage, const float PSaturation);

extern int      EpImage_Invert(EpImage* PSImage, EpImage* PDImage, const int PChannels);
extern int      EpImage_RotateLeft90(EpImage* PSImage, EpImage* PDImage);
extern int      EpImage_RotateRight90(EpImage* PSImage, EpImage* PDImage);


extern int      EpImage_ColorDither(EpImage* PImage, EpImage* PImageD,  const int PRLevels, const int PGLevels, const int PBLevels,  const int PMethod, const EBool PInteractive);

extern EBool    EpImage_ChromaKey(EpImage* PSImage, EpImage* PDImage, const EcRGBAiColor* PTransparentColor, const float PThreshold, const float PThreshold2, const int PSamplingKernelRadius, void (*PShowProgressFunc)(float, void*), void* PShowProgressProcData);
extern EBool    EpImage_ChromaKeyRGBDist(EpImage* PSImage, EpImage* PDImage, const EcRGBAiColor* PTransparentColor, const float PThreshold, const float PThreshold2, const int PSamplingKernelRadius, void (*PShowProgressFunc)(float, void*), void* PShowProgressProcData);


//--------------------------------
// Compositing.c
//--------------------------------
extern int      EpVerbose_Compositing;

extern int      EpImage_Paste(const EpImage* PImageDecal, EpImage* PImageBackground, EpImage* PDImage,  EIndex PSrcX, EIndex PSrcY,  const EIndex PDstX, const EIndex PDstY,  const EIndex PSizeX, EIndex PSizeY);
extern int      EpImage_Composite(const EpImage* PImageDecal, EpImage* PImageBackground, EpImage* PDImage,  EIndex PSrcX, EIndex PSrcY,  EIndex PDstX, EIndex PDstY, EIndex PSizeX, EIndex PSizeY,  const uint16_t PBlendMode, const uint16_t PAlphaSource);

extern int      EpImage_Gray8Paste(const EpImage* PImageDecal, EpImage* PImageBackground,  const int PPixelOperation,  const EIndex PSrcX, const EIndex PSrcY,  const EIndex PDstX, const EIndex PDstY,  const EIndex PSizeX, const EIndex PSizeY);
extern int      EpImage_Gray8PasteRGBAiColor(const EpImage* PImageDecal, EpImage* PImageBackground,  const EcRGBAiColor* PColor,  const EIndex PSrcX, const EIndex PSrcY,  const EIndex PDstX, const EIndex PDstY,  const EIndex PSizeX, const EIndex PSizeY,  const int PBlendMode);
extern int      EpImage_Gray8PasteClipRGBAiColor(const EpImage* PImageDecal, EpImage* PImageBackground, EpImage* PDImage,  const EcRGBAiColor* PColor,  const EIndex PSrcX, const EIndex PSrcY,  const EIndex PDstX, const EIndex PDstY,  const EIndex PSizeX, const EIndex PSizeY,
                 const EIndex PClipMinX, const EIndex PClipMinY,  const EIndex PClipSizeX, const EIndex PClipSizeY,  const uint16_t PBlendMode, const uint16_t PAlphaOp);

extern int      EpImage_BitmapPasteRGBAiColor(const EpImage* PImageDecal, const uint8_t* PBitmap, const int PSrcBytesPerLine, EpImage* PImageBackground,  const EcRGBAiColor* PColor,  const EIndex PSrcX, const EIndex PSrcY,  const EIndex PDstX, const EIndex PDstY,  const EIndex PSizeX, const EIndex PSizeY);
extern int      EpImage_BitmapPasteClipRGBAiColor(const EpImage* PImageDecal, const uint8_t* PBitmap, const int PSrcBytesPerLine, EpImage* PImageBackground,  const EcRGBAiColor* PColor,  const EIndex PSrcX, const EIndex PSrcY,  const EIndex PDstX, const EIndex PDstY,  const EIndex PSizeX, const EIndex PSizeY,  const EIndex PClipMinX, const EIndex PClipMinY,  const EIndex PClipSizeX, const EIndex PClipSizeY);

extern int      EpImage_sBlend(const EpImage* PImageDecal, EpImage* PImageBackground, EpImage* PDImage,  const float PWeight,  EIndex PSrcX, EIndex PSrcY,  EIndex PDstX, EIndex PDstY,  EIndex PSizeX, EIndex PSizeY);

extern int      EpImage_Add(EpImage* PSImage, EpImage* PDImage);
extern void     EpImage_Div(const EpImage* PSImage, EpImage* PDImage, const int PNFramesFinishedI);

extern int      EpImage_GainRGBAi(EpImage* PImage, const EcRGBAiColor* PColor);

extern int      EpImages_Stitch(EpImage** PSImages,  const EIndex PNumOfColumns, const EIndex PNumOfRows,  EpImage* PDImage, const EBool PScaleToFit, const int PVerbosity);

extern int      EpImage_BumpmapToNormalmap(EpImage* PSImage, const float PHeightScale, const EpChannelMask PSrcChannelss, const EBool PCache,  const int PDstPixelType, const int PDstNComponents, const uint16_t PDstComponentType,  void* PNormalMapPixels,  void (*PProgressFunc)(const float PProgress, EPointer PClientData), EPointer PProgressClientData);

#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
}	// Close scope of 'extern "C"' declaration
#endif


#endif // _EImageOps_h
