// ASCGraphicsPath.h : Declaration of the AVSGraphicsPath

#pragma once
#include "stdafx.h"
#include "resource.h"       // main symbols
#include "Registration.h"

#include "..\Interfaces\IASCGraphics.h"
#include "ASCMatrix.h"
#include "ASCGraphicsPath.h"
#include "ASCSolidPattern.h"
#include "ASCGlyphImage.h"

#include "..\VectorGraphics\STypes.h"
#include "..\Common\Clip.h"
#include "..\Common\Pixels.h"
#include "..\VectorGraphics\SScreen.h"
#include "..\VectorGraphics\SErrorCodes.h"
#include "..\VectorGraphics\SXPath.h"
#include "..\VectorGraphics\SXPathScanner.h"
#include "..\VectorGraphics\SMathExt.h"
#include "ASCFontManager.h"

#include "TimeMeasurer.h"

// Расстояние от центра до контрольных точек Безье для аппроксимации окружности = (4 * (sqrt(2) - 1) / 3) * r
static const double c_dKappa   = ((double)0.55228475);
static const double c_dKappa_2 = ((double)(0.5 * 0.55228475));

// Делим 16-битное значение [0, 255*255] на 255, возвращаем 8-битное значение.
static inline unsigned char Div255(int nValue) 
{
	return (unsigned char)((nValue + (nValue >> 8) + 0x80) >> 8);
}

//-------------------------------------------------------------------------------------------------------------------------------
// Blend functions
//-------------------------------------------------------------------------------------------------------------------------------

static void RGBToHSV(unsigned char unR, unsigned char unG, unsigned char unB, int *pnH, int *pnS, int *pnV) 
{
	int nCmax, nCmid, nCmin, nValue;

	if ( unR >= unG ) 
	{
		if      ( unG >= unB ) { nValue = 0; nCmax = unR; nCmid = unG; nCmin = unB; }
		else if ( unB >= unR ) { nValue = 4; nCmax = unB; nCmid = unR; nCmin = unG; }
		else                   { nValue = 5; nCmax = unR; nCmid = unB; nCmin = unG; }
	} 
	else 
	{
		if      ( unR >= unB ) { nValue = 1; nCmax = unG; nCmid = unR; nCmin = unB; }
		else if ( unG >= unB ) { nValue = 2; nCmax = unG; nCmid = unB; nCmin = unR; }
		else                   { nValue = 3; nCmax = unB; nCmid = unG; nCmin = unR; }
	}
	if ( nCmax == nCmin ) 
	{
		*pnH = *pnS = 0;
	} 
	else 
	{
		*pnH = nValue * 60;
		if ( nValue & 1 ) 
		{
			*pnH += ( ( nCmax - nCmid ) * 60 ) / ( nCmax - nCmin );
		} 
		else 
		{
			*pnH += ( ( nCmid - nCmin ) * 60 ) / ( nCmax - nCmin );
		}
		*pnS = ( 255 * ( nCmax - nCmin ) ) / nCmax;
	}
	*pnV = nCmax;
}

static void HSVToRGB(int nH, int nS, int nV, unsigned char *punR, unsigned char *punG, unsigned char *punB) 
{
	if ( nS == 0 ) 
	{
		*punR = *punG = *punB = nV;
	} 
	else 
	{
		int nInt   = nH / 60;
		int nFloat = nH % 60;
		int nCmax = nV, nCmid;
		if ( nInt & 1 ) 
		{
			nCmid = Div255( nV * 255 - ( ( nS * nFloat ) / 60 ) );
		} 
		else 
		{
			nCmid = Div255( nV * ( 255 - ( ( nS * ( 60 - nFloat ) ) / 60 ) ) );
		}
		int nCmin = Div255( nV * ( 255 - nS ) );
		switch (nInt) 
		{
		case 0: *punR = nCmax; *punG = nCmid; *punB = nCmin; break;
		case 1: *punG = nCmax; *punR = nCmid; *punB = nCmin; break;
		case 2: *punG = nCmax; *punB = nCmid; *punR = nCmin; break;
		case 3: *punB = nCmax; *punG = nCmid; *punR = nCmin; break;
		case 4: *punB = nCmax; *punR = nCmid; *punG = nCmin; break;
		case 5: *punR = nCmax; *punB = nCmid; *punG = nCmin; break;
		}
	}
}


static void BlendMultiply  (SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	for ( int nIndex = 0; nIndex < SColorModeNComps[eColorMode]; ++nIndex ) 
	{
		pBlend[nIndex] = ( pDst[nIndex] * pSrc[nIndex] ) / 255;
	}
}

static void BlendScreen    (SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	for ( int nIndex = 0; nIndex < SColorModeNComps[eColorMode]; ++nIndex ) 
	{
		pBlend[nIndex] = pDst[nIndex] + pSrc[nIndex] - ( pDst[nIndex] * pSrc[nIndex] ) / 255;
	}
}

static void BlendOverlay   (SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	for ( int nIndex = 0; nIndex < SColorModeNComps[eColorMode]; ++nIndex ) 
	{
		pBlend[nIndex] = pDst[nIndex] < 0x80 ? ( pSrc[nIndex] * 2 * pDst[nIndex] ) / 255 : 255 - 2 * ( ( 255 - pSrc[nIndex] ) * ( 255 - pDst[nIndex] ) ) / 255;
	}
}

static void BlendDarken    (SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	for ( int nIndex = 0; nIndex < SColorModeNComps[eColorMode]; ++nIndex ) 
	{
		pBlend[nIndex] = pDst[nIndex] < pSrc[nIndex] ? pDst[nIndex] : pSrc[nIndex];
	}
}

static void BlendLighten   (SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	for ( int nIndex = 0; nIndex < SColorModeNComps[eColorMode]; ++nIndex ) 
	{
		pBlend[nIndex] = pDst[nIndex] > pSrc[nIndex] ? pDst[nIndex] : pSrc[nIndex];
	}
}

static void BlendColorDodge(SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	for ( int nIndex = 0; nIndex < SColorModeNComps[eColorMode]; ++nIndex ) 
	{
		if ( pSrc[nIndex] == 255 ) 
		{
			pBlend[nIndex] = 255;
		} 
		else 
		{
			int nValue = ( pDst[nIndex] * 255 ) / ( 255 - pSrc[nIndex] );
			pBlend[nIndex] = nValue <= 255 ? nValue : 255;
		}
	}
}

static void BlendColorBurn (SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	for ( int nIndex = 0; nIndex < SColorModeNComps[eColorMode]; ++nIndex ) 
	{
		if ( pSrc[nIndex] == 0 ) 
		{
			pBlend[nIndex] = 0;
		} 
		else 
		{
			int nValue = ( ( 255 - pDst[nIndex] ) * 255 ) / pSrc[nIndex];
			pBlend[nIndex] = nValue <= 255 ? 255 - nValue : 0;
		}
	}
}

static void BlendHardLight (SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	for ( int nIndex = 0; nIndex < SColorModeNComps[eColorMode]; ++nIndex ) 
	{
		pBlend[nIndex] = pSrc[nIndex] < 0x80 ? ( pDst[nIndex] * 2 * pSrc[nIndex] ) / 255 : 255 - 2 * ( ( 255 - pDst[nIndex] ) * ( 255 - pSrc[nIndex] ) ) / 255;
	}
}

static void BlendSoftLight (SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	for ( int nIndex = 0; nIndex < SColorModeNComps[eColorMode]; ++nIndex ) 
	{
		if ( pSrc[nIndex] < 0x80 ) 
		{
			pBlend[nIndex] = pDst[nIndex] - (255 - 2 * pSrc[nIndex]) * pDst[nIndex] * (255 - pDst[nIndex]) / ( 255 * 255 );
		} 
		else 
		{
			int nValue;
			if ( pDst[nIndex] < 0x40 ) 
			{
				nValue = ( ( ( ( ( 16 * pDst[nIndex] - 12 * 255 ) * pDst[nIndex] ) / 255 ) + 4 * 255 ) * pDst[nIndex] ) / 255;
			} 
			else 
			{
				nValue = (int)sqrt( 255.0 * pDst[nIndex] );
			}
			pBlend[nIndex] = pDst[nIndex] + ( 2 * pSrc[nIndex] - 255 ) * ( nValue - pDst[nIndex] ) / 255;
		}
	}
}

static void BlendDifference(SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	for ( int nIndex = 0; nIndex < SColorModeNComps[eColorMode]; ++nIndex ) 
	{
		pBlend[nIndex] = pDst[nIndex] < pSrc[nIndex] ? pSrc[nIndex] - pDst[nIndex] : pDst[nIndex] - pSrc[nIndex];
	}
}

static void BlendExclusion (SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	for ( int nIndex = 0; nIndex < SColorModeNComps[eColorMode]; ++nIndex ) 
	{
		pBlend[nIndex] = pDst[nIndex] + pSrc[nIndex] - ( 2 * pDst[nIndex] * pSrc[nIndex] ) / 255;
	}
}

static void BlendHue       (SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	int nSrcH, nSrcS, nSrcV, nDstH, nDstS, nDstV;

	switch (eColorMode) 
	{
	case colorModeMono1:
	case colorModeMono8:
		pBlend[0] = pDst[0];
		break;
	case colorModeRGB8:
	case colorModeBGR8:
		RGBToHSV( pSrc[0], pSrc[1], pSrc[2], &nSrcH, &nSrcS, &nSrcV );
		RGBToHSV( pDst[0], pDst[1], pDst[2], &nDstH, &nDstS, &nDstV );
		HSVToRGB( nSrcH, nDstS, nDstV, &pBlend[0], &pBlend[1], &pBlend[2] );
		break;
	}
}

static void BlendSaturation(SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	int nSrcH, nSrcS, nSrcV, nDstH, nDstS, nDstV;

	switch (eColorMode) 
	{
	case colorModeMono1:
	case colorModeMono8:
		pBlend[0] = pDst[0];
		break;
	case colorModeRGB8:
	case colorModeBGR8:
		RGBToHSV( pSrc[0], pSrc[1], pSrc[2], &nSrcH, &nSrcS, &nSrcV );
		RGBToHSV( pDst[0], pDst[1], pDst[2], &nDstH, &nDstS, &nDstV );
		HSVToRGB( nDstH, nSrcS, nDstV, &pBlend[0], &pBlend[1], &pBlend[2] );
		break;
	}
}

static void BlendColor     (SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	int nSrcH, nSrcS, nSrcV, nDstH, nDstS, nDstV;

	switch (eColorMode) 
	{
	case colorModeMono1:
	case colorModeMono8:
		pBlend[0] = pDst[0];
		break;
	case colorModeRGB8:
	case colorModeBGR8:
		RGBToHSV( pSrc[0], pSrc[1], pSrc[2], &nSrcH, &nSrcS, &nSrcV );
		RGBToHSV( pDst[0], pDst[1], pDst[2], &nDstH, &nDstS, &nDstV );
		HSVToRGB( nSrcH, nSrcS, nDstV, &pBlend[0], &pBlend[1], &pBlend[2] );
		break;
	}
}

static void BlendLuminosity(SColorPointer pSrc, SColorPointer pDst, SColorPointer pBlend, SColorMode eColorMode) 
{
	int nSrcH, nSrcS, nSrcV, nDstH, nDstS, nDstV;

	switch (eColorMode) 
	{
	case colorModeMono1:
	case colorModeMono8:
		pBlend[0] = pDst[0];
		break;
	case colorModeRGB8:
	case colorModeBGR8:
		RGBToHSV( pSrc[0], pSrc[1], pSrc[2], &nSrcH, &nSrcS, &nSrcV );
		RGBToHSV( pDst[0], pDst[1], pDst[2], &nDstH, &nDstS, &nDstV );
		HSVToRGB( nDstH, nDstS, nSrcV, &pBlend[0], &pBlend[1], &pBlend[2] );
		break;
	}
}