/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-2008 Miranda ICQ/IM project, 
all portions of this codebase are copyrighted to the people 
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

//Include 
#include "hdr/modern_commonheaders.h"
#include <m_png.h> 
#include "m_api/m_skin_eng.h"
#include "hdr/modern_skinselector.h"
#include "hdr/modern_cluiframes.h"

#define _EFFECTENUM_FULL_H
#include "hdr/modern_effectenum.h"
#undef _EFFECTENUM_FULL_H

#include "hdr/modern_skinengine.h"
#include "hdr/modern_commonprototypes.h"
#include <shlwapi.h>
#include "hdr/modern_sync.h"
//Implementation

/* Global variables */

SKINOBJECTSLIST g_SkinObjectList={0};
CURRWNDIMAGEDATA * g_pCachedWindow=NULL;

BOOL (WINAPI *g_proc_UpdateLayeredWindow)(HWND,HDC,POINT*,SIZE*,HDC,POINT*,COLORREF,BLENDFUNCTION*,DWORD);

BOOL    g_flag_bPostWasCanceled =FALSE;
BOOL	g_flag_bFullRepaint     =FALSE;
BOOL    g_mutex_bLockUpdating   =FALSE;

SortedList * gl_plGlyphTexts=NULL;
SortedList * gl_plSkinFonts =NULL;

/* Private module variables */

static HANDLE  hSkinLoadedEvent;

static GLYPHIMAGE * pLoadedImages=NULL;
static DWORD        dwLoadedImagesCount=0;
static DWORD        dwLoadedImagesAlocated=0;

static BOOL flag_bUpdateQueued=FALSE;
static BOOL	flag_bJustDrawNonFramedObjects=FALSE;
static BOOL mutex_bLockUpdate=FALSE;

static SortedList       * pEffectStack=NULL;
static SKINOBJECTSLIST  * pCurrentSkin=NULL;
static char            ** pszSettingName=NULL;
static int                nArrayLen=0;
static char             * iniCurrentSection=NULL;
static char             * szFileName=NULL;

static BYTE             pbGammaWeight[256]={0};
static BYTE             pbGammaWeightAdv[256]={0};
static BOOL             bGammaWeightFilled=FALSE;

static CRITICAL_SECTION cs_SkinChanging={0};


/* Private module procedures */
static BOOL ske_GetMaskBit(BYTE *line, int x);
static INT_PTR  ske_Service_AlphaTextOut(WPARAM wParam,LPARAM lParam);
static INT_PTR ske_Service_DrawIconEx(WPARAM wParam,LPARAM lParam);

static int  ske_AlphaTextOut (HDC hDC, LPCTSTR lpString, int nCount, RECT * lpRect, UINT format, DWORD ARGBcolor);
static void ske_AddParseTextGlyphObject(char * szGlyphTextID,char * szDefineString,SKINOBJECTSLIST *Skin);
static void ske_AddParseSkinFont(char * szFontID,char * szDefineString,SKINOBJECTSLIST *Skin);
static int  ske_DeleteAllSettingInSection(char * SectionName);
static int  ske_GetSkinFromDB(char * szSection, SKINOBJECTSLIST * Skin);
static LPSKINOBJECTDESCRIPTOR ske_FindObject(const char * szName, BYTE objType,SKINOBJECTSLIST* Skin);
static HBITMAP ske_LoadGlyphImageByDecoders(char * szFileName);
static int  ske_LoadSkinFromResource(BOOL bOnlyObjects);
static void ske_PreMultiplyChanells(HBITMAP hbmp,BYTE Mult);
static int  ske_ValidateSingleFrameImage(FRAMEWND * Frame, BOOL SkipBkgBlitting);
static INT_PTR ske_Service_UpdateFrameImage(WPARAM wParam, LPARAM lParam);
static INT_PTR ske_Service_InvalidateFrameImage(WPARAM wParam, LPARAM lParam);
static INT_PTR ske_Service_DrawTextWithEffect( WPARAM wParam, LPARAM lParam );

//Decoders
static HMODULE hImageDecoderModule;

typedef  DWORD  (__stdcall *pfnImgNewDecoder)(void ** ppDecoder); 
static pfnImgNewDecoder ImgNewDecoder;

typedef DWORD (__stdcall *pfnImgDeleteDecoder)(void * pDecoder);
static pfnImgDeleteDecoder ImgDeleteDecoder;

typedef  DWORD  (__stdcall *pfnImgNewDIBFromFile)(LPVOID /*in*/pDecoder, LPCSTR /*in*/pFileName, LPVOID /*out*/*pImg);
static pfnImgNewDIBFromFile ImgNewDIBFromFile;

typedef DWORD (__stdcall *pfnImgDeleteDIBSection)(LPVOID /*in*/pImg);
static pfnImgDeleteDIBSection ImgDeleteDIBSection;

typedef DWORD (__stdcall *pfnImgGetHandle)(LPVOID /*in*/pImg, HBITMAP /*out*/*pBitmap, LPVOID /*out*/*ppDIBBits);
static pfnImgGetHandle ImgGetHandle;    

static MODERNEFFECT meCurrentEffect={-1,{0},0,0};


//////////////////////////////////////////////////////////////////////////
// Ini file parser
//////////////////////////////////////////////////////////////////////////
IniParser::IniParser( TCHAR * tcsFileName, BYTE flags ) : _Flags( flags )
{
	_DoInit();
	if ( !tcsFileName ) return;

	if ( tcsFileName[0] == _T('%'))
	{
		//TODO: Add parser of resource filename here
		_LoadResourceIni( g_hInst, MAKEINTRESOURCEA(IDR_MSF_DEFAULT_SKIN), "MSF");
		return;
	}

	_hFile = _tfopen( tcsFileName, _T("r"));

	if ( _hFile != NULL )
	{
		_eType = IT_FILE;
		_isValid = true;
	}
}

IniParser::IniParser( HINSTANCE hInst, const char *  resourceName, const char * resourceType, BYTE flags ) : _Flags( flags )
{
	_DoInit();
	_LoadResourceIni( hInst, resourceName, resourceType );
}

IniParser::~IniParser()
{
	if ( _szSection ) mir_free( _szSection ); 
	if ( _hFile ) fclose( _hFile ); 
	if ( _hGlobalRes ) 
	{
		UnlockResource( _hGlobalRes ); 
		FreeResource(_hGlobalRes);	
	}

	_szSection = NULL;
	_hGlobalRes = NULL;
	_hFile = NULL;
	_isValid = false;
	_eType = IT_UNKNOWN;
}

HRESULT IniParser::Parse( ParserCallback_t pLineCallBackProc, LPARAM SecCheck )
{
	if ( _isValid && pLineCallBackProc )
	{	
		_pLineCallBackProc = pLineCallBackProc;
		_SecCheck = SecCheck;
		switch ( _eType )
		{
		case IT_FILE:
			return _DoParseFile();
		case IT_RESOURCE:
			return _DoParseResource();
		}
	}
	return E_FAIL;
}


HRESULT IniParser::WriteStrToDb( const char * szSection, const char * szName, const char * szValue, IniParser * This )
{
	if ( This->_SecCheck)
	{
		//TODO check security here
		if ( wildcmp( szSection,"Skin_Description_Section",1 )) return S_OK;
	}
	if ( ( This->_Flags == IniParser::FLAG_ONLY_OBJECTS ) && !wildcmp( szSection, DEFAULTSKINSECTION,1 ))
		return S_OK;					 // skip not objects


//	if ( strlen(szValue)>0 && szValue[strlen(szValue)-1]=='\n' ) 
//		szValue[strlen(szValue)-1]='\0';  //kill linefeed at the end  

	switch(szValue[0]) 
	{
	case 'b':
		{
			BYTE P;
			P=(BYTE)atoi(szValue+1);
			ModernWriteSettingByte(NULL,szSection,szName,P);
		}
		break;
	case 'w':
		{
			WORD P;
			P=(WORD)atoi(szValue+1);
			ModernWriteSettingWord(NULL,szSection,szName,P);
		}
		break;
	case 'd':
		{
			DWORD P;
			P=(DWORD)atoi(szValue+1);
			ModernWriteSettingDword(NULL,szSection,szName,P);
		}
		break;
	case 's':
		ModernWriteSettingString(NULL,szSection,szName,szValue+1);
		break;
	case 'f':
		if (szFileName)
		{
			char fn[MAX_PATH]={0};
			char bb[MAX_PATH*2]={0};
			int pp, i;
			pp=-1;
			CallService(MS_UTILS_PATHTORELATIVE, (WPARAM)szFileName, (LPARAM)fn);
			{
				for (i=strlen(fn); i>=0; i--)  if (fn[i]=='.') break;
				if (i>0) fn[i]='\0';
			}                      
			_snprintf(bb,SIZEOF(bb),"%s\\%s",fn,szValue+1);
			ModernWriteSettingString(NULL,szSection,szName,bb);
		}
		break;
	}
	return S_OK;
}
int IniParser::GetSkinFolder( IN const TCHAR * szFileName, OUT TCHAR * pszFolderName )
{
	TCHAR *pszPos;   
	TCHAR *szBuff;

	szBuff = mir_tstrdup( szFileName );
	pszPos = szBuff + _tcslen( szBuff );
	while ( pszPos > szBuff && *pszPos!= _T('.')) { pszPos--; }
	*pszPos=_T('\0');
	_tcscpy( pszFolderName, szBuff );

	TCHAR custom_folder[MAX_PATH];
	TCHAR cus[MAX_PATH];
	TCHAR *b3;
	_tcscpy( custom_folder, pszFolderName );
	b3=custom_folder + _tcslen( custom_folder );
	while ( b3 > custom_folder && *b3!= _T('\\')) { b3--; }
	*b3=_T('\0');

	GetPrivateProfileString(_T("Skin_Description_Section"),_T("SkinFolder"),_T(""),cus,SIZEOF(custom_folder),szFileName);
	if ( cus && _tcslen(cus)>0)
		_sntprintf(pszFolderName,MAX_PATH,_T("%s\\%s"),custom_folder,cus);

	mir_free_and_nill(szBuff);
	CallService(MS_UTILS_PATHTORELATIVET, (WPARAM)pszFolderName, (LPARAM)pszFolderName);

	return 0;
}
void IniParser::_DoInit()
{
	_isValid	 = false;
	_eType		 = IT_UNKNOWN; 
	_szSection	 = NULL;
	_hFile		 = NULL;
	_hGlobalRes  = NULL;
	_dwSizeOfRes = 0;
	_pPosition   = NULL;
	_pLineCallBackProc = NULL;
	_SecCheck		 = 0;

}

void IniParser::_LoadResourceIni( HINSTANCE hInst,  const char *  resourceName, const char * resourceType )
{

	if ( _eType != IT_UNKNOWN ) return;

	HRSRC hRSrc = FindResourceA( hInst, resourceName, resourceType );
	if ( !hRSrc ) return;

	_hGlobalRes=LoadResource( hInst, hRSrc );
	if ( !_hGlobalRes ) return;

	_dwSizeOfRes = SizeofResource( hInst, hRSrc );
	_pPosition =(char*) LockResource( _hGlobalRes );

	_isValid = true;
	_eType	 = IT_RESOURCE;

}



HRESULT IniParser::_DoParseFile()
{
	char szLine[MAX_LINE_LEN];
	_nLine = 0;
	while ( fgets( szLine, SIZEOF(szLine), _hFile ) != NULL )
	{	
		size_t len = 0;
		char * pLine = (char*)_RemoveTailings( szLine, len );
		if ( len > 0 )
		{
			pLine[len]='\0';
			if ( !_DoParseLine( pLine ))	return E_FAIL;
		}
		else
			_nLine++;
	};

	return S_OK;
}

HRESULT IniParser::_DoParseResource()
{
	_nLine = 0;
	char szLine[MAX_LINE_LEN];
	char * pos = ( char* ) _pPosition;

	while ( pos < _pPosition + _dwSizeOfRes )
	{
		int i=0;
		while ( pos < _pPosition + _dwSizeOfRes && *pos != '\n' && *pos!= '\0' && i < MAX_LINE_LEN - 1 )
		{
			if ( (*pos) != '\r' ) 
				szLine[ i++ ] = *pos;
			pos++;
		}
		szLine[ i ]='\0';
		pos++;
		
		size_t len = 0;
		char * pLine = (char*) _RemoveTailings( szLine, len );
		if ( len > 0 )
		{
			pLine[len]='\0';
			if ( !_DoParseLine( pLine ))	return E_FAIL;
		}
		else
			_nLine++;
	}
	return S_OK;
}

const char * IniParser::_RemoveTailings( const char * szLine, size_t& len )
{
	const char * pStart = szLine;
	while( *pStart == ' ' || *pStart=='\t' ) pStart++; //skip spaces at begin
	const char * pEnd = pStart + strlen( pStart );
	while( pEnd > pStart && ( *pEnd == ' ' || *pEnd == '\t' || *pEnd == '\n' || *pEnd == '\r' )) pEnd--;

	len = pEnd - pStart;
	return pStart;
}

BOOL IniParser::_DoParseLine( char * szLine )
{
	_nLine++;
	DWORD len = strlen( szLine );

	if ( len == 0 )	return TRUE;

	switch( szLine[0] )
	{
	case ';':
		return TRUE; // start of comment is found
	case '[':
		{
			//New section start here
			if ( _szSection ) mir_free( _szSection );
			_szSection = NULL;

			char *tbuf = szLine + 1;	// skip [
	
			char *ebuf= tbuf;

			while ( *ebuf != ']' && *ebuf !='\0' ) ebuf++;
			if ( *ebuf == '\0' ) 
				return FALSE; // no close bracket
	
			DWORD sectionLen = ebuf - tbuf;
			_szSection = (char*) mir_alloc( sectionLen + 1 );
			strncpy( _szSection, tbuf, sectionLen );
			_szSection[sectionLen]='\0';
		}
		return TRUE;

	default:
		if ( !_szSection ) 
			return TRUE;  //param found out of section

		char *keyName=szLine;
		char *keyValue=szLine;

		DWORD eqPlace=0;
		DWORD len2=strlen(keyName);

		while ( eqPlace < len2 && keyName[ eqPlace ] != '=' ) 
			eqPlace++; //find '='

		if (eqPlace==0 || eqPlace==len2) 
			return TRUE; //= not found or no key name //say false

		keyName[eqPlace] = '\0';

		keyValue = keyName + eqPlace + 1;

		//remove tail spaces in Name
		{
			DWORD len3=strlen(keyName);
			int j=len3-1;
			while (j>0 && (keyName[j]==' ' || keyName[j]=='\t')) j--;
			if (j>=0) keyName[j+1]='\0';
		}		
		//remove start spaces in Value
		{
			DWORD len3=strlen(keyValue);
			DWORD j=0;
			while (j<len3 && (keyValue[j]==' ' || keyValue[j]=='\t')) j++;
			if (j<len3) keyValue+=j;
		}
		//remove tail spaces in Value
		{
			DWORD len3=strlen(keyValue);
			int j=len3-1;
			while (j>0 && (keyValue[j]==' ' || keyValue[j]=='\t' || keyValue[j]=='\n')) j--;
			if (j>=0) keyValue[j+1]='\0';
		}
		_pLineCallBackProc( _szSection, keyName, keyValue, this );
	}
	return TRUE;
}
//////////////////////////////////////////////////////////////////////////
// End of IniParser
//////////////////////////////////////////////////////////////////////////

HRESULT SkinEngineLoadModule()
{
	ModernSkinButtonLoadModule();
	InitializeCriticalSection(&cs_SkinChanging);
	MainModernMaskList=(LISTMODERNMASK*)mir_calloc(sizeof(LISTMODERNMASK));
	//init variables
	g_SkinObjectList.dwObjLPAlocated=0;
	g_SkinObjectList.dwObjLPReserved=0;
	g_SkinObjectList.pObjects=NULL;
	// Initialize GDI+
	InitGdiPlus();
	AniAva_InitModule();
	//load decoder
	hImageDecoderModule=NULL;
	if (g_CluiData.fGDIPlusFail)
	{
		hImageDecoderModule = LoadLibrary(TEXT("ImgDecoder.dll"));
		if (hImageDecoderModule==NULL) 
		{
			char tDllPath[ MAX_PATH ];
			GetModuleFileNameA( g_hInst, tDllPath, sizeof( tDllPath ));
			{
				char* p = strrchr( tDllPath, '\\' );
				if ( p != NULL )
					strcpy( p+1, "ImgDecoder.dll" );
				else
				{
					strcpy( tDllPath, "ImgDecoder.dll" );
				}
			}

			hImageDecoderModule = LoadLibraryA(tDllPath);
		}
		if (hImageDecoderModule!=NULL) 
		{
			ImgNewDecoder=(pfnImgNewDecoder )GetProcAddress( hImageDecoderModule, "ImgNewDecoder");
			ImgDeleteDecoder=(pfnImgDeleteDecoder )GetProcAddress( hImageDecoderModule, "ImgDeleteDecoder");
			ImgNewDIBFromFile=(pfnImgNewDIBFromFile)GetProcAddress( hImageDecoderModule, "ImgNewDIBFromFile");
			ImgDeleteDIBSection=(pfnImgDeleteDIBSection)GetProcAddress( hImageDecoderModule, "ImgDeleteDIBSection");
			ImgGetHandle=(pfnImgGetHandle)GetProcAddress( hImageDecoderModule, "ImgGetHandle");
		}
	}
	//create services
	CreateServiceFunction(MS_SKIN_DRAWGLYPH,ske_Service_DrawGlyph);
	CreateServiceFunction(MS_SKINENG_UPTATEFRAMEIMAGE,ske_Service_UpdateFrameImage);
	CreateServiceFunction(MS_SKINENG_INVALIDATEFRAMEIMAGE,ske_Service_InvalidateFrameImage);
	CreateServiceFunction(MS_SKINENG_ALPHATEXTOUT,ske_Service_AlphaTextOut);    
	CreateServiceFunction(MS_SKINENG_DRAWICONEXFIX,ske_Service_DrawIconEx);

    CreateServiceFunction(MS_DRAW_TEXT_WITH_EFFECT,ske_Service_DrawTextWithEffect);

	//create event handle
	hSkinLoadedEvent=ModernHookEvent(ME_SKIN_SERVICESCREATED,CLUI_OnSkinLoad);
	NotifyEventHooks(g_CluiData.hEventSkinServicesCreated,0,0);
	return S_OK;
}

int SkinEngineUnloadModule()
{
	//unload services
	ModernUnhookEvent(hSkinLoadedEvent);
	ModernSkinButtonUnloadModule(0,0);
	ske_UnloadSkin(&g_SkinObjectList);
	if (g_SkinObjectList.pObjects) 
		mir_free_and_nill(g_SkinObjectList.pObjects);
	if (g_SkinObjectList.pMaskList) 
		mir_free_and_nill(g_SkinObjectList.pMaskList);
	if (MainModernMaskList)
		mir_free_and_nill(MainModernMaskList);
	if (pEffectStack)
	{
		int i;
		for (i=0; i<pEffectStack->realCount; i++)
			if (pEffectStack->items[i])
			{
				EFFECTSSTACKITEM * effect=(EFFECTSSTACKITEM*)(pEffectStack->items[i]);
				mir_free_and_nill(effect);
			}
			li.List_Destroy(pEffectStack);
			mir_free_and_nill(pEffectStack);
	}
	if (g_pCachedWindow)
	{
		SelectObject(g_pCachedWindow->hBackDC,g_pCachedWindow->hBackOld);
		SelectObject(g_pCachedWindow->hImageDC,g_pCachedWindow->hImageOld);
		DeleteObject(g_pCachedWindow->hBackDIB);
		DeleteObject(g_pCachedWindow->hImageDIB);
		mod_DeleteDC(g_pCachedWindow->hBackDC);
		mod_DeleteDC(g_pCachedWindow->hImageDC);
		ReleaseDC(NULL,g_pCachedWindow->hScreenDC);
		mir_free_and_nill(g_pCachedWindow);
		g_pCachedWindow=NULL;
	}
	DeleteCriticalSection(&cs_SkinChanging);
	GdiFlush();
	DestroyServiceFunction((HANDLE)MS_SKIN_REGISTEROBJECT);
	DestroyServiceFunction((HANDLE)MS_SKIN_DRAWGLYPH);
	DestroyHookableEvent(g_CluiData.hEventSkinServicesCreated);
	if (hImageDecoderModule) FreeLibrary(hImageDecoderModule);
	AniAva_UnloadModule();
	ShutdownGdiPlus();
	//free variables
	return 1;
}



BOOL ske_AlphaBlend(HDC hdcDest,int nXOriginDest,int nYOriginDest,int nWidthDest,int nHeightDest,HDC hdcSrc,int nXOriginSrc,int nYOriginSrc,int nWidthSrc,int nHeightSrc,BLENDFUNCTION blendFunction)
{
	if (g_CluiData.fDisableSkinEngine && !(!g_CluiData.fGDIPlusFail && blendFunction.BlendFlags&128))
	{
		if (nWidthDest!=nWidthSrc || nHeightDest!=nHeightSrc) 
			return StretchBlt(hdcDest,nXOriginDest,nYOriginDest,nWidthDest,nHeightDest,hdcSrc,nXOriginSrc,nYOriginSrc,nWidthSrc,nHeightSrc, SRCCOPY);
		else
			return BitBlt(hdcDest,nXOriginDest,nYOriginDest,nWidthDest,nHeightDest,hdcSrc,nXOriginSrc,nYOriginSrc, SRCCOPY);
	}

	if (!g_CluiData.fGDIPlusFail && blendFunction.BlendFlags&128 ) //Use gdi+ engine
	{
		return GDIPlus_AlphaBlend( hdcDest,nXOriginDest,nYOriginDest,nWidthDest,nHeightDest,
			hdcSrc,nXOriginSrc,nYOriginSrc,nWidthSrc,nHeightSrc,
			&blendFunction);
	}
	blendFunction.BlendFlags&=~128;
	return AlphaBlend(hdcDest,nXOriginDest,nYOriginDest,nWidthDest,nHeightDest,hdcSrc,nXOriginSrc,nYOriginSrc,nWidthSrc,nHeightSrc,blendFunction);
}


static int ske_LockSkin()
{
	EnterCriticalSection(&cs_SkinChanging);
	return 0;
}
static int ske_UnlockSkin()
{
	LeaveCriticalSection(&cs_SkinChanging);
	return 0;
}

typedef struct _tagDCBuffer
{
	HDC hdcOwnedBy;
	int nUsageID;
	int width;
	int height;
	void* pImage;
	HDC hDC;
	HBITMAP oldBitmap;
	HBITMAP hBitmap;
	DWORD dwDestroyAfterTime;
}DCBUFFER;
CRITICAL_SECTION BufferListCS={0};

SortedList * BufferList=NULL;
enum
{
	BUFFER_DRAWICON=0,
	BUFFER_DRAWIMAGE
};

int SortBufferList(void* it1, void * it2)
{
	DCBUFFER * buf1=(DCBUFFER *)it1;
	DCBUFFER * buf2=(DCBUFFER *)it2;
	if (buf1->hdcOwnedBy!=buf2->hdcOwnedBy) return (int)(buf1->hdcOwnedBy < buf2->hdcOwnedBy);
	else if (buf1->nUsageID!=buf2->nUsageID) return (int) (buf1->nUsageID < buf2->nUsageID);
	else return (int) (buf1->hDC < buf2->hDC);
}

HDC ske_RequestBufferDC(HDC hdcOwner, int dcID, int width, int height, BOOL fClear)
{
	DCBUFFER buf;
	DCBUFFER * pBuf;
	if (BufferList==NULL)
	{
		BufferList=li.List_Create(0,2);
		BufferList->sortFunc=SortBufferList;
		InitializeCriticalSection(&BufferListCS);
	}
	EnterCriticalSection(&BufferListCS);
	//Try to find DC in buffer list	
	buf.hdcOwnedBy=hdcOwner;
	buf.nUsageID=dcID;
	buf.hDC=NULL;
	pBuf=(DCBUFFER*)li.List_Find(BufferList,(void*)&buf);
	if (!pBuf)
	{
		//if not found - allocate it
		pBuf=(DCBUFFER *)mir_alloc(sizeof(DCBUFFER));
		*pBuf=buf;
		pBuf->width=width;
		pBuf->height=height;
		pBuf->hBitmap=ske_CreateDIB32Point(width,height,&(pBuf->pImage));
		pBuf->hDC=CreateCompatibleDC(hdcOwner);
		pBuf->oldBitmap=(HBITMAP)SelectObject(pBuf->hDC,pBuf->hBitmap);
		pBuf->dwDestroyAfterTime=0;
		li.List_InsertPtr(BufferList,pBuf);		
	}
	else 
	{
		if (pBuf->width!=width || pBuf->height!=height)
		{
			//resize
			SelectObject(pBuf->hDC,pBuf->oldBitmap);
			DeleteObject(pBuf->hBitmap);
			pBuf->width=width;
			pBuf->height=height;
			pBuf->hBitmap=ske_CreateDIB32Point(width,height,&(pBuf->pImage));
			pBuf->oldBitmap=(HBITMAP)SelectObject(pBuf->hDC,pBuf->hBitmap);
		}  else	if (fClear)
			memset(pBuf->pImage,0,width*height*sizeof(DWORD));
	}
	pBuf->dwDestroyAfterTime=0;
	LeaveCriticalSection(&BufferListCS);
	return pBuf->hDC;
}

int ske_ReleaseBufferDC(HDC hDC, int keepTime)
{
	DWORD dwCurrentTime=GetTickCount();
	DCBUFFER * pBuf=NULL;
	//Try to find DC in buffer list - set flag to be release after time;
	int i=0;
	EnterCriticalSection(&BufferListCS);
	for (i=0; i<BufferList->realCount; i++)
	{
		pBuf=(DCBUFFER *)BufferList->items[i];
		if (pBuf)
		{
			if (hDC!=NULL && pBuf->hDC==hDC)
			{
				pBuf->dwDestroyAfterTime=dwCurrentTime+keepTime;
				break;
			}
			else  
			{
				if ((pBuf->dwDestroyAfterTime && pBuf->dwDestroyAfterTime < dwCurrentTime) || keepTime==-1)
				{
					SelectObject(pBuf->hDC,pBuf->oldBitmap);
					DeleteObject(pBuf->hBitmap);
					DeleteDC(pBuf->hDC);
					mir_free(pBuf);
					li.List_Remove(BufferList,i);
					i--;
				}
			}
		}
	}
	LeaveCriticalSection(&BufferListCS);
	return 0;
}

BOOL ske_SetRgnOpaque(HDC memdc,HRGN hrgn, BOOL force)
{
	RGNDATA * rdata;
	DWORD rgnsz;
	DWORD d;
	RECT * rect;
	if (g_CluiData.fDisableSkinEngine && !force) return TRUE;
	rgnsz=GetRegionData(hrgn,0,NULL);
	rdata=(RGNDATA *) mir_alloc(rgnsz);
	GetRegionData(hrgn,rgnsz,rdata);
	rect=(RECT *)rdata->Buffer;
	for (d=0; d<rdata->rdh.nCount; d++)
	{
		ske_SetRectOpaque(memdc,&rect[d], force);
	}
	mir_free_and_nill(rdata);
	return TRUE;
}


BOOL ske_SetRectOpaque(HDC memdc,RECT *fr, BOOL force)
{
	int f=0;
	BYTE * bits;
	BITMAP bmp;
	HBITMAP hbmp;

	if ( g_CluiData.fDisableSkinEngine && !force ) 
		return TRUE;

	hbmp=(HBITMAP)GetCurrentObject( memdc,OBJ_BITMAP );  
	GetObject( hbmp, sizeof(bmp), &bmp );

	if ( bmp.bmPlanes != 1 )
		return FALSE;
	
	if (!bmp.bmBits)
	{
		f=1;
		bits=(BYTE*)malloc(bmp.bmWidthBytes*bmp.bmHeight);
		GetBitmapBits(hbmp,bmp.bmWidthBytes*bmp.bmHeight,bits);
	}
	else
		bits=(BYTE*)bmp.bmBits;

	int sx =( fr->left > 0 ) ? fr->left : 0;
	int sy =( fr->top > 0 ) ? fr->top : 0;
	int ex =( fr->right < bmp.bmWidth ) ? fr->right : bmp.bmWidth;
	int ey =( fr->bottom<bmp.bmHeight) ? fr->bottom : bmp.bmHeight;

	int width=ex-sx;
	
	BYTE* pLine = ((BYTE*)bits) + (bmp.bmHeight-sy-1)*bmp.bmWidthBytes + (sx << 2) + 3;
	for ( int y = 0; y < (ey - sy); y++ )
	{	
		BYTE * pColumn = pLine;
		for ( int x = 0; x < width; x++ )
		{	
			*pColumn = 255; 
			pColumn += 4;
		}
		pLine -= bmp.bmWidthBytes;
	}
	if (f)
	{
		SetBitmapBits(hbmp,bmp.bmWidthBytes*bmp.bmHeight,bits);    
		free(bits);
	}
	// DeleteObject(hbmp);
	return 1;
}

static BOOL ske_SkinFillRectByGlyph(HDC hDest, HDC hSource, RECT * rFill, RECT * rGlyph, RECT * rClip, BYTE mode, BYTE drawMode, int depth)
{
	int destw=0, desth=0;
	int xstart=0, xmax=0;
	int ystart=0, ymax=0;
	BLENDFUNCTION bfa={AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; 
	BLENDFUNCTION bf={AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; 

	//initializations
	if (mode==FM_STRETCH)
	{
		HDC mem2dc;
		HBITMAP mem2bmp, oldbmp;
		RECT wr;
		IntersectRect(&wr,rClip,rFill);
		if ((wr.bottom-wr.top)*(wr.right-wr.left)==0) return 0;       
		if (drawMode!=2)
		{
			mem2dc=CreateCompatibleDC(hDest);
			mem2bmp=ske_CreateDIB32(wr.right-wr.left,wr.bottom-wr.top);
			oldbmp=(HBITMAP)SelectObject(mem2dc,mem2bmp);

		}

		if (drawMode==0 || drawMode==2)
		{
			if (drawMode==0)
			{
				ske_AlphaBlend(mem2dc,rFill->left-wr.left,rFill->top-wr.top,rFill->right-rFill->left,rFill->bottom-rFill->top,
					hSource,rGlyph->left,rGlyph->top,rGlyph->right-rGlyph->left,rGlyph->bottom-rGlyph->top,bf);
				ske_AlphaBlend(hDest,wr.left,wr.top,wr.right-wr.left, wr.bottom -wr.top,mem2dc,0,0,wr.right-wr.left, wr.bottom -wr.top,bf);
			}
			else 
			{
				ske_AlphaBlend(hDest,rFill->left,rFill->top,rFill->right-rFill->left,rFill->bottom-rFill->top,
					hSource,rGlyph->left,rGlyph->top,rGlyph->right-rGlyph->left,rGlyph->bottom-rGlyph->top,bf);

			}
		}
		else
		{
			//            BLENDFUNCTION bf={AC_SRC_OVER, 0, 255, 0 };
			ske_AlphaBlend(mem2dc,rFill->left-wr.left,rFill->top-wr.top,rFill->right-rFill->left,rFill->bottom-rFill->top,
				hSource,rGlyph->left,rGlyph->top,rGlyph->right-rGlyph->left,rGlyph->bottom-rGlyph->top,bf); 
			ske_AlphaBlend(hDest,wr.left,wr.top,wr.right-wr.left, wr.bottom -wr.top,mem2dc,0,0,wr.right-wr.left, wr.bottom -wr.top,bf);
		}
		if (drawMode!=2)
		{
			SelectObject(mem2dc,oldbmp);
			DeleteObject(mem2bmp);
			mod_DeleteDC(mem2dc);
		}
		return 1;
	}
	else if (mode==FM_TILE_VERT && (rGlyph->bottom-rGlyph->top>0)&& (rGlyph->right-rGlyph->left>0))
	{
		HDC mem2dc;
		HBITMAP mem2bmp,oldbmp;
		RECT wr;
		IntersectRect(&wr,rClip,rFill);
		if ((wr.bottom-wr.top)*(wr.right-wr.left)==0) return 0;
		mem2dc=CreateCompatibleDC(hDest);
		//SetStretchBltMode(mem2dc, HALFTONE);
		mem2bmp=ske_CreateDIB32(wr.right-wr.left, rGlyph->bottom-rGlyph->top);
		oldbmp=(HBITMAP)SelectObject(mem2dc,mem2bmp);
		if (!oldbmp) 
			return 0;

		/// draw here
		{
			int  y=0, sy=0, maxy=0;
			int w=rFill->right-rFill->left;
			int h=rGlyph->bottom-rGlyph->top;
			if (h>0 && (wr.bottom-wr.top)*(wr.right-wr.left)!=0)
			{
				w=wr.right-wr.left;
				{
					//                   BLENDFUNCTION bf={AC_SRC_OVER, 0, 255, 0 };
					ske_AlphaBlend(mem2dc,-(wr.left-rFill->left),0,rFill->right-rFill->left,h,hSource,rGlyph->left,rGlyph->top,rGlyph->right-rGlyph->left,h,bf);
					//StretchBlt(mem2dc,-(wr.left-rFill->left),0,rFill->right-rFill->left,h,hSource,rGlyph->left,rGlyph->top,rGlyph->right-rGlyph->left,h,SRCCOPY);
				}
				if (drawMode==0 || drawMode==2)
				{
					if (drawMode==0 )
					{

						int dy;
						dy=(wr.top-rFill->top)%h;
						if (dy>=0)
						{
							int ht;
							y=wr.top;
							ht=(y+h-dy<=wr.bottom)?(h-dy):(wr.bottom-wr.top);
							BitBlt(hDest,wr.left,y,w,ht,mem2dc,0,dy,SRCCOPY);
						}

						y=wr.top+h-dy;
						while (y<wr.bottom-h){
							BitBlt(hDest,wr.left,y,w,h,mem2dc,0,0,SRCCOPY);
							y+=h;
						}
						if (y<=wr.bottom)
							BitBlt(hDest,wr.left,y,w,wr.bottom-y, mem2dc,0,0,SRCCOPY);                                            

					}
					else 
					{  
						y=wr.top;
						while (y<wr.bottom-h)
						{
							//                             BLENDFUNCTION bf={AC_SRC_OVER, 0, 255, 0 };
							ske_AlphaBlend(hDest,wr.left,y,w,h, mem2dc,0,0,w,h,bf);                    
							y+=h;
						}
						if (y<=wr.bottom)
						{
							//                           BLENDFUNCTION bf={AC_SRC_OVER, 0, 255, 0 };
							ske_AlphaBlend(hDest,wr.left,y,w,wr.bottom-y, mem2dc,0,0,w,wr.bottom-y,bf);                                            
						}
					}

				}
				else
				{
					int dy;

					BLENDFUNCTION bf={AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };

					dy=(wr.top-rFill->top)%h;

					if (dy>=0)
					{
						int ht;
						y=wr.top;
						ht=(y+h-dy<=wr.bottom)?(h-dy):(wr.bottom-wr.top);
						ske_AlphaBlend(hDest,wr.left,y,w,ht,mem2dc,0,dy,w,ht,bf);
					}

					y=wr.top+h-dy;
					while (y<wr.bottom-h)
					{
						ske_AlphaBlend(hDest,wr.left,y,w,h,mem2dc,0,0,w,h,bf);
						y+=h;
					}
					if (y<=wr.bottom)
						ske_AlphaBlend(hDest,wr.left,y,w,wr.bottom-y, mem2dc,0,0,w,wr.bottom-y,bf);
				}
			}
		}
		SelectObject(mem2dc,oldbmp);
		DeleteObject(mem2bmp);
		mod_DeleteDC(mem2dc);
	}
	else if (mode==FM_TILE_HORZ && (rGlyph->right-rGlyph->left>0)&& (rGlyph->bottom-rGlyph->top>0)&&(rFill->bottom-rFill->top)>0 && (rFill->right-rFill->left)>0)
	{
		HDC mem2dc;
		RECT wr;
		HBITMAP mem2bmp,oldbmp;
		int w=rGlyph->right-rGlyph->left;
		int h=rFill->bottom-rFill->top;
		IntersectRect(&wr,rClip,rFill);
		if ((wr.bottom-wr.top)*(wr.right-wr.left)==0) return 0;
		h=wr.bottom-wr.top;
		mem2dc=CreateCompatibleDC(hDest);

		mem2bmp=ske_CreateDIB32(w,h);
		oldbmp=(HBITMAP)SelectObject(mem2dc,mem2bmp);

		if (!oldbmp)
			return 0;
		/// draw here
		{
			int  x=0, sy=0, maxy=0;
			{
				//SetStretchBltMode(mem2dc, HALFTONE);
				//StretchBlt(mem2dc,0,0,w,h,hSource,rGlyph->left+(wr.left-rFill->left),rGlyph->top,w,h,SRCCOPY);

				//                    BLENDFUNCTION bf={AC_SRC_OVER, 0, 255, 0 };
				ske_AlphaBlend(mem2dc,0,-(wr.top-rFill->top),w,rFill->bottom-rFill->top,hSource,rGlyph->left,rGlyph->top,w,rGlyph->bottom-rGlyph->top,bf);
				if (drawMode==0 || drawMode==2)
				{
					if (drawMode==0)
					{

						int dx;
						dx=(wr.left-rFill->left)%w;
						if (dx>=0)
						{   
							int wt;
							x=wr.left;
							wt=(x+w-dx<=wr.right)?(w-dx):(wr.right-wr.left);                                                   
							BitBlt(hDest,x,wr.top,wt,h,mem2dc,dx,0,SRCCOPY);
						}
						x=wr.left+w-dx;                        
						while (x<wr.right-w){
							BitBlt(hDest,x,wr.top,w,h,mem2dc,0,0,SRCCOPY);
							x+=w;
						}
						if (x<=wr.right)
						BitBlt(hDest,x,wr.top,wr.right-x,h, mem2dc,0,0,SRCCOPY);  
					}
					else 
					{
						int dx;
						dx=(wr.left-rFill->left)%w;
						x=wr.left-dx;
						while (x<wr.right-w){
							ske_AlphaBlend(hDest,x,wr.top,w,h,mem2dc,0,0,w,h,bf);
							x+=w;
						}
						if (x<=wr.right)
							ske_AlphaBlend(hDest,x,wr.top,wr.right-x,h, mem2dc,0,0,wr.right-x,h,bf);
					}

				}
				else
				{
					BLENDFUNCTION bf={AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
					int dx;
					dx=(wr.left-rFill->left)%w;
					if (dx>=0)
					{
						int wt;
						x=wr.left;
						wt=(x+w-dx<=wr.right)?(w-dx):(wr.right-wr.left); 
						ske_AlphaBlend(hDest,x,wr.top,wt,h,mem2dc,dx,0,wt,h,bf);
					}
					x=wr.left+w-dx;
					while (x<wr.right-w){
						ske_AlphaBlend(hDest,x,wr.top,w,h,mem2dc,0,0,w,h,bf);
						x+=w;
					}
					if (x<=wr.right)
						ske_AlphaBlend(hDest,x,wr.top,wr.right-x,h, mem2dc,0,0,wr.right-x,h,bf);  

				}
			}
		}
		SelectObject(mem2dc,oldbmp);
		DeleteObject(mem2bmp);
		mod_DeleteDC(mem2dc);
	}
	else if (mode==FM_TILE_BOTH && (rGlyph->right-rGlyph->left>0) && (rGlyph->bottom-rGlyph->top>0))
	{
		HDC mem2dc;
		int w=rGlyph->right-rGlyph->left;
		int  x=0, sy=0, maxy=0;
		int h=rFill->bottom-rFill->top;
		HBITMAP mem2bmp,oldbmp;
		RECT wr;
		IntersectRect(&wr,rClip,rFill);
		if ((wr.bottom-wr.top)*(wr.right-wr.left)==0) return 0;
		mem2dc=CreateCompatibleDC(hDest);
		mem2bmp=ske_CreateDIB32(w,wr.bottom-wr.top);
		h=wr.bottom-wr.top;
		oldbmp=(HBITMAP)SelectObject(mem2dc,mem2bmp);
#ifdef _DEBUG
		if (!oldbmp) 
			(NULL,"Tile bitmap not selected","ERROR", MB_OK);
#endif
		/// draw here
		{

			//fill temp bitmap
			{
				int y;
				int dy;
				dy=(wr.top-rFill->top)%(rGlyph->bottom-rGlyph->top);
				y=-dy;
				while (y<wr.bottom-wr.top)
				{

					ske_AlphaBlend(mem2dc,0,y,w,rGlyph->bottom-rGlyph->top, hSource,rGlyph->left,rGlyph->top,w,rGlyph->bottom-rGlyph->top,bf);                    
					y+=rGlyph->bottom-rGlyph->top;
				}

				//--    
				//end temp bitmap
				if (drawMode==0 || drawMode==2)
				{
					if (drawMode==0)
					{

						int dx;
						dx=(wr.left-rFill->left)%w;
						if (dx>=0)
						{   
							int wt;
							x=wr.left;
							wt=(x+w-dx<=wr.right)?(w-dx):(wr.right-wr.left);                                                   
							BitBlt(hDest,x,wr.top,wt,h,mem2dc,dx,0,SRCCOPY);
						}
						x=wr.left+w-dx;                        
						while (x<wr.right-w){
							BitBlt(hDest,x,wr.top,w,h,mem2dc,0,0,SRCCOPY);
							x+=w;
						}
						if (x<=wr.right)
						BitBlt(hDest,x,wr.top,wr.right-x,h, mem2dc,0,0,SRCCOPY);  
					}
					else 
					{
						int dx;
						dx=(wr.left-rFill->left)%w;
						x=wr.left-dx;
						while (x<wr.right-w){
							ske_AlphaBlend(hDest,x,wr.top,w,h,mem2dc,0,0,w,h,bf);
							x+=w;
						}
						if (x<=wr.right)
							ske_AlphaBlend(hDest,x,wr.top,wr.right-x,h, mem2dc,0,0,wr.right-x,h,bf);
					}

				}
				else
				{
					BLENDFUNCTION bf={AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };

					int dx;
					dx=(wr.left-rFill->left)%w;
					if (dx>=0)
					{
						int wt;
						x=wr.left;
						wt=(x+w-dx<=wr.right)?(w-dx):(wr.right-wr.left); 
						ske_AlphaBlend(hDest,x,wr.top,wt,h,mem2dc,dx,0,wt,h,bf);
					}
					x=wr.left+w-dx;
					while (x<wr.right-w){
						ske_AlphaBlend(hDest,x,wr.top,w,h,mem2dc,0,0,w,h,bf);
						x+=w;
					}
					if (x<=wr.right)
						ske_AlphaBlend(hDest,x,wr.top,wr.right-x,h, mem2dc,0,0,wr.right-x,h,bf);  

				}
			}

		}
		SelectObject(mem2dc,oldbmp);
		DeleteObject(mem2bmp);
		mod_DeleteDC(mem2dc);
	}
	return 1;

}

HBITMAP ske_CreateDIB32(int cx, int cy)
{
	return ske_CreateDIB32Point(cx,cy,NULL);
}

HBITMAP ske_CreateDIB32Point(int cx, int cy, void ** bits)
{
	BITMAPINFO RGB32BitsBITMAPINFO; 
	UINT * ptPixels;
	HBITMAP DirectBitmap;

	if ( cx < 0 || cy < 0 ) {
#ifdef _DEBUG
		DebugBreak();
#endif
		return NULL;
	}

	ZeroMemory(&RGB32BitsBITMAPINFO,sizeof(BITMAPINFO));
	RGB32BitsBITMAPINFO.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
	RGB32BitsBITMAPINFO.bmiHeader.biWidth=cx;//bm.bmWidth;
	RGB32BitsBITMAPINFO.bmiHeader.biHeight=cy;//bm.bmHeight;
	RGB32BitsBITMAPINFO.bmiHeader.biPlanes=1;
	RGB32BitsBITMAPINFO.bmiHeader.biBitCount=32;
	// pointer used for direct Bitmap pixels access


	DirectBitmap = CreateDIBSection(NULL, 
		(BITMAPINFO *)&RGB32BitsBITMAPINFO, 
		DIB_RGB_COLORS,
		(void **)&ptPixels, 
		NULL, 0);
	if ((DirectBitmap == NULL || ptPixels == NULL) && cx!= 0 && cy!=0) 
	{
#ifdef _DEBUG
		MessageBoxA(NULL,"Object not allocated. Check GDI object count","ERROR",MB_OK|MB_ICONERROR); 
		DebugBreak();
#endif
		;
	}
	else	memset(ptPixels,0,cx*cy*4);
	if (bits!=NULL) *bits=ptPixels;  
	return DirectBitmap;
}

HRGN ske_CreateOpaqueRgn(BYTE Level, bool Opaque)
{
	if (!g_pCachedWindow)
		return NULL;

	RGBQUAD * buf = (RGBQUAD *) g_pCachedWindow->hImageDIBByte;

	int x,y;
	unsigned int cRect = 64;
	PRGNDATA pRgnData = (PRGNDATA)malloc(sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT));
	memset(pRgnData, 0, sizeof(RGNDATAHEADER));
	pRgnData->rdh.dwSize = sizeof(RGNDATAHEADER);
	pRgnData->rdh.iType = RDH_RECTANGLES;

	for (y = 0; y < g_pCachedWindow->Height; ++y)
	{
		bool inside = false;
		bool lastin = false;
		unsigned int entry = 0;

		for (x = 0; x < g_pCachedWindow->Width; ++x)
		{
			inside = Opaque ? (buf->rgbReserved > Level) : (buf->rgbReserved < Level);
			++buf;

			if (inside != lastin)
			{
				if (inside)
				{
					lastin = true;
					entry = x;
				} else {
					if (pRgnData->rdh.nCount == cRect)
					{
						cRect = cRect + 64;
						pRgnData = (PRGNDATA)realloc(pRgnData, sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT));
					}
					SetRect(((LPRECT)pRgnData->Buffer) + pRgnData->rdh.nCount, entry, g_pCachedWindow->Height - y, x, g_pCachedWindow->Height - y + 1);

					pRgnData->rdh.nCount++;
					lastin = false;
				}
			}
		}

		if (lastin)
		{
			if (pRgnData->rdh.nCount == cRect)
			{
				cRect = cRect + 64;
				pRgnData = (PRGNDATA)realloc(pRgnData, sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT));
			}
			SetRect(((LPRECT)pRgnData->Buffer) + pRgnData->rdh.nCount, entry, g_pCachedWindow->Height - y, x, g_pCachedWindow->Height - y + 1);

			pRgnData->rdh.nCount++;
		}
	}
	HRGN hRgn = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + pRgnData->rdh.nCount*sizeof(RECT), (LPRGNDATA)pRgnData);
	free(pRgnData);

	return hRgn;
}

static int ske_DrawSkinObject(SKINDRAWREQUEST * preq, GLYPHOBJECT * pobj)
{
	HDC memdc=NULL, glyphdc=NULL;
	int k=0;
	//BITMAP bmp={0};
	HBITMAP membmp=0,oldbmp=0,oldglyph=0;
	BYTE Is32Bit=0;
	RECT PRect;
	POINT mode2offset={0};
	int depth=0; 
	int mode=0; //0-FastDraw, 1-DirectAlphaDraw, 2-BufferedAlphaDraw

	if (!(preq && pobj)) return -1;
	if ((!pobj->hGlyph || pobj->hGlyph==(HBITMAP)-1) && ((pobj->Style&7) ==ST_IMAGE ||(pobj->Style&7) ==ST_FRAGMENT|| (pobj->Style&7) ==ST_SOLARIZE)) return 0;
	// Determine painting mode   
	depth=GetDeviceCaps(preq->hDC,BITSPIXEL);
	depth=depth<16?16:depth;
	Is32Bit=pobj->bmBitsPixel==32;
	if ((!Is32Bit && pobj->dwAlpha==255)&& pobj->Style!=ST_BRUSH) mode=0;
	else if (pobj->dwAlpha==255 && pobj->Style!=ST_BRUSH) mode=1;
	else mode=2;
	// End painting mode

	//force mode

	if(preq->rcClipRect.bottom-preq->rcClipRect.top*preq->rcClipRect.right-preq->rcClipRect.left==0)
		preq->rcClipRect=preq->rcDestRect;
	IntersectRect(&PRect,&preq->rcDestRect,&preq->rcClipRect);
	if (IsRectEmpty(&PRect))
	{
		return 0;
	}
	if (mode==2)
	{   
		memdc=CreateCompatibleDC(preq->hDC);
		membmp=ske_CreateDIB32(PRect.right-PRect.left,PRect.bottom-PRect.top);
		oldbmp=(HBITMAP)SelectObject(memdc,membmp);
		if (oldbmp==NULL) 
		{
			if (mode==2)
			{
				SelectObject(memdc,oldbmp);
				mod_DeleteDC(memdc);
				DeleteObject(membmp);
			}
			return 0;
		}
	} 

	if (mode!=2) memdc=preq->hDC;
	{
		if (pobj->hGlyph && pobj->hGlyph!=(HBITMAP)-1)
		{
			glyphdc=CreateCompatibleDC(preq->hDC);
			if (!oldglyph)
				oldglyph=(HBITMAP)SelectObject(glyphdc,pobj->hGlyph);
			else 
				SelectObject(glyphdc,pobj->hGlyph);
		}
		// Drawing
		{    
			RECT rFill, rGlyph, rClip;
			if ((pobj->Style&7) ==ST_BRUSH)
			{
				HBRUSH br=CreateSolidBrush(pobj->dwColor);
				RECT fr;
				if (mode==2)
				{
					SetRect(&fr,0,0,PRect.right-PRect.left,PRect.bottom-PRect.top);
					FillRect(memdc,&fr,br);
					ske_SetRectOpaque(memdc,&fr);
					// FillRectAlpha(memdc,&fr,pobj->dwColor|0xFF000000);
				}
				else
				{
					fr=PRect;
					// SetRect(&fr,0,0,PRect.right-PRect.left,PRect.bottom-PRect.top);
					FillRect(preq->hDC,&fr,br);
				}
				DeleteObject(br);
				k=-1;
			}
			else
			{
				if (mode==2)  
				{
					mode2offset.x=PRect.left;
					mode2offset.y=PRect.top;
					OffsetRect(&PRect,-mode2offset.x,-mode2offset.y);
				}
				rClip=(preq->rcClipRect);

				{
					int lft=0;
					int top=0;
					int rgh=pobj->bmWidth;
					int btm=pobj->bmHeight;
					if ((pobj->Style&7) ==ST_FRAGMENT)
					{
						lft=pobj->clipArea.x;
						top=pobj->clipArea.y;
						rgh=min(rgh,lft+pobj->szclipArea.cx);
						btm=min(btm,top+pobj->szclipArea.cy);
					}

					// Draw center...
					if (1)
					{
						rFill.top=preq->rcDestRect.top+pobj->dwTop;
						rFill.bottom=preq->rcDestRect.bottom-pobj->dwBottom; 
						rFill.left=preq->rcDestRect.left+pobj->dwLeft;
						rFill.right=preq->rcDestRect.right-pobj->dwRight;

						if (mode==2)
							OffsetRect(&rFill,-mode2offset.x,-mode2offset.y);

						rGlyph.top=top+pobj->dwTop;
						rGlyph.left=lft+pobj->dwLeft;
						rGlyph.right=rgh-pobj->dwRight;
						rGlyph.bottom=btm-pobj->dwBottom;

						k+=ske_SkinFillRectByGlyph(memdc,glyphdc,&rFill,&rGlyph,&PRect,pobj->FitMode,mode,depth);
					}

					// Draw top side...
					if(1)
					{
						rFill.top=preq->rcDestRect.top;
						rFill.bottom=preq->rcDestRect.top+pobj->dwTop; 
						rFill.left=preq->rcDestRect.left+pobj->dwLeft;
						rFill.right=preq->rcDestRect.right-pobj->dwRight;

						if (mode==2)
							OffsetRect(&rFill,-mode2offset.x,-mode2offset.y);

						rGlyph.top=top+0;
						rGlyph.left=lft+pobj->dwLeft;
						rGlyph.right=rgh-pobj->dwRight;
						rGlyph.bottom=top+pobj->dwTop;

						k+=ske_SkinFillRectByGlyph(memdc,glyphdc,&rFill,&rGlyph,&PRect,pobj->FitMode&FM_TILE_HORZ,mode,depth);
					}
					// Draw bottom side...
					if(1)
					{
						rFill.top=preq->rcDestRect.bottom-pobj->dwBottom;
						rFill.bottom=preq->rcDestRect.bottom; 
						rFill.left=preq->rcDestRect.left+pobj->dwLeft;
						rFill.right=preq->rcDestRect.right-pobj->dwRight;

						if (mode==2)
							OffsetRect(&rFill,-mode2offset.x,-mode2offset.y);


						rGlyph.top=btm-pobj->dwBottom;
						rGlyph.left=lft+pobj->dwLeft;
						rGlyph.right=rgh-pobj->dwRight;
						rGlyph.bottom=btm;

						k+=ske_SkinFillRectByGlyph(memdc,glyphdc,&rFill,&rGlyph,&PRect,pobj->FitMode&FM_TILE_HORZ,mode,depth);
					}
					// Draw left side...
					if(1)
					{
						rFill.top=preq->rcDestRect.top+pobj->dwTop;
						rFill.bottom=preq->rcDestRect.bottom-pobj->dwBottom; 
						rFill.left=preq->rcDestRect.left;
						rFill.right=preq->rcDestRect.left+pobj->dwLeft;

						if (mode==2)
							OffsetRect(&rFill,-mode2offset.x,-mode2offset.y);


						rGlyph.top=top+pobj->dwTop;
						rGlyph.left=lft;
						rGlyph.right=lft+pobj->dwLeft;
						rGlyph.bottom=btm-pobj->dwBottom;

						k+=ske_SkinFillRectByGlyph(memdc,glyphdc,&rFill,&rGlyph,&PRect,pobj->FitMode&FM_TILE_VERT,mode,depth);
					}

					// Draw right side...
					if(1)
					{
						rFill.top=preq->rcDestRect.top+pobj->dwTop;
						rFill.bottom=preq->rcDestRect.bottom-pobj->dwBottom; 
						rFill.left=preq->rcDestRect.right-pobj->dwRight;
						rFill.right=preq->rcDestRect.right;

						if (mode==2)
							OffsetRect(&rFill,-mode2offset.x,-mode2offset.y);


						rGlyph.top=top+pobj->dwTop;
						rGlyph.left=rgh-pobj->dwRight;
						rGlyph.right=rgh;
						rGlyph.bottom=btm-pobj->dwBottom;

						k+=ske_SkinFillRectByGlyph(memdc,glyphdc,&rFill,&rGlyph,&PRect,pobj->FitMode&FM_TILE_VERT,mode,depth);
					}


					// Draw Top-Left corner...
					if(1)
					{
						rFill.top=preq->rcDestRect.top;
						rFill.bottom=preq->rcDestRect.top+pobj->dwTop; 
						rFill.left=preq->rcDestRect.left;
						rFill.right=preq->rcDestRect.left+pobj->dwLeft;

						if (mode==2)
							OffsetRect(&rFill,-mode2offset.x,-mode2offset.y);


						rGlyph.top=top;
						rGlyph.left=lft;
						rGlyph.right=lft+pobj->dwLeft;
						rGlyph.bottom=top+pobj->dwTop;

						k+=ske_SkinFillRectByGlyph(memdc,glyphdc,&rFill,&rGlyph,&PRect,0,mode,depth);
					}
					// Draw Top-Right corner...
					if(1)
					{
						rFill.top=preq->rcDestRect.top;
						rFill.bottom=preq->rcDestRect.top+pobj->dwTop; 
						rFill.left=preq->rcDestRect.right-pobj->dwRight;
						rFill.right=preq->rcDestRect.right;

						if (mode==2)
							OffsetRect(&rFill,-mode2offset.x,-mode2offset.y);


						rGlyph.top=top;
						rGlyph.left=rgh-pobj->dwRight;
						rGlyph.right=rgh;
						rGlyph.bottom=top+pobj->dwTop;

						k+=ske_SkinFillRectByGlyph(memdc,glyphdc,&rFill,&rGlyph,&PRect,0,mode,depth);
					}

					// Draw Bottom-Left corner...
					if(1)
					{
						rFill.top=preq->rcDestRect.bottom-pobj->dwBottom;
						rFill.bottom=preq->rcDestRect.bottom; 
						rFill.left=preq->rcDestRect.left;
						rFill.right=preq->rcDestRect.left+pobj->dwLeft;


						if (mode==2)
							OffsetRect(&rFill,-mode2offset.x,-mode2offset.y);


						rGlyph.left=lft;
						rGlyph.right=lft+pobj->dwLeft; 
						rGlyph.top=btm-pobj->dwBottom;
						rGlyph.bottom=btm;

						k+=ske_SkinFillRectByGlyph(memdc,glyphdc,&rFill,&rGlyph,&PRect,0,mode,depth);
					}
					// Draw Bottom-Right corner...
					if(1)
					{
						rFill.top=preq->rcDestRect.bottom-pobj->dwBottom;
						rFill.bottom=preq->rcDestRect.bottom;
						rFill.left=preq->rcDestRect.right-pobj->dwRight;
						rFill.right=preq->rcDestRect.right;


						if (mode==2)
							OffsetRect(&rFill,-mode2offset.x,-mode2offset.y);

						rGlyph.left=rgh-pobj->dwRight;
						rGlyph.right=rgh;
						rGlyph.top=btm-pobj->dwBottom;
						rGlyph.bottom=btm;

						k+=ske_SkinFillRectByGlyph(memdc,glyphdc,&rFill,&rGlyph,&PRect,0,mode,depth);
					}
				}

			}

			if ((k>0 || k==-1) && mode==2)
			{            
				{
					BLENDFUNCTION bf={AC_SRC_OVER, 0, /*(bm.bmBitsPixel==32)?255:*/pobj->dwAlpha, (pobj->bmBitsPixel==32 && pobj->Style!=ST_BRUSH)?AC_SRC_ALPHA:0};
					if (mode==2)
						OffsetRect(&PRect,mode2offset.x,mode2offset.y);
					ske_AlphaBlend( preq->hDC,PRect.left,PRect.top,PRect.right-PRect.left,PRect.bottom-PRect.top, 
						memdc,0,0,PRect.right-PRect.left,PRect.bottom-PRect.top,bf);
				}                 
			}
		}
		//free GDI resources
		//--++--

		//free GDI resources
		{

			if (oldglyph) SelectObject(glyphdc,oldglyph);
			if (glyphdc) mod_DeleteDC(glyphdc);
		}    
		if (mode==2)
		{
			SelectObject(memdc,oldbmp);
			mod_DeleteDC(memdc);
			DeleteObject(membmp);
		}

	}  
	if (pobj->plTextList && pobj->plTextList->realCount>0)
	{
		int i;
		HFONT hOldFont;
		for (i=0; i<pobj->plTextList->realCount; i++)
		{
			GLYPHTEXT * gt=(GLYPHTEXT *)pobj->plTextList->items[i];
			if (!gt->hFont)
			{
				if (gl_plSkinFonts && gl_plSkinFonts->realCount>0)
				{
					int j=0;
					for (j=0; j<gl_plSkinFonts->realCount; j++)
					{
						SKINFONT * sf;
						sf=(SKINFONT*)gl_plSkinFonts->items[j];
						if (sf->szFontID && !strcmp(sf->szFontID,gt->szFontID))
						{
							gt->hFont=sf->hFont;
							break;
						}
					}
				}
				if (!gt->hFont) gt->hFont=(HFONT)-1;
			}
			if (gt->hFont!=(HFONT)-1)
			{
				RECT rc={0};
				hOldFont=(HFONT)SelectObject(preq->hDC,gt->hFont);



				if (gt->RelativeFlags&2) rc.left=preq->rcDestRect.right+gt->iLeft;
				else if (gt->RelativeFlags&1) rc.left=((preq->rcDestRect.right-preq->rcDestRect.left)>>1)+gt->iLeft;
				else rc.left=preq->rcDestRect.left+gt->iLeft;

				if (gt->RelativeFlags&8) rc.top=preq->rcDestRect.bottom+gt->iTop;
				else if (gt->RelativeFlags&4) rc.top=((preq->rcDestRect.bottom-preq->rcDestRect.top)>>1)+gt->iTop;
				else rc.top=preq->rcDestRect.top+gt->iTop;

				if (gt->RelativeFlags&32) rc.right=preq->rcDestRect.right+gt->iRight;
				else if (gt->RelativeFlags&16) rc.right=((preq->rcDestRect.right-preq->rcDestRect.left)>>1)+gt->iRight;
				else rc.right=preq->rcDestRect.left+gt->iRight;

				if (gt->RelativeFlags&128) rc.bottom=preq->rcDestRect.bottom+gt->iBottom;
				else if (gt->RelativeFlags&64) rc.bottom=((preq->rcDestRect.bottom-preq->rcDestRect.top)>>1)+gt->iBottom;
				else rc.bottom=preq->rcDestRect.top+gt->iBottom;

				ske_AlphaTextOut(preq->hDC, gt->stText, -1, &rc,gt->dwFlags, gt->dwColor);
				SelectObject(preq->hDC,hOldFont);
			}
		}
	}

	return 0;
}



int ske_AddDescriptorToSkinObjectList (LPSKINOBJECTDESCRIPTOR lpDescr, SKINOBJECTSLIST* Skin)
{
	SKINOBJECTSLIST *sk;
	if (Skin) sk=Skin; else sk=&g_SkinObjectList;
	if (!sk) return 0;
	if (mir_bool_strcmpi(lpDescr->szObjectID,"_HEADER_")) return 0;
	{//check if new object allready presents.
		DWORD i=0;
		for (i=0; i<sk->dwObjLPAlocated;i++)
			if (!mir_strcmp(sk->pObjects[i].szObjectID,lpDescr->szObjectID)) return 0;
	}
	if (sk->dwObjLPAlocated+1>sk->dwObjLPReserved)
	{ // Realocated list to add space for new object

		sk->pObjects=(SKINOBJECTDESCRIPTOR*)mir_realloc(sk->pObjects,sizeof(SKINOBJECTDESCRIPTOR)*(sk->dwObjLPReserved+1)/*alloc step*/);
		sk->dwObjLPReserved++; 
	}
	{ //filling new objects field
		sk->pObjects[sk->dwObjLPAlocated].bType=lpDescr->bType;
		sk->pObjects[sk->dwObjLPAlocated].Data=NULL;
		sk->pObjects[sk->dwObjLPAlocated].szObjectID=mir_strdup(lpDescr->szObjectID);
		//  sk->Objects[sk->dwObjLPAlocated].szObjectName=mir_strdup(lpDescr->szObjectName);
		if (lpDescr->Data!=NULL)
		{   //Copy defaults values
			switch (lpDescr->bType) 
			{
			case OT_GLYPHOBJECT:
				{   
					GLYPHOBJECT * obdat;
					GLYPHOBJECT * gl=(GLYPHOBJECT*)lpDescr->Data;
					sk->pObjects[sk->dwObjLPAlocated].Data=mir_alloc(sizeof(GLYPHOBJECT));
					obdat=(GLYPHOBJECT*)sk->pObjects[sk->dwObjLPAlocated].Data;
					memmove(obdat,gl,sizeof(GLYPHOBJECT));
					if (gl->szFileName!=NULL)                    
					{
						obdat->szFileName=mir_strdup(gl->szFileName);
						mir_free_and_nill(gl->szFileName);
					}
					else
						obdat->szFileName=NULL;
					obdat->hGlyph=NULL;
					break;
				}
			}

		}
	}
	sk->dwObjLPAlocated++;
	return 1;
}

static LPSKINOBJECTDESCRIPTOR ske_FindObject(const char * szName, BYTE objType, SKINOBJECTSLIST* Skin)
{
	// DWORD i;
	SKINOBJECTSLIST* sk;
	sk=(Skin==NULL)?(&g_SkinObjectList):Skin;
	return skin_FindObjectByRequest((char *)szName,sk->pMaskList);
}

static LPSKINOBJECTDESCRIPTOR ske_FindObjectByMask(MODERNMASK * pModernMask, BYTE objType, SKINOBJECTSLIST* Skin)
{
	// DWORD i;
	SKINOBJECTSLIST* sk;
	sk=(Skin==NULL)?(&g_SkinObjectList):Skin;
	if (!sk->pMaskList) return NULL;
	return skin_FindObjectByMask(pModernMask,sk->pMaskList);
}

LPSKINOBJECTDESCRIPTOR ske_FindObjectByName(const char * szName, BYTE objType, SKINOBJECTSLIST* Skin)
{
	DWORD i;
	SKINOBJECTSLIST* sk;
	sk=(Skin==NULL)?(&g_SkinObjectList):Skin;
	for (i=0; i<sk->dwObjLPAlocated; i++)
	{
		if (sk->pObjects[i].bType==objType || objType==OT_ANY)
		{
			if (!mir_strcmp(sk->pObjects[i].szObjectID,szName))
				return &(sk->pObjects[i]);
		}
	}
	return NULL;
}

//////////////////////////////////////////////////////////////////////////
// Paint glyph
// wParam - LPSKINDRAWREQUEST
// lParam - possible direct pointer to modern mask
//////////////////////////////////////////////////////////////////////////

INT_PTR ske_Service_DrawGlyph(WPARAM wParam,LPARAM lParam)
{
	LPSKINDRAWREQUEST preq;
	LPSKINOBJECTDESCRIPTOR pgl;
	LPGLYPHOBJECT gl;
	if (!wParam) return -1;
	ske_LockSkin();
	__try
	{
		preq=(LPSKINDRAWREQUEST)wParam;   
		if (lParam)
			pgl=ske_FindObjectByMask((MODERNMASK*)lParam, OT_GLYPHOBJECT,NULL);
		else
			pgl=ske_FindObject(preq->szObjectID, OT_GLYPHOBJECT,NULL);
		if (pgl==NULL) return -1;
		if (pgl->Data==NULL) return -1;
		gl= (LPGLYPHOBJECT)pgl->Data;
		if ((gl->Style&7) ==ST_SKIP) return ST_SKIP;
		if (gl->hGlyph==NULL && gl->hGlyph!=(HBITMAP)-1 &&
			(  (gl->Style&7)==ST_IMAGE 
			||(gl->Style&7)==ST_FRAGMENT 
			||(gl->Style&7)==ST_SOLARIZE ))
			if (gl->szFileName) 
			{
				gl->hGlyph=ske_LoadGlyphImage(gl->szFileName);
				if (gl->hGlyph)
				{
					BITMAP bmp={0};
					GetObject(gl->hGlyph,sizeof(BITMAP),&bmp);
					gl->bmBitsPixel=(BYTE)bmp.bmBitsPixel;
					gl->bmHeight=bmp.bmHeight;
					gl->bmWidth=bmp.bmWidth;
				}
				else
					gl->hGlyph=(HBITMAP)-1; //invalid
			}
			return ske_DrawSkinObject(preq,gl);
	}
	__finally
	{
		ske_UnlockSkin();
	}   
	return -1;
}


void ske_PreMultiplyChanells(HBITMAP hbmp,BYTE Mult)
{
	BITMAP bmp;     
	BOOL flag=FALSE;
	BYTE * pBitmapBits;
	DWORD Len;
	int bh,bw,y,x;

	GetObject(hbmp, sizeof(BITMAP), (LPSTR)&bmp);
	bh=bmp.bmHeight;
	bw=bmp.bmWidth;
	Len=bh*bw*4;
	flag=(bmp.bmBits==NULL);
	if (flag)
	{
		pBitmapBits=(LPBYTE)malloc(Len);
		GetBitmapBits(hbmp,Len,pBitmapBits);
	}
	else 
		pBitmapBits=(BYTE*)bmp.bmBits;
	for (y=0; y<bh; ++y)
	{
		BYTE *pPixel= pBitmapBits + bw * 4 * y;

		for (x=0; x<bw ; ++x)
		{
			if (Mult)
			{
				pPixel[0]= pPixel[0]*pPixel[3]/255;
				pPixel[1]= pPixel[1]*pPixel[3]/255;
				pPixel[2]= pPixel[2]*pPixel[3]/255;
			}
			else
			{
				pPixel[3]=255;
			}
			pPixel+= 4;
		}
	}
	if (flag)
	{
		Len=SetBitmapBits(hbmp,Len,pBitmapBits);
		free (pBitmapBits);
	}
	return;
}

int ske_GetFullFilename(char * buf, char *file, char * skinfolder,BOOL madeAbsolute)
{
	char b2[MAX_PATH]={0};
	char *SkinPlace= ModernGetStringA(NULL,SKIN,"SkinFolder");
	if (!SkinPlace) SkinPlace=mir_strdup("\\Skin\\default");
	if (file[0]!='\\' && file[1]!=':') 
		_snprintf(b2, MAX_PATH,"%s\\%s",(skinfolder==NULL)?SkinPlace:((INT_PTR)skinfolder!=-1)?skinfolder:"",file);
	else
		_snprintf(b2, MAX_PATH,"%s",file);
	if (madeAbsolute) 
		if (b2[0]=='\\' && b2[1]!='\\')
			CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)(b2+1), (LPARAM)buf);
		else
			CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)(b2), (LPARAM)buf);
	else
		memcpy(buf,b2,MAX_PATH);

	if(SkinPlace) mir_free_and_nill(SkinPlace);
	return 0;
}


static HBITMAP ske_skinLoadGlyphImage(char * szFileName)
{
	if (!g_CluiData.fGDIPlusFail && !wildcmpi(szFileName,"*.tga"))
		return GDIPlus_LoadGlyphImage(szFileName);
	else 
		return ske_LoadGlyphImageByDecoders(szFileName);
}

/* 
This function is required to load TGA to dib buffer myself 
Major part of routines is from http://tfcduke.developpez.com/tutoriel/format/tga/fichiers/tga.c
*/

static BOOL ske_ReadTGAImageData(void * From, DWORD fromSize, BYTE * destBuf, DWORD bufSize, BOOL RLE)
{
	BYTE * pos=destBuf;
	BYTE * from=fromSize?(BYTE*)From:NULL;
	FILE * fp=!fromSize?(FILE*)From:NULL;
	DWORD destCount=0;
	DWORD fromCount=0;
	if (!RLE)
	{
		while (((from&&fromCount<fromSize) || (fp&& fromCount<bufSize))
			&&(destCount<bufSize))
		{
			BYTE r=from?from[fromCount++]:(BYTE)fgetc(fp);
			BYTE g=from?from[fromCount++]:(BYTE)fgetc(fp);
			BYTE b=from?from[fromCount++]:(BYTE)fgetc(fp);
			BYTE a=from?from[fromCount++]:(BYTE)fgetc(fp);
			pos[destCount++]=r;
			pos[destCount++]=g;
			pos[destCount++]=b;
			pos[destCount++]=a;

			if (destCount>bufSize) break;
			if (from) 	if (fromCount<fromSize) break;
		}
	}
	else
	{
		BYTE rgba[4];
		BYTE packet_header;
		BYTE *ptr=pos;
		BYTE size;
		int i;
		while (ptr < pos + bufSize)
		{
			/* read first byte */
			packet_header = from?from[fromCount]:(BYTE)fgetc(fp);
			if (from) from++;
			size = 1 + (packet_header & 0x7f);
			if (packet_header & 0x80)
			{
				/* run-length packet */
				if (from) 
				{
					*((DWORD*)rgba)=*((DWORD*)(from+fromCount));
					fromCount+=4;
				}
				else fread (rgba, sizeof (BYTE), 4, fp);
				for (i = 0; i < size; ++i, ptr += 4)
				{
					ptr[2] = rgba[2];
					ptr[1] = rgba[1];
					ptr[0] = rgba[0];
					ptr[3] = rgba[3];
				}
			}
			else
			{	/* not run-length packet */
				for (i = 0; i < size; ++i, ptr += 4)
				{
					ptr[0] = from? from[fromCount++]:(BYTE)fgetc (fp);
					ptr[1] = from? from[fromCount++]:(BYTE)fgetc (fp);
					ptr[2] = from? from[fromCount++]:(BYTE)fgetc (fp);
					ptr[3] = from? from[fromCount++]:(BYTE)fgetc (fp);
				}
			}
		}
	}
	return TRUE;
}

static HBITMAP ske_LoadGlyphImage_TGA(char * szFilename)
{
	BYTE *colormap = NULL;
	int cx=0,cy=0;
	BOOL err=FALSE;
	tga_header_t header;
	if (!szFilename) return NULL;
	if (!wildcmpi(szFilename,"*\\*%.tga")) 
	{
		//Loading TGA image from file
		FILE *fp;
		fp = fopen (szFilename, "rb");
		if (!fp)
		{
			TRACEVAR("error: couldn't open \"%s\"!\n", szFilename);
			return NULL;
		}
		/* read header */
		fread (&header, sizeof (tga_header_t), 1, fp);
		if (  (header.pixel_depth!=32)
			||((header.image_type!=10)&&(header.image_type!=2))
			)
		{
			fclose(fp);
			return NULL;
		}

		/*memory allocation */
		colormap=(BYTE*)malloc(header.width*header.height*4);	
		cx=header.width;
		cy=header.height;
		fseek (fp, header.id_lenght, SEEK_CUR);
		fseek (fp, header.cm_length, SEEK_CUR);
		err=!ske_ReadTGAImageData((void*)fp, 0, colormap, header.width*header.height*4,header.image_type==10);
		fclose(fp);
	}


	else 
	{
		/* reading from resources IDR_TGA_DEFAULT_SKIN */
		DWORD size=0;
		BYTE * mem;
		HGLOBAL hRes;
		HRSRC hRSrc=FindResourceA(g_hInst,MAKEINTRESOURCEA(IDR_TGA_DEFAULT_SKIN),"TGA");
		if (!hRSrc) return NULL;
		hRes=LoadResource(g_hInst,hRSrc);
		if (!hRes) return NULL;
		size=SizeofResource(g_hInst,hRSrc);
		mem=(BYTE*) LockResource(hRes);
		if (size>sizeof(header))
		{
			tga_header_t * header=(tga_header_t *)mem;
			if (header->pixel_depth==32&& (header->image_type==2 ||header->image_type==10))
			{
				colormap=(BYTE*)malloc(header->width*header->height*4);	
				cx=header->width;
				cy=header->height;
				ske_ReadTGAImageData((void*)(mem+sizeof(tga_header_t)+header->id_lenght+header->cm_length), size-(sizeof(tga_header_t)+header->id_lenght+header->cm_length), colormap, cx*cy*4,header->image_type==10);
			}
		}
		FreeResource(hRes);
	}
	if (colormap)  //create dib section
	{
		BYTE * pt;
		HBITMAP hbmp=ske_CreateDIB32Point(cx,cy,(void**)&pt);
		if (hbmp) memcpy(pt,colormap,cx*cy*4);
		free(colormap);
		return hbmp;
	}
	return NULL;
}


//this function is required to load PNG to dib buffer myself
HBITMAP ske_LoadGlyphImage_Png2Dib(char * szFilename)
{

	{
		HANDLE hFile, hMap = NULL;
		BYTE* ppMap = NULL;
		long  cbFileSize = 0;
		BITMAPINFOHEADER* pDib;
		BYTE* pDibBits;

		if ( !ServiceExists( MS_PNG2DIB )) {
			MessageBox( NULL, TranslateT( "You need an image services plugin to process PNG images." ), TranslateT( "Error" ), MB_OK );
			return (HBITMAP)NULL;
		}

		if (( hFile = CreateFileA( szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL )) != INVALID_HANDLE_VALUE )
			if (( hMap = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL )) != NULL )
				if (( ppMap = ( BYTE* )MapViewOfFile( hMap, FILE_MAP_READ, 0, 0, 0 )) != NULL )
					cbFileSize = GetFileSize( hFile, NULL );

		if ( cbFileSize != 0 ) {
			PNG2DIB param;
			param.pSource = ppMap;
			param.cbSourceSize = cbFileSize;
			param.pResult = &pDib;
			if ( CallService( MS_PNG2DIB, 0, ( LPARAM )&param ))
				pDibBits = ( BYTE* )( pDib+1 );
			else
				cbFileSize = 0;
		}

		if ( ppMap != NULL )	UnmapViewOfFile( ppMap );
		if ( hMap  != NULL )	CloseHandle( hMap );
		if ( hFile != NULL ) CloseHandle( hFile );

		if ( cbFileSize == 0 )
			return (HBITMAP)NULL;

		{
			BITMAPINFO* bi=( BITMAPINFO* )pDib;
			BYTE *pt=(BYTE*)bi;
			pt+=bi->bmiHeader.biSize;
			if (bi->bmiHeader.biBitCount!=32)
			{
				HDC sDC = GetDC( NULL );
				HBITMAP hBitmap = CreateDIBitmap( sDC, pDib, CBM_INIT, pDibBits, bi, DIB_PAL_COLORS );
				SelectObject( sDC, hBitmap );
				DeleteDC( sDC );
				GlobalFree( pDib );
				return hBitmap;
			}
			else
			{
				BYTE * ptPixels=pt;
				HBITMAP hBitmap=CreateDIBSection(NULL,bi, DIB_RGB_COLORS, (void **)&ptPixels,  NULL, 0);         
				memcpy(ptPixels,pt,bi->bmiHeader.biSizeImage);
				GlobalFree( pDib );
				return hBitmap;
			}	
		}
	}	
}

static HBITMAP ske_LoadGlyphImageByDecoders(char * szFileName)
{
	// Loading image from file by imgdecoder...    
	HBITMAP hBitmap=NULL;
	char ext[5];
	BYTE f=0;
	LPBYTE pBitmapBits;
	LPVOID pImg= NULL;
	LPVOID m_pImgDecoder;

	BITMAP bmpInfo;
	{
		int l;
		l=mir_strlen(szFileName);
		memmove(ext,szFileName +(l-4),5);   
	}
	if (!strchr(szFileName,'%') && !PathFileExistsA(szFileName)) return NULL;
	if (mir_bool_strcmpi(ext,".tga"))
	{
		hBitmap=ske_LoadGlyphImage_TGA(szFileName);
		f=1;
	}
	else if (ServiceExists("Image/Png2Dib") && mir_bool_strcmpi(ext,".png"))
	{
		hBitmap=ske_LoadGlyphImage_Png2Dib(szFileName);
		GetObject(hBitmap, sizeof(BITMAP), &bmpInfo);
		f=(bmpInfo.bmBits!=NULL);
		// hBitmap=(HBITMAP)CallService(MS_UTILS_LOADBITMAP,0,(LPARAM)szFileName);
		// f=1;

	}
	else if (hImageDecoderModule==NULL || !mir_bool_strcmpi(ext,".png"))
		hBitmap=(HBITMAP)CallService(MS_UTILS_LOADBITMAP,0,(LPARAM)szFileName);
	else
	{
		f=1;
		ImgNewDecoder(&m_pImgDecoder);
		if (!ImgNewDIBFromFile(m_pImgDecoder, szFileName, &pImg))	
		{
			ImgGetHandle(pImg, &hBitmap, (LPVOID *)&pBitmapBits);
			ImgDeleteDecoder(m_pImgDecoder);
		}
	}
	if (hBitmap)
	{

		GetObject(hBitmap, sizeof(BITMAP), &bmpInfo);
		if (bmpInfo.bmBitsPixel == 32)	
			ske_PreMultiplyChanells(hBitmap,f);
		else
		{
			HDC dc24,dc32;
			HBITMAP hBitmap32,obmp24,obmp32;
			dc32=CreateCompatibleDC(NULL);
			dc24=CreateCompatibleDC(NULL);
			hBitmap32=ske_CreateDIB32(bmpInfo.bmWidth,bmpInfo.bmHeight);
			obmp24=(HBITMAP)SelectObject(dc24,hBitmap);
			obmp32=(HBITMAP)SelectObject(dc32,hBitmap32);
			BitBlt(dc32,0,0,bmpInfo.bmWidth,bmpInfo.bmHeight,dc24,0,0,SRCCOPY);
			SelectObject(dc24,obmp24);
			SelectObject(dc32,obmp32);
			mod_DeleteDC(dc24);
			mod_DeleteDC(dc32);
			DeleteObject(hBitmap);
			hBitmap=hBitmap32;
			ske_PreMultiplyChanells(hBitmap,0);
		}

	}
	return hBitmap; 
}

HBITMAP ske_LoadGlyphImage(char * szFileName)
{
	// try to find image in loaded
	DWORD i;
	HBITMAP hbmp;
	char szFile [MAX_PATH]={0};	
	ske_GetFullFilename(szFile,szFileName,g_SkinObjectList.szSkinPlace,TRUE);
	ske_LockSkin();
	if (pLoadedImages)
	{
		for (i=0; i<dwLoadedImagesCount; i++)
		{
			if (mir_bool_strcmpi(pLoadedImages[i].szFileName,szFile))
			{
				pLoadedImages[i].dwLoadedTimes++;
				ske_UnlockSkin();
				return pLoadedImages[i].hGlyph;
			}
		}
	}
	// load new image
	hbmp=ske_skinLoadGlyphImage(szFile);
	if (hbmp==NULL) 
	{
		ske_UnlockSkin();
		return NULL;
	}
	// add to loaded list
	if (dwLoadedImagesCount+1>dwLoadedImagesAlocated)
	{
		pLoadedImages=(GLYPHIMAGE*)mir_realloc(pLoadedImages,sizeof(GLYPHIMAGE)*(dwLoadedImagesCount+1));
		if (pLoadedImages) dwLoadedImagesAlocated++;
		else 
		{
			ske_UnlockSkin();
			return NULL;
		}
	}
	pLoadedImages[dwLoadedImagesCount].dwLoadedTimes=1;
	pLoadedImages[dwLoadedImagesCount].hGlyph=hbmp;
	pLoadedImages[dwLoadedImagesCount].szFileName=mir_strdup(szFile);
	dwLoadedImagesCount++;
	ske_UnlockSkin();
	return hbmp;
}
int ske_UnloadGlyphImage(HBITMAP hbmp)
{
	DWORD i;
	for (i=0; i<dwLoadedImagesCount; i++)
	{
		if (hbmp==pLoadedImages[i].hGlyph)
		{
			pLoadedImages[i].dwLoadedTimes--;
			if (pLoadedImages[i].dwLoadedTimes==0)
			{
				LPGLYPHIMAGE gl=&(pLoadedImages[i]);
				if (gl->szFileName) mir_free_and_nill(gl->szFileName);
				memmove(&(pLoadedImages[i]),&(pLoadedImages[i+1]),sizeof(GLYPHIMAGE)*(dwLoadedImagesCount-i-1));
				dwLoadedImagesCount--;
				DeleteObject(hbmp);
				if (pLoadedImages && dwLoadedImagesCount==0) 
				{
					dwLoadedImagesAlocated=0;
					mir_free_and_nill(pLoadedImages);
				}
			}
			return 0;
		}

	}
	DeleteObject(hbmp);
	return 0;
}

int ske_UnloadSkin(SKINOBJECTSLIST * Skin)
{   

	DWORD i;
	ske_LockSkin();
	ClearMaskList(Skin->pMaskList);
	{//clear font list
		int i;
		if (gl_plSkinFonts && gl_plSkinFonts->realCount>0)
		{
			for (i=0; i<gl_plSkinFonts->realCount; i++)
			{
				SKINFONT * sf=(SKINFONT *)gl_plSkinFonts->items[i];
				if (sf)
				{
					if (sf->szFontID) mir_free_and_nill(sf->szFontID);
					DeleteObject(sf->hFont);
					mir_free_and_nill(sf);
				}
			}
			li.List_Destroy(gl_plSkinFonts);
			mir_free_and_nill(gl_plSkinFonts);
		}
	}

	if (Skin->szSkinPlace) mir_free_and_nill(Skin->szSkinPlace);
	if (Skin->pTextList) li.List_Destroy(Skin->pTextList);
	mir_free_and_nill(Skin->pTextList);
	ModernSkinButtonDeleteAll();
	if (Skin->dwObjLPAlocated==0) { ske_UnlockSkin(); return 0;}
	for (i=0; i<Skin->dwObjLPAlocated; i++)
	{
		switch(Skin->pObjects[i].bType)
		{
		case OT_GLYPHOBJECT:
			{
				GLYPHOBJECT * dt;
				dt=(GLYPHOBJECT*)Skin->pObjects[i].Data;
				if (dt->hGlyph && dt->hGlyph!=(HBITMAP)-1) 
					ske_UnloadGlyphImage(dt->hGlyph);
				dt->hGlyph=NULL;
				if (dt->szFileName) mir_free_and_nill(dt->szFileName);
				{// delete texts
					int i;
					if (dt->plTextList && dt->plTextList->realCount>0)
					{
						for (i=0; i<dt->plTextList->realCount; i++)
						{
							GLYPHTEXT * gt=(GLYPHTEXT *)dt->plTextList->items[i];
							if (gt)
							{
								if (gt->stText)       mir_free_and_nill(gt->stText);
								if (gt->stValueText)  mir_free_and_nill(gt->stValueText);
								if (gt->szFontID)     mir_free_and_nill(gt->szFontID);
								if (gt->szGlyphTextID)mir_free_and_nill(gt->szGlyphTextID);
								mir_free_and_nill(gt);
							}
						}
						li.List_Destroy(dt->plTextList);
						mir_free_and_nill(dt->plTextList);
					}
				}
				mir_free_and_nill(dt);
			}
			break;
		} 
		if (Skin->pObjects[i].szObjectID) mir_free_and_nill(Skin->pObjects[i].szObjectID); 

	}
	mir_free_and_nill(Skin->pObjects);
	Skin->pTextList=NULL;
	Skin->dwObjLPAlocated=0;
	Skin->dwObjLPReserved=0;
	ske_UnlockSkin();
	return 0;
}

static void RegisterMaskByParce(const char * szSetting, char * szValue, SKINOBJECTSLIST * pSkin)
{
	int i;
	DWORD ID=atoi(szSetting+1);
	for (i=0; i<mir_strlen(szValue); i++)  if (szValue[i]==':') break;
	if (i<mir_strlen(szValue))
	{
		char * Obj, *Mask;
		int res;
		Mask=szValue+i+1;
		Obj=(char*)mir_alloc(i+1);
		strncpy(Obj,szValue,i);
		Obj[i]='\0';
		res=AddStrModernMaskToList(ID,Mask,Obj,pSkin->pMaskList,pSkin);
		mir_free_and_nill(Obj);
	}
}

static int ske_ProcessLoadindString(const char * szSetting, char *szValue)
{	
	if (!pCurrentSkin) return 0;
	if (szSetting[0]=='$')
		RegisterObjectByParce((char *)szSetting, szValue);
	else if (szSetting[0]=='#')
		RegisterButtonByParce((char *)szSetting,szValue);
	else if (szSetting[0]=='@')
		RegisterMaskByParce((char *)szSetting,  szValue, pCurrentSkin); ///
	else if (szSetting[0]=='t')
		ske_AddParseTextGlyphObject((char*)szSetting,szValue,pCurrentSkin);
	else if (szSetting[0]=='f')
		ske_AddParseSkinFont((char*)szSetting,szValue,pCurrentSkin);
	else return 0;
	return 1;
}
static int ske_enumdb_SkinObjectsProc (const char *szSetting,LPARAM lParam)
{   
	char *value;
	value= ModernGetStringA(NULL,SKIN,szSetting);
	ske_ProcessLoadindString(szSetting,value);
	mir_free_and_nill(value);

	return 0;
}

static int ske_SortTextGlyphObjectFunc(void * first, void * second)
{
	return strcmp(((GLYPHTEXT*)(((int*)first)[0]))->szGlyphTextID,((GLYPHTEXT*)(((int*)second)[0]))->szGlyphTextID);
}

static void ske_LinkSkinObjects(SKINOBJECTSLIST * pObjectList)
{
	DWORD i;
	// LINK Mask with objects
	for (i=0; i<pObjectList->pMaskList->dwMaskCnt; i++)
	{
		MODERNMASK *mm=&(pObjectList->pMaskList->pl_Masks[i]);
		void * pObject=(void*) ske_FindObjectByName(mm->szObjectName, OT_ANY, (SKINOBJECTSLIST*) pObjectList);
		mir_free_and_nill(mm->szObjectName);
		mm->bObjectFound=TRUE;
		mm->pObject=pObject;
	}	

	if (pObjectList->pTextList)
	{
		int i;
		// LINK Text with objects
		for (i=0; i<pObjectList->pTextList->realCount; i++)
		{
			GLYPHTEXT * glText;
			GLYPHOBJECT *globj=NULL;
			SKINOBJECTDESCRIPTOR * lpobj;
			glText=(GLYPHTEXT *)pObjectList->pTextList->items[i];
			lpobj=ske_FindObjectByName(glText->szObjectName,OT_GLYPHOBJECT, pObjectList);
			mir_free_and_nill(glText->szObjectName);
			if (lpobj)
				globj=(GLYPHOBJECT*)lpobj->Data;
			if (globj)
			{
				if (!globj->plTextList)
				{
					globj->plTextList=li.List_Create(0,1);
					globj->plTextList->sortFunc=ske_SortTextGlyphObjectFunc;
				}
				li.List_Insert(globj->plTextList,(void*)glText,globj->plTextList->realCount);     
				qsort(globj->plTextList->items,globj->plTextList->realCount,sizeof(void*),(int(*)(const void*, const void*))globj->plTextList->sortFunc);
				pObjectList->pTextList->items[i]=NULL;
			}
			else
			{
				GLYPHTEXT * gt=glText;
				if (gt)
				{
					if (gt->stText)       mir_free_and_nill(gt->stText);
					if (gt->stValueText)  mir_free_and_nill(gt->stValueText);
					if (gt->szFontID)     mir_free_and_nill(gt->szFontID);
					if (gt->szGlyphTextID)mir_free_and_nill(gt->szGlyphTextID);
					mir_free_and_nill(gt);
				}
			}
		}
		li.List_Destroy(pObjectList->pTextList);
		mir_free_and_nill(pObjectList->pTextList);
	}
}
// Getting skin objects and masks from DB
static int ske_GetSkinFromDB(char * szSection, SKINOBJECTSLIST * Skin)
{
	if (Skin==NULL) return 0;
	ske_UnloadSkin(Skin);
	g_CluiData.fDisableSkinEngine=ModernGetSettingByte(NULL,"ModernData","DisableEngine", SETTING_DISABLESKIN_DEFAULT);
	//window borders
	if (g_CluiData.fDisableSkinEngine) {
		g_CluiData.LeftClientMargin=0;
		g_CluiData.RightClientMargin=0; 
		g_CluiData.TopClientMargin=0;
		g_CluiData.BottomClientMargin=0;
	} else {
		//window borders
		g_CluiData.LeftClientMargin=(int)ModernGetSettingByte(NULL,"CLUI","LeftClientMargin",SETTING_LEFTCLIENTMARIGN_DEFAULT);
		g_CluiData.RightClientMargin=(int)ModernGetSettingByte(NULL,"CLUI","RightClientMargin",SETTING_RIGHTCLIENTMARIGN_DEFAULT); 
		g_CluiData.TopClientMargin=(int)ModernGetSettingByte(NULL,"CLUI","TopClientMargin",SETTING_TOPCLIENTMARIGN_DEFAULT);
		g_CluiData.BottomClientMargin=(int)ModernGetSettingByte(NULL,"CLUI","BottomClientMargin",SETTING_BOTTOMCLIENTMARIGN_DEFAULT);
	}

	if (g_CluiData.fDisableSkinEngine) return 0;

	Skin->pMaskList=(LISTMODERNMASK*)mir_alloc(sizeof(LISTMODERNMASK));
	memset(Skin->pMaskList,0,sizeof(LISTMODERNMASK));
	Skin->szSkinPlace= ModernGetStringA(NULL,SKIN,"SkinFolder");
	if (!Skin->szSkinPlace || (strchr(Skin->szSkinPlace, '%') && !ModernGetSettingByte(NULL,SKIN,"Modified",0))) 
	{
		BOOL bOnlyObjects=FALSE;
		if (Skin->szSkinPlace && strchr(Skin->szSkinPlace, '%'))
			bOnlyObjects=TRUE;
		mir_free(Skin->szSkinPlace);
		Skin->szSkinPlace=mir_strdup("%Default%");
		ske_LoadSkinFromResource( bOnlyObjects );
	}
	//Load objects
	{
		DBCONTACTENUMSETTINGS dbces;
		pCurrentSkin=Skin;
		dbces.pfnEnumProc=ske_enumdb_SkinObjectsProc;
		dbces.szModule=SKIN;
		dbces.ofsSettings=0;
		CallService(MS_DB_CONTACT_ENUMSETTINGS,0,(LPARAM)&dbces);

		SortMaskList(pCurrentSkin->pMaskList);
		ske_LinkSkinObjects(pCurrentSkin);
	}
	//Load Masks
	return 0;
}

//surrogate to be called from outside
void ske_LoadSkinFromDB(void) 
{ 
	ske_GetSkinFromDB(SKIN,&g_SkinObjectList); 
	g_CluiData.dwKeyColor=ModernGetSettingDword(NULL,"ModernSettings","KeyColor",(DWORD)SETTING_KEYCOLOR_DEFAULT);
}



//


/*
static BOOL ske_ParseLineOfIniFile(char * Line, BOOL bOnlyObjects)
{
	DWORD i=0;
	DWORD len=strlen(Line);
	while (i<len && (Line[i]==' ' || Line[i]=='\t')) i++; //skip spaces&tabs
	if (i>=len) return FALSE; //only spaces (or tabs)
	if (len>0 && Line[len-1]==10) Line[len-1]='\0';
	switch(Line[i])
	{
	case ';':
		return FALSE; // start of comment is found
	case '[':
		//New section start here
		if (iniCurrentSection) mir_free_and_nill(iniCurrentSection);
		{
			char *tbuf=Line+i+1;		
			DWORD len2=strlen(tbuf);
			DWORD k=len2;
			while (k>0 && tbuf[k]!=']') k--; //searching close bracket
			tbuf[k]='\0';   //closing string
			if (k==0) return FALSE;
			iniCurrentSection=mir_strdup(tbuf);
		}
		return TRUE;
	default:
		if (!iniCurrentSection) return FALSE;  //param found out of section
		{
			char *keyName=Line+i;
			char *keyValue=Line+i;

			DWORD eqPlace=0;
			DWORD len2=strlen(keyName);
			while (eqPlace<len2 && keyName[eqPlace]!='=') eqPlace++; //find '='
			if (eqPlace==0 || eqPlace==len2) return FALSE; //= not found or no key name
			keyName[eqPlace]='\0';
			keyValue=keyName+eqPlace+1;
			//remove tail spaces in Name
			{
				DWORD len3=strlen(keyName);
				int j=len3-1;
				while (j>0 && (keyName[j]==' ' || keyName[j]=='\t')) j--;
				if (j>=0) keyName[j+1]='\0';
			}		
			//remove start spaces in Value
			{
				DWORD len3=strlen(keyValue);
				DWORD j=0;
				while (j<len3 && (keyValue[j]==' ' || keyValue[j]=='\t')) j++;
				if (j<len3) keyValue+=j;
			}
			//remove tail spaces in Value
			{
				DWORD len3=strlen(keyValue);
				int j=len3-1;
				while (j>0 && (keyValue[j]==' ' || keyValue[j]=='\t')) j--;
				if (j>=0) keyValue[j+1]='\0';
			}
			ske_WriteParamToDatabase(iniCurrentSection,keyName,keyValue,TRUE);
		}
	}
	return FALSE;
}
*/
//Load data from ini file



//int ske_OldLoadSkinFromIniFile(char * szFileName)
//{
//	char bsn[MAXSN_BUFF_SIZE];
//	char * Buff;
//
//	int i=0;
//	int f=0;
//	int ReadingSection=0;
//	char AllowedSection[260];
//	int AllowedAll=0;
//	char t2[MAX_PATH];
//	char t3[MAX_PATH];
//
//	DWORD retu=GetPrivateProfileSectionNamesA(bsn,MAXSN_BUFF_SIZE,szFileName);
//	ske_DeleteAllSettingInSection("ModernSkin");
//	ske_GetSkinFolder(szFileName,t2);
//	ModernWriteSettingString(NULL,SKIN,"SkinFolder",t2);
//	CallService(MS_UTILS_PATHTORELATIVE, (WPARAM)szFileName, (LPARAM)t3);
//	ModernWriteSettingString(NULL,SKIN,"SkinFile",t3);
//	Buff=bsn;
//	AllowedSection[0]=0;
//	do         
//	{
//		f=mir_strlen(Buff);
//		if (f>0 && !mir_bool_strcmpi(Buff,"Skin_Description_Section"))
//		{
//			char b3[MAX_BUFF_SIZE];
//			DWORD ret=0;
//			ret=GetPrivateProfileSectionA(Buff,b3,MAX_BUFF_SIZE,szFileName);
//			if (ret>MAX_BUFF_SIZE-3) continue;
//			if (ret==0) continue;
//			{
//				DWORD p=0;
//				char *s1;
//				char *s2;
//				char *s3;
//				{
//					DWORD t;
//					BOOL LOCK=FALSE;
//					for (t=0; t<ret-1;t++)
//					{
//						if (b3[t]=='\0') LOCK=FALSE;
//						if (b3[t]=='=' && !LOCK) 
//						{
//							b3[t]='\0';
//							LOCK=TRUE;
//						}
//					}
//				}
//				do
//				{
//					s1=b3+p;
//
//					s2=s1+mir_strlen(s1)+1;
//					switch (s2[0])
//					{
//					case 'b':
//						{
//							BYTE P;
//							//                            char ba[255];
//							s3=s2+1;
//							P=(BYTE)atoi(s3);
//							ModernWriteSettingByte(NULL,Buff,s1,P);
//						}
//						break;
//					case 'w':
//						{
//							WORD P;
//							//                           char ba[255];
//							s3=s2+1;
//							P=(WORD)atoi(s3);
//							ModernWriteSettingWord(NULL,Buff,s1,P);
//						}break;
//					case 'd':
//						{
//							DWORD P;
//
//							s3=s2+1;
//							P=(DWORD)atoi(s3);
//							ModernWriteSettingDword(NULL,Buff,s1,P);
//						}break;
//					case 's':
//						{
//							//                          char ba[255];
//							char bb[255];
//							s3=s2+1;
//							strncpy(bb,s3,sizeof(bb));
//							ModernWriteSettingString(NULL,Buff,s1,s3);
//						}break;
//					case 'f': //file
//						{
//							//                         char ba[255];
//							char bb[255];
//
//							s3=s2+1;
//							{
//								char fn[MAX_PATH];
//								int pp, i;
//								pp=-1;
//								CallService(MS_UTILS_PATHTORELATIVE, (WPARAM)szFileName, (LPARAM)fn);
//								{
//									for (i=0; i<mir_strlen(fn); i++)  if (fn[i]=='.') pp=i;
//									if (pp!=-1)
//									{
//										fn[pp]='\0';
//									}
//								}                      
//								sprintf(bb,"%s\\%s",fn,s3);
//								ModernWriteSettingString(NULL,Buff,s1,bb);
//							}
//						}break;
//					}
//					p=p+mir_strlen(s1)+mir_strlen(s2)+2;
//				} while (p<ret);
//
//			}
//		}
//		Buff+=mir_strlen(Buff)+1;
//	}while (((DWORD)Buff-(DWORD)bsn)<retu);
//	return 0;
//}
//



static int ske_LoadSkinFromResource(BOOL bOnlyObjects)
{

	IniParser parser(g_hInst, MAKEINTRESOURCEA(IDR_MSF_DEFAULT_SKIN), "MSF", bOnlyObjects ? IniParser::FLAG_ONLY_OBJECTS : IniParser::FLAG_WITH_SETTINGS );
	if ( !parser.CheckOK()) return 0;

	ske_DeleteAllSettingInSection("ModernSkin");
	ModernWriteSettingString(NULL,SKIN,"SkinFolder","%Default%");
	ModernWriteSettingString(NULL,SKIN,"SkinFile","%Default%");
	parser.Parse( IniParser::WriteStrToDb, 0 );	
	return 0;
}

//Load data from ini file
int ske_LoadSkinFromIniFile(TCHAR * szFileName, BOOL bOnlyObjects)
{
	TCHAR skinFolder[MAX_PATH]={0};
	TCHAR skinFile[MAX_PATH]={0};
	if (_tcschr(szFileName,_T('%'))) 
		return ske_LoadSkinFromResource( bOnlyObjects );

	IniParser parser( szFileName, bOnlyObjects ? IniParser::FLAG_ONLY_OBJECTS : IniParser::FLAG_WITH_SETTINGS  );
	if ( !parser.CheckOK()) return 0;

	ske_DeleteAllSettingInSection("ModernSkin");
	IniParser::GetSkinFolder(szFileName,skinFolder);
	CallService(MS_UTILS_PATHTORELATIVET, (WPARAM)szFileName, (LPARAM)skinFile);

	ModernWriteSettingTString(NULL,SKIN,"SkinFolder", skinFolder);
	ModernWriteSettingTString(NULL,SKIN,"SkinFile", skinFile);

	parser.Parse( IniParser::WriteStrToDb, 1 );

	return 0;
}


static int ske_enumdb_SkinSectionDeletionProc (const char *szSetting,LPARAM lParam)
{

	if (szSetting==NULL){return(0);};
	nArrayLen++;
	pszSettingName=(char **)realloc(pszSettingName,nArrayLen*sizeof(char *));
	pszSettingName[nArrayLen-1]=_strdup(szSetting);
	return(0);
};
static int ske_DeleteAllSettingInSection(char * SectionName)
{
	DBCONTACTENUMSETTINGS dbces;
	nArrayLen=0;
	pszSettingName=NULL;
	dbces.pfnEnumProc=ske_enumdb_SkinSectionDeletionProc;
	dbces.szModule=SectionName;
	dbces.ofsSettings=0;

	CallService(MS_DB_CONTACT_ENUMSETTINGS,0,(LPARAM)&dbces);

	//delete all settings
	if (nArrayLen==0){return(0);};
	{
		int i;
		for (i=0;i<nArrayLen;i++)
		{
			ModernDeleteSetting(0,SectionName,pszSettingName[i]);
			free(pszSettingName[i]);
		};
		free(pszSettingName);
		pszSettingName=NULL;
		nArrayLen=0;    
	};
	return(0);
};


BOOL ske_TextOutA(HDC hdc, int x, int y, char * lpString, int nCount)
{
	TCHAR *buf=(TCHAR *)mir_alloc((2+nCount)*sizeof(TCHAR));
	BOOL res;
	MultiByteToWideChar(CallService( MS_LANGPACK_GETCODEPAGE, 0, 0 ), 0, lpString, -1, buf, (2+nCount)*sizeof(TCHAR)); 
	res=ske_TextOut(hdc,x,y,buf,nCount);
	mir_free_and_nill(buf);
	return res;
}

BOOL ske_TextOut(HDC hdc, int x, int y, LPCTSTR lpString, int nCount)
{
	int ta;
	SIZE sz;
	RECT rc={0};
	if (!g_CluiData.fGDIPlusFail &&0) ///text via gdi+
	{
		TextOutWithGDIp(hdc,x,y,lpString,nCount);
		return 0;
	}
	else

	{
		// return TextOut(hdc, x,y,lpString,nCount);
		GetTextExtentPoint32(hdc,lpString,nCount,&sz);
		ta=GetTextAlign(hdc);
		SetRect(&rc,x,y,x+sz.cx,y+sz.cy);
		ske_DrawText(hdc,lpString,nCount,&rc,DT_NOCLIP|DT_SINGLELINE|DT_LEFT);
	}
	return 1;
}

static INT_PTR ske_Service_AlphaTextOut(WPARAM wParam,LPARAM lParam)
{
	if (!wParam) return 0;
	{
		AlphaTextOutParams ap=*(AlphaTextOutParams*)wParam;
		return ske_AlphaTextOut(ap.hDC,ap.lpString,ap.nCount,ap.lpRect,ap.format,ap.ARGBcolor);
	}
}

static __inline void ske_SetMatrix( sbyte * matrix,
										  sbyte a, sbyte b, sbyte c, 
										  sbyte d, sbyte e, sbyte f, 
										  sbyte g, sbyte h, sbyte i)
{
	matrix[0]=a;	matrix[1]=b;	matrix[2]=c;
	matrix[3]=d;	matrix[4]=e;	matrix[5]=f;
	matrix[6]=g;	matrix[7]=h;	matrix[8]=i;
}

static void ske_SetTextEffect(BYTE EffectID, DWORD FirstColor, DWORD SecondColor)
{
	if (EffectID>MAXPREDEFINEDEFFECTS) return; 
	if (EffectID==-1) meCurrentEffect.EffectID=-1;
	else
	{
		meCurrentEffect.EffectID=EffectID;
		meCurrentEffect.EffectMatrix=ModernEffectsEnum[EffectID];
		meCurrentEffect.EffectColor1=FirstColor;
		meCurrentEffect.EffectColor2=SecondColor;
	}
}

BOOL ske_ResetTextEffect(HDC hdc)
{
	int i;
	if (!pEffectStack || !pEffectStack->realCount) return TRUE;
	for (i=0; i<pEffectStack->realCount; i++)
		if (pEffectStack->items[i] && ((EFFECTSSTACKITEM*)(pEffectStack->items[i]))->hdc==hdc)
		{
			EFFECTSSTACKITEM * effect=(EFFECTSSTACKITEM*)(pEffectStack->items[i]);
			mir_free_and_nill(effect);
			li.List_Remove(pEffectStack,i);
			return TRUE;
		}
		return FALSE;
};

BOOL ske_SelectTextEffect(HDC hdc, BYTE EffectID, DWORD FirstColor, DWORD SecondColor)
{
	if (EffectID>MAXPREDEFINEDEFFECTS) return 0; 
	if (EffectID==-1) return ske_ResetTextEffect(hdc);
	if (!pEffectStack)
	{
		pEffectStack=li.List_Create(0,1);
	}
	{
		int i;
		for (i=0; i<pEffectStack->realCount; i++)
			if (pEffectStack->items[i] && ((EFFECTSSTACKITEM*)(pEffectStack->items[i]))->hdc==hdc)
			{
				EFFECTSSTACKITEM * effect=(EFFECTSSTACKITEM*)(pEffectStack->items[i]);
				effect->EffectID=EffectID;		    
				effect->FirstColor=FirstColor;
				effect->SecondColor=SecondColor;
				return TRUE;
			}
	}
	{
		EFFECTSSTACKITEM * effect=(EFFECTSSTACKITEM *) mir_alloc(sizeof(EFFECTSSTACKITEM));
		effect->hdc=hdc;
		effect->EffectID=EffectID;
		effect->FirstColor=FirstColor;
		effect->SecondColor=SecondColor;
		li.List_Insert(pEffectStack, effect, 0);
		return TRUE;
	}	  
	return FALSE;
}

static BOOL ske_GetTextEffect(HDC hdc, MODERNEFFECT * modernEffect)
{
	int i=0;
	if (!pEffectStack || !pEffectStack->realCount) return FALSE;
	if (!modernEffect) return FALSE;
	for (i=0; i<pEffectStack->realCount; i++)
		if (pEffectStack->items[i] && ((EFFECTSSTACKITEM*)(pEffectStack->items[i]))->hdc==hdc)
		{
			EFFECTSSTACKITEM * effect=(EFFECTSSTACKITEM*)(pEffectStack->items[i]);
			modernEffect->EffectID=effect->EffectID;		    
			modernEffect->EffectColor1=effect->FirstColor;
			modernEffect->EffectColor2=effect->SecondColor;
			modernEffect->EffectMatrix=ModernEffectsEnum[effect->EffectID];
			return TRUE;
		}
		return FALSE;
}

static BOOL ske_DrawTextEffect(BYTE* destPt,BYTE* maskPt, DWORD width, DWORD height, MODERNEFFECT *effect)
{
	sbyte *buf;
	sbyte *outbuf;
	sbyte *bufline, *buflineTop, *buflineMid;
	int sign=0;
	BYTE *maskline,*destline;
	BYTE al,rl,gl,bl,ad,rd,gd,bd;
	int k=0;
	DWORD x,y;
	sbyte *matrix;
	BYTE mcTopStart;
	BYTE mcBottomEnd;
	BYTE mcLeftStart;
	BYTE mcRightEnd;
	BYTE effectCount;
	int minX=width;
	int maxX=0;
	int minY=height;
	int maxY=0;
	if (effect->EffectID==0xFF) return FALSE;
	if (!width || ! height) return FALSE;
	if (!destPt) return FALSE;
	buf=(sbyte*)malloc(width*height*sizeof(BYTE));
	{
		matrix=effect->EffectMatrix.matrix;
		mcTopStart=2-effect->EffectMatrix.topEffect;
		mcBottomEnd=3+effect->EffectMatrix.bottomEffect;
		mcLeftStart=2-effect->EffectMatrix.leftEffect;
		mcRightEnd=3+effect->EffectMatrix.rightEffect;
		effectCount=effect->EffectMatrix.cycleCount;
	}
	al=255-((BYTE)(effect->EffectColor1>>24));
	rl=GetRValue(effect->EffectColor1);
	gl=GetGValue(effect->EffectColor1);
	bl=GetBValue(effect->EffectColor1);
	rd=GetRValue(effect->EffectColor2);
	gd=GetGValue(effect->EffectColor2);
	bd=GetBValue(effect->EffectColor2);
	ad=255-((BYTE)(effect->EffectColor2>>24));
	rd=GetRValue(effect->EffectColor2);
	gd=GetGValue(effect->EffectColor2);
	bd=GetBValue(effect->EffectColor2);

	//Fill buffer by mid values of image
	for (y=0; y<height; y++)
	{
		bufline=buf+y*width;
		maskline=maskPt+((y*width)<<2);
		for (x=0; x<width; x++)
		{
			BYTE a=(sbyte)(DWORD)((maskline[0]+maskline[2]+maskline[1]+maskline[1])>>4);
			*bufline=a;
			if (a!=0)
			{
				minX=min((int)x,minX);
				minY=min((int)y,minY);
				maxX=max((int)x,maxX);
				maxY=max((int)y,maxY);
			}
			bufline++;
			maskline+=4;
		}
	}
	//Here perform effect on buffer and place results to outbuf
	for (k=0; k<(effectCount&0x7F); k++)
	{
		minX = max( 0,           minX + mcLeftStart - 2 );
		minY = max( 0,           minY + mcTopStart  - 2 );
		maxX = min( (int)width,  maxX + mcRightEnd  - 1 );
		maxY = min( (int)height, maxY + mcBottomEnd - 1 );

		outbuf=(sbyte*)malloc(width*height*sizeof(sbyte));
		memset(outbuf,0,width*height*sizeof(sbyte));
		for (y=(DWORD)minY; y<(DWORD)maxY; y++)
		{
			int val;
			bufline=outbuf+y*width+minX;
			buflineMid=buf+y*width+minX; 
			for (x=(DWORD)minX; x<(DWORD)maxX; x++)
			{			
				int matrixHor,matrixVer;
				val=0;			
				for (matrixVer=mcTopStart; matrixVer<mcBottomEnd; matrixVer++)
				{
					int buflineStep = width*(matrixVer-2);
					int as=y+matrixVer-2;
					sbyte * buflineTopS=NULL;
					if (as>=0 && (DWORD)as<height) buflineTopS=buflineMid+buflineStep;

					for (matrixHor=mcLeftStart; matrixHor<mcRightEnd;matrixHor++)
					{						
						int a=as;
						buflineTop = buflineTopS;
						a=x+matrixHor-2;
						if (buflineTop && a>=0 && (DWORD)a<width) buflineTop+=matrixHor-2;
						else buflineTop=NULL;
						if (buflineTop) 
							val+=((*buflineTop)*matrix[matrixVer*5+matrixHor]); 					
					}
				}
				val=(val+1)>>5;
				*bufline=(sbyte)((val>127)?127:(val<-125)?-125:val);
				bufline++;
				buflineMid++;
			}
		}
		free(buf);
		buf=outbuf;
	}
	{
		BYTE r1,b1,g1,a1;
		b1=bl; r1=rl; g1=gl; a1=al; sign=1;
		//perform out to dest
		for (y=0; y<height; y++)
		{		
			bufline=buf+y*width;
			destline=destPt+((y*width)<<2);
			for (x=0; x<width; x++)
			{
				sbyte val=*bufline;
				BYTE absVal=((val<0)?-val:val);

				if (val!=0)
				{	
					if (val>0 && sign<0)
					{ b1=bl; r1=rl; g1=gl; a1=al; sign=1;}
					else if (val<0 && sign>0)
					{ b1=bd; r1=rd; g1=gd; a1=ad; sign=-1;}

					absVal=absVal*a1/255;				

					destline[0]=((destline[0]*(128-absVal))+absVal*b1)>>7;
					destline[1]=((destline[1]*(128-absVal))+absVal*g1)>>7;
					destline[2]=((destline[2]*(128-absVal))+absVal*r1)>>7;
					destline[3]+=((255-destline[3])*(a1*absVal))/32640;				
				}
				bufline++;
				destline+=4;
			}
		}
		free(buf);
	}
	return FALSE;
}

static int ske_AlphaTextOut (HDC hDC, LPCTSTR lpString, int nCount, RECT * lpRect, UINT format, DWORD ARGBcolor)
{
	if ( !( lpString && lpRect ))
	{
		DebugBreak();
		return 0;
	}

	// Step first fill fast calc correction tables:
	static bool _tables_empty = true;
	static BYTE gammaTbl[256];			// Gamma correction table
	static WORD blueMulTbl[256];		// blue  coefficient multiplication table
	static WORD greenMulTbl[256];		// green coefficient multiplication table
	static WORD redMulTbl[256];			// red   coefficient multiplication table
	if ( _tables_empty )
	{
		// fill tables
		double gammaCfPw = 1000 / (double)ModernGetSettingRangedWord(NULL,"ModernData","AlphaTextOutGamma", 700, 1, 5000 );
		BYTE blueCf    = ModernGetSettingByte(NULL,"ModernData","AlphaTextOutBlueCorrection", 28 );
		BYTE redCf     = ModernGetSettingByte(NULL,"ModernData","AlphaTextOutRed Correction", 77 );
		BYTE greenCf   = ModernGetSettingByte(NULL,"ModernData","AlphaTextOutGreen Correction", 151 );

		for ( int i = 0; i < 256; i++ )
		{
			gammaTbl[i]= (BYTE)( 255 * pow( (double)i / 255, gammaCfPw ));
			blueMulTbl[i] = i * blueCf;
			redMulTbl[i] = i * redCf;
			greenMulTbl[i] = i * greenCf;
		}
	}

	// Calc len of input string
	if ( nCount == -1 ) nCount = lstrlen( lpString );

	// retrieve destination bitmap bits
	HBITMAP hDestBitmap = (HBITMAP) GetCurrentObject( hDC, OBJ_BITMAP );
	BITMAP  bmpDest;
	GetObject( hDestBitmap, sizeof(BITMAP), &bmpDest );

	BOOL destHasNotDIB = FALSE;
	BYTE * pDestBits = NULL;
	if ( bmpDest.bmBits == NULL )
	{
		destHasNotDIB = TRUE;
		pDestBits = (BYTE*) malloc ( bmpDest.bmHeight * bmpDest.bmWidthBytes );
		GetBitmapBits( hDestBitmap, bmpDest.bmHeight*bmpDest.bmWidthBytes, pDestBits );
	}
	else 
		pDestBits=(BYTE*)bmpDest.bmBits;

	BOOL isDest16bit=( bmpDest.bmBitsPixel ) != 32;


	// Creating offscreen buffer
	HDC hOffscreenDC = CreateCompatibleDC( hDC );

	// Font to be used to draw text
	HFONT hFont      = (HFONT)GetCurrentObject( hDC, OBJ_FONT);
	HFONT hOldOffscreenFont = (HFONT)SelectObject( hOffscreenDC, hFont );

	// Calculating text geometric size
	RECT workRect = *lpRect;
	int  workRectWidth  = workRect.right - workRect.left;
	int  workRectHeight = workRect.bottom - workRect.top;
	
	if ( workRectWidth <= 0 || workRectHeight <= 0 )
		return 0;

	SIZE textSize;
	GetTextExtentPoint32( hOffscreenDC, lpString, nCount, &textSize );

	LPCTSTR lpWorkString = lpString;
	BOOL bNeedFreeWorkString = FALSE;

	// if we need to cut the text with ellipsis
	if ( ( format&DT_END_ELLIPSIS ) && textSize.cx > workRectWidth )
	{
		// Calc geometric width of ellipsis
		SIZE szEllipsis;
		GetTextExtentPoint32A(hOffscreenDC,"...",3,&szEllipsis);
		szEllipsis.cx++;                                          // CORRECTION: some width correction

		// Calc count of visible chars
		int visibleCharCount = nCount;
		if ( workRectWidth > szEllipsis.cx)
		{
			GetTextExtentExPoint( hOffscreenDC, lpString, nCount,
			                      workRectWidth - szEllipsis.cx,
			                      &visibleCharCount, NULL, &textSize );
		}
		else
		{
			GetTextExtentExPoint( hOffscreenDC, lpString, nCount,
			                     0, &visibleCharCount, NULL, &textSize);
		}

		// replace end of string by elipsis
		bNeedFreeWorkString = TRUE;
		lpWorkString = (TCHAR*) malloc( ( visibleCharCount + 4) * sizeof(TCHAR));
		
		memcpy( (void*) lpWorkString, lpString, visibleCharCount * sizeof(TCHAR));
		memcpy( (void*) ( lpWorkString + visibleCharCount ), _T("..."), 4 * sizeof(TCHAR)); // 3 + 1
		
		nCount = visibleCharCount + 3;
	}

	// Calc sizes and offsets

	textSize.cx += 2;             // CORRECTION: for italic

	int drx = 0;                  // x-axis offset of draw point

	if ( workRectWidth > textSize.cx )
	{
		if ( format & ( DT_RIGHT | DT_RTLREADING )) 
		{
			drx = workRectWidth - textSize.cx;
		}
		else if ( format & DT_CENTER ) 
		{
			drx = ( workRectWidth - textSize.cx ) >> 1;
		}
	}
	else
	{
		textSize.cx = workRectWidth;
	}

	int dry = 0;                  // y-axis offset of draw point

	if ( workRectHeight > textSize.cy )
	{
		if ( format & DT_BOTTOM ) 
		{
			dry = workRectHeight - textSize.cy;
		}
		else if ( format & DT_VCENTER ) 
		{
			dry = ( workRectHeight - textSize.cy ) >> 1;
		}
	}
	else
	{
		textSize.cy = workRectHeight;
	}

	textSize.cx += 4;    // CORRECTION: for effects ???
	textSize.cy += 4;	 // CORRECTION: for effects ???

	if ( textSize.cx > 0 && textSize.cy > 0 )     // Ok we need to paint
	{
		// probably here are mess ofscreen and temp buff dc

		//Create bitmap image for offscreen
		BYTE * bits     = NULL;
		HBITMAP hbmp    = ske_CreateDIB32Point( textSize.cx, textSize.cy, (void**)&bits );
		if ( bits != NULL ) 
		{
			HBITMAP holdbmp = (HBITMAP)SelectObject( hOffscreenDC, hbmp );

			//Create buffer bitmap image for temp text
			BYTE *  bufbits  = NULL;
			HBITMAP bufbmp   = ske_CreateDIB32Point( textSize.cx, textSize.cy, (void**)&bufbits );
			if ( bufbits != NULL )
			{
				HDC     bufDC     = CreateCompatibleDC( hDC );
				HBITMAP bufoldbmp = (HBITMAP)SelectObject( bufDC, bufbmp );
				HFONT   hOldBufFont = (HFONT)SelectObject( bufDC, hFont );
				SetBkColor( bufDC,  RGB( 0, 0, 0 ));
				SetTextColor( bufDC,RGB(255,255,255));
				
				// Copy from destination to temp buffer
				BitBlt( hOffscreenDC, 0, 0, textSize.cx, textSize.cy, hDC, workRect.left + drx - 2, workRect.top + dry - 2, SRCCOPY );

				//Draw text on offscreen bitmap
				TextOut( bufDC, 2, 2, lpWorkString, nCount);

				MODERNEFFECT effect;
				if (ske_GetTextEffect( hDC, &effect )) 
					ske_DrawTextEffect( bits, bufbits, textSize.cx, textSize.cy, &effect );	

				//RenderText
				RECT drawRect;
				drawRect.left = 0; drawRect.top = 0;
				drawRect.right = textSize.cx;
				drawRect.bottom = textSize.cy;

				DWORD x,y;
				DWORD width=textSize.cx;
				DWORD heigh=textSize.cy;

				BYTE * pDestScanLine;
				BYTE * pBufScanLine;
				BYTE * pix;
				BYTE * bufpix;

				BYTE al = 255 - ((BYTE)( ARGBcolor >> 24 ));
				BYTE r = GetRValue( ARGBcolor );
				BYTE g = GetGValue( ARGBcolor );
				BYTE b = GetBValue( ARGBcolor );      

				for ( y = 2; y < heigh - 2; y++ )
				{
					int lineBytes = y * (width<<2);
					
					pDestScanLine = bits + lineBytes;
					pBufScanLine  = bufbits + lineBytes;
					
					for ( x = 2; x < width - 2; x++)
					{
						pix    = pDestScanLine + ( x<<2 );
						bufpix = pBufScanLine  + ( x<<2 );

						// Monochromatic
						BYTE bx = gammaTbl[ bufpix[0] ];
						BYTE gx = gammaTbl[ bufpix[1] ];
						BYTE rx = gammaTbl[ bufpix[2] ];

						if ( al != 255 )
						{
							bx*=al/255;
							gx*=al/255;
							rx*=al/255;
						}
						BYTE ax = (BYTE)( ( (DWORD)rx*77 + (DWORD)gx * 151 + (DWORD)bx *28 + 128 ) / 256 );

						if (ax)
						{
							//Normalize components to gray
							BYTE axx = 255 - ( ( r + g + b ) >> 2 ) ; // Coefficient of grayance, more white font - more gray edges
							WORD atx = ax * (255 - axx);
							bx=( atx + bx * axx )/255;
							gx=( atx + gx * axx )/255;
							rx=( atx + rx * axx )/255;

							short rrx, grx, brx;
							brx=(short)((b-pix[0])*bx/255);
							grx=(short)((g-pix[1])*gx/255);
							rrx=(short)((r-pix[2])*rx/255);

							pix[0]+=brx;
							pix[1]+=grx;
							pix[2]+=rrx;
							pix[3]=(BYTE)(ax+(BYTE)(255-ax)*pix[3]/255);
						}
					}
				}

				//Blit to destination
				BitBlt( hDC, workRect.left + drx - 2, workRect.top + dry - 2, textSize.cx, textSize.cy, hOffscreenDC, 0, 0, SRCCOPY );

				//free resources
				SelectObject(bufDC,bufoldbmp);
				DeleteObject(bufbmp);
				SelectObject(bufDC, hOldBufFont );
				mod_DeleteDC(bufDC);
			}
			SelectObject(hOffscreenDC,holdbmp);
			DeleteObject(hbmp);
		}
	}

	// Final cleanup 
	SelectObject( hOffscreenDC, hOldOffscreenFont );
	DeleteDC( hOffscreenDC ); 
	
	if ( destHasNotDIB )
		free( pDestBits );
	
	if ( bNeedFreeWorkString )
		free( (void*)lpWorkString );
	
	return 0;
}

static int ske_DrawTextWithEffectWorker( HDC hdc, LPCTSTR lpString, int nCount, RECT * lpRect, UINT format, MODERNFONTEFFECT * effect )
{     
    if (format&DT_CALCRECT) return DrawText(hdc,lpString,nCount,lpRect,format);

    if (format&DT_RTLREADING) SetTextAlign(hdc,TA_RTLREADING);
    DWORD color=GetTextColor(hdc);

    RECT r=*lpRect;	
    OffsetRect(&r,1,1);
    DWORD form = format;
    if ( effect && effect->effectIndex )
        ske_SelectTextEffect( hdc, effect->effectIndex - 1, effect->baseColour, effect->secondaryColour );

    int res = ske_AlphaTextOut(hdc, lpString, nCount, lpRect, form, color );

    if ( effect && effect->effectIndex )
        ske_ResetTextEffect( hdc );

    return res;
}

INT_PTR ske_Service_DrawTextWithEffect( WPARAM wParam, LPARAM lParam )
{
    DrawTextWithEffectParam * p = ( DrawTextWithEffectParam * ) wParam;
    if ( p->cbSize != sizeof(DrawTextWithEffectParam))
        return FALSE;
    return ske_DrawTextWithEffectWorker( p->hdc, p->lpchText, p->cchText, p->lprc, p->dwDTFormat, p->pEffect );
}

BOOL ske_DrawTextA(HDC hdc, char * lpString, int nCount, RECT * lpRect, UINT format)
{
	TCHAR *buf=mir_a2u(lpString);
	BOOL res;
	res=ske_DrawText(hdc,buf,nCount,lpRect,format);
	mir_free(buf);
	return res;
}


BOOL ske_DrawText(HDC hdc, LPCTSTR lpString, int nCount, RECT * lpRect, UINT format)
{
	DWORD form=0, color=0;
	RECT r=*lpRect;	
	OffsetRect(&r,1,1);
	if (format&DT_RTLREADING) SetTextAlign(hdc,TA_RTLREADING);
	if (format&DT_CALCRECT) return DrawText(hdc,lpString,nCount,lpRect,format);
	if (format&DT_FORCENATIVERENDER || g_CluiData.fDisableSkinEngine) 
		return DrawText(hdc,lpString,nCount,lpRect,format&~DT_FORCENATIVERENDER);
	form=format;
	color=GetTextColor(hdc);
	if (!g_CluiData.fGDIPlusFail &&0) ///text via gdi+
	{
		TextOutWithGDIp(hdc,lpRect->left,lpRect->top,lpString,nCount);
		return 0;
	}
	return ske_AlphaTextOut(hdc,lpString,nCount,lpRect,form,color);
}

HICON ske_ImageList_GetIcon(HIMAGELIST himl, int i, UINT fStyle)
{
	IMAGEINFO imi={0};
	BITMAP bm={0};
	if ( IsWinVerXPPlus()  && i!=-1 )
	{
		ImageList_GetImageInfo(himl,i,&imi);
		GetObject(imi.hbmImage,sizeof(bm),&bm);
		if (bm.bmBitsPixel==32) //stupid bug of Microsoft 
			// Icons bitmaps are not premultiplied
			// So Imagelist_AddIcon - premultiply alpha
			// But incorrect - it is possible that alpha will
			// be less than color and
			// ImageList_GetIcon will return overflowed colors
			// TODO: Direct draw Icon from imagelist without 
			// extracting of icon
		{
			BYTE * bits=NULL;	
			bits=(BYTE*)bm.bmBits;
			if (!bits)
			{
				bits=(BYTE*)malloc(bm.bmWidthBytes*bm.bmHeight);
				GetBitmapBits(imi.hbmImage,bm.bmWidthBytes*bm.bmHeight,bits);
			} 
			{
				int iy;
				BYTE *bcbits;
				int wb=((imi.rcImage.right-imi.rcImage.left)*bm.bmBitsPixel>>3);
				bcbits=bits+(bm.bmHeight-imi.rcImage.bottom)*bm.bmWidthBytes+(imi.rcImage.left*bm.bmBitsPixel>>3);
				for (iy=0; iy<imi.rcImage.bottom-imi.rcImage.top; iy++)
				{
					int x;
					// Dummy microsoft fix - alpha can be less than r,g or b
					// Looks like color channels in icons should be non-premultiplied with alpha
					// But AddIcon store it premultiplied (incorrectly cause can be Alpha==7F, but R,G or B==80
					// So i check that alpha is 0x7F and set it to 0x80
					DWORD *c=((DWORD*)bcbits);
					for (x=0;x<imi.rcImage.right-imi.rcImage.left; x++)
					{		  
						DWORD val=*c;
						BYTE a= (BYTE)((val)>>24);
						if (a!=0)
						{
							BYTE r= (BYTE)((val&0xFF0000)>>16);
							BYTE g= (BYTE)((val&0xFF00)>>8);
							BYTE b= (BYTE)(val&0xFF);
							if (a<r || a<g || a<b)
							{
								a=max(max(r,g),b);
								val=a<<24|r<<16|g<<8|b;
								*c=val;
							}
						}
						c++;
					}
					bcbits+=bm.bmWidthBytes;
				}
			}  
			if (!bm.bmBits) 
			{ 
				SetBitmapBits(imi.hbmImage,bm.bmWidthBytes*bm.bmHeight,bits);
				free(bits);
			}
		}
	}
	return ImageList_GetIcon(himl,i,ILD_NORMAL);
}

////////////////////////////////////////////////////////////////////////////
// This creates new dib image from Imagelist icon ready to alphablend it 

HBITMAP ske_ExtractDIBFromImagelistIcon( HIMAGELIST himl,int index, int * outWidth, int * outHeight)
{
	return NULL;
}

BOOL ske_ImageList_DrawEx( HIMAGELIST himl,int i,HDC hdcDst,int x,int y,int dx,int dy,COLORREF rgbBk,COLORREF rgbFg,UINT fStyle)
{    
	//the routine to directly draw icon from image list without creating icon from there - should be some faster

	if (i<0) return FALSE;

	if (g_CluiData.fDisableSkinEngine)
		return ImageList_DrawEx( himl, i, hdcDst, x, y, dx, dy, rgbBk, rgbFg, fStyle);

	BYTE alpha;
	if (fStyle&ILD_BLEND25) alpha=64;
	else if (fStyle&ILD_BLEND50) alpha=128;
	else alpha=255;

	HICON hIcon=ske_ImageList_GetIcon(himl,i,ILD_NORMAL);        
	if (hIcon) 
	{
		ske_DrawIconEx(hdcDst,x,y,hIcon,dx?dx:GetSystemMetrics(SM_CXSMICON),dy?dy:GetSystemMetrics(SM_CYSMICON),0,NULL,DI_NORMAL|(alpha<<24));
		DestroyIcon(hIcon);
		return TRUE;
	}
	return FALSE;
}


static INT_PTR ske_Service_DrawIconEx(WPARAM wParam,LPARAM lParam)
{
	DrawIconFixParam *p=(DrawIconFixParam*)wParam;
	if (!p) return 0;
	return ske_DrawIconEx(p->hdc,p->xLeft,p->yTop,p->hIcon,p->cxWidth,p->cyWidth,p->istepIfAniCur,p->hbrFlickerFreeDraw,p->diFlags);
}


BOOL ske_DrawIconEx(HDC hdcDst,int xLeft,int yTop,HICON hIcon,int cxWidth,int cyWidth, UINT istepIfAniCur, HBRUSH hbrFlickerFreeDraw, UINT diFlags)
{

	ICONINFO ici;
	BYTE alpha=(BYTE)((diFlags&0xFF000000)>>24);


	HDC imDC;
	HBITMAP oldBmp, imBmp,tBmp;
	BITMAP imbt,immaskbt;
	BYTE * imbits;
	BYTE * imimagbits;
	BYTE * immaskbits;
	DWORD cx,cy,icy;
	BYTE *t1, *t2, *t3;

	BOOL NoDIBImage=FALSE;
	//lockimagelist
	BYTE hasmask=FALSE;
	BYTE no32bit=FALSE;
	BYTE noMirrorMask=FALSE;
	BYTE hasalpha=FALSE;
	alpha=alpha?alpha:255;

	if ( g_CluiData.fDisableSkinEngine && !(diFlags&0x80))
		return DrawIconEx(hdcDst,xLeft,yTop,hIcon,cxWidth,cyWidth,istepIfAniCur,hbrFlickerFreeDraw,diFlags&0xFFFF7F);

	if (!GetIconInfo(hIcon,&ici))  return 0;

	GetObject(ici.hbmColor,sizeof(BITMAP),&imbt);
	if (imbt.bmWidth*imbt.bmHeight==0)
	{
		DeleteObject(ici.hbmColor);
		DeleteObject(ici.hbmMask);
		return 0;
	}
	GetObject(ici.hbmMask,sizeof(BITMAP),&immaskbt);
	cy=imbt.bmHeight;

	if (imbt.bmBitsPixel!=32)
	{
		HDC tempDC1;
		HBITMAP otBmp;
		no32bit=TRUE;
		tempDC1=CreateCompatibleDC(hdcDst);
		tBmp=ske_CreateDIB32(imbt.bmWidth,imbt.bmHeight);
		if (tBmp) 
		{
			GetObject(tBmp,sizeof(BITMAP),&imbt);
			otBmp=(HBITMAP)SelectObject(tempDC1,tBmp);
			DrawIconEx(tempDC1,0,0,hIcon,imbt.bmWidth,imbt.bmHeight,istepIfAniCur,hbrFlickerFreeDraw,DI_IMAGE);   
			noMirrorMask=TRUE;

		}
		SelectObject(tempDC1,otBmp);
		mod_DeleteDC(tempDC1);
	}
	/*
	if (imbt.bmBitsPixel!=32)
	{
	HDC tempDC1;
	HBITMAP otBmp;
	no32bit=TRUE;
	tempDC1=ske_RequestBufferDC(hdcDst,BUFFER_DRAWICON,imbt.bmWidth,imbt.bmHeight);
	if (tempDC1) 
	{
	DrawIconEx(tempDC1,0,0,hIcon,imbt.bmWidth,imbt.bmHeight,istepIfAniCur,hbrFlickerFreeDraw,DI_IMAGE);   
	noMirrorMask=TRUE;
	ske_ReleaseBufferDC(tempDC1,2000); //keep buffer for 2 seconds
	}        
	}
	*/
	if (imbt.bmBits==NULL)
	{
		NoDIBImage=TRUE;
		imimagbits=(BYTE*)malloc(cy*imbt.bmWidthBytes);
		GetBitmapBits(ici.hbmColor,cy*imbt.bmWidthBytes,(void*)imimagbits);
	}
	else imimagbits=(BYTE*)imbt.bmBits;


	if (immaskbt.bmBits==NULL)
	{
		immaskbits=(BYTE*)malloc(cy*immaskbt.bmWidthBytes);
		GetBitmapBits(ici.hbmMask,cy*immaskbt.bmWidthBytes,(void*)immaskbits);
	}
	else 
		immaskbits=(BYTE*)immaskbt.bmBits;
	icy=imbt.bmHeight;
	cx=imbt.bmWidth;
	imDC=CreateCompatibleDC(hdcDst);
	imBmp=ske_CreateDIB32Point(cx,icy,(void**)&imbits);
	oldBmp=(HBITMAP)SelectObject(imDC,imBmp);
	if (imbits!=NULL && imimagbits!=NULL && immaskbits!=NULL)
	{
		int x; int y;
		int bottom,right,top,h;
		int mwb,mwb2;
		mwb=immaskbt.bmWidthBytes;
		mwb2=imbt.bmWidthBytes;
		bottom=icy;
		right=cx;   
		top=0;
		h=icy;
		for (y=top;(y<bottom)&&!hasmask; y++)
		{ 
			t1=immaskbits+y*mwb;
			for (x=0; (x<mwb)&&!hasmask; x++)
				hasmask|=(*(t1+x)!=0);
		}

		for (y=top;(y<bottom)&&!hasalpha; y++)
		{
			t1=imimagbits+(cy-y-1)*mwb2;
			for (x=0; (x<right)&&!hasalpha; x++)
				hasalpha|=(*(t1+(x<<2)+3)!=0);
		}

		for (y=0; y<(int)icy; y++)
		{
			t1=imimagbits+(h-y-1-top)*mwb2;
			t2=imbits+(!no32bit?y:(icy-y-1))*mwb2;
			t3=immaskbits+(noMirrorMask?y:(h-y-1-top))*mwb;
			for (x=0; x<right; x++)
			{
				DWORD * src, *dest;               
				BYTE mask=0;
				BYTE a = 0; 
				src=(DWORD*)(t1+(x<<2));
				dest=(DWORD*)(t2+(x<<2));              
				if (hasalpha && !hasmask)  
					a=((BYTE*)src)[3];
				else
				{ 
					mask=((1<<(7-x%8))&(*(t3+(x>>3))))!=0;
					if (mask)// && !hasalpha)		
					{
						if (!hasalpha) 
						{ *dest=0; continue; }
						else
						{ 
							if (((BYTE*)src)[3]>0 )
								a = ((BYTE*)src)[3];
							else
							    a = 0;
						}
					}
					else if (hasalpha || hasmask)
						a=(((BYTE*)src)[3]>0?((BYTE*)src)[3]:255);
					else if (!hasalpha && !hasmask)
						a=255;
					else {  *dest=0; continue; }
				}
				if (a>0)
				{
					((BYTE*)dest)[3]=a;
					((BYTE*)dest)[0]=((BYTE*)src)[0]*a/255;
					((BYTE*)dest)[1]=((BYTE*)src)[1]*a/255;
					((BYTE*)dest)[2]=((BYTE*)src)[2]*a/255;
				}
				else 
					*dest=0;
			}
		}
	}
	{
		BLENDFUNCTION bf={AC_SRC_OVER, diFlags&128, alpha, AC_SRC_ALPHA };   
		ske_AlphaBlend(hdcDst,xLeft,yTop,cxWidth, cyWidth, imDC,0,0, cx,icy,bf);
	}
	if (immaskbt.bmBits==NULL) free(immaskbits);
	if (imbt.bmBits==NULL) free(imimagbits);
	SelectObject(imDC,oldBmp);
	DeleteObject(imBmp);
	if(no32bit)DeleteObject(tBmp);
	DeleteObject(ici.hbmColor);
	DeleteObject(ici.hbmMask);
	SelectObject(imDC,GetStockObject(DEFAULT_GUI_FONT));
	mod_DeleteDC(imDC);
	return 1;// DrawIconExS(hdc,xLeft,yTop,hIcon,cxWidth,cyWidth,istepIfAniCur,hbrFlickerFreeDraw,diFlags);
}



int ske_PrepeareImageButDontUpdateIt(RECT * r)
{
	if (g_CluiData.fLayered)
	{
		mutex_bLockUpdate=1;
		ske_DrawNonFramedObjects(TRUE,r);
		ske_ValidateFrameImageProc(r);
		mutex_bLockUpdate=0;
		return 0;
	}
	else
	{
		return ske_ReCreateBackImage(FALSE,r);
	}
	return 0;
}

int ske_RedrawCompleteWindow()
{   
	if (g_CluiData.fLayered)
	{ 
		ske_DrawNonFramedObjects(TRUE,0);
		CallService(MS_SKINENG_INVALIDATEFRAMEIMAGE,0,0);   
	}
	else
	{
		RedrawWindow(pcli->hwndContactList,NULL,NULL,RDW_ALLCHILDREN|RDW_ERASE|RDW_INVALIDATE|RDW_FRAME);
	}
	return 0;
}
// Request to repaint frame or change/drop callback data
// wParam = hWnd of called frame
// lParam = pointer to sPaintRequest (or NULL to redraw all)
// return 2 - already queued, data updated, 1-have been queued, 0 - failure

static INT_PTR ske_Service_UpdateFrameImage(WPARAM wParam, LPARAM lParam)           // Immideately recall paint routines for frame and refresh image
{
	if ( MirandaLoading()) return 0;

	RECT wnd;
	FRAMEWND *frm;
	BOOL NoCancelPost=0;
	BOOL IsAnyQueued=0;
	if (!g_CluiData.mutexOnEdgeSizing)
		GetWindowRect(pcli->hwndContactList,&wnd);
	else
		wnd=g_rcEdgeSizingRect;
	if (!g_CluiData.fLayered)
	{
		RedrawWindow((HWND)wParam,NULL,NULL,RDW_UPDATENOW|RDW_ERASE|RDW_INVALIDATE|RDW_FRAME);
		return 0;
	}
	if (g_pCachedWindow==NULL) ske_ValidateFrameImageProc(&wnd);
	else if (g_pCachedWindow->Width!=wnd.right-wnd.left || g_pCachedWindow->Height!=wnd.bottom-wnd.top) ske_ValidateFrameImageProc(&wnd);
	else if (wParam==0) ske_ValidateFrameImageProc(&wnd);
	else // all Ok Update Single Frame
	{
		// TO BE LOCKED OR PROXIED
		frm=FindFrameByItsHWND((HWND)wParam);
		if (!frm)  ske_ValidateFrameImageProc(&wnd);
		// Validate frame, update window image and remove it from queue
		else 
		{
			if(frm->UpdateRgn)
			{
				DeleteObject(frm->UpdateRgn);
				frm->UpdateRgn=0;
			}
			ske_ValidateSingleFrameImage(frm,0);
			ske_UpdateWindowImage();
			NoCancelPost=1;
			//-- Remove frame from queue
			if (flag_bUpdateQueued)
			{
				int i;
				frm->bQueued=0;
				for(i=0;i<g_nFramesCount;i++)
					if(IsAnyQueued|=g_pfwFrames[i].bQueued) break;
			}
		}
	}       
	if ((!NoCancelPost || !IsAnyQueued) && flag_bUpdateQueued) // no any queued updating cancel post or need to cancel post
	{
		flag_bUpdateQueued=0;
		g_flag_bPostWasCanceled=1;
	}
	return 1;   
}
static INT_PTR ske_Service_InvalidateFrameImage(WPARAM wParam, LPARAM lParam)       // Post request for updating
{
	if ( MirandaLoading()) return 0; 
	if (wParam)
	{
		FRAMEWND *frm=FindFrameByItsHWND((HWND)wParam);
		sPaintRequest * pr=(sPaintRequest*)lParam;
		if (!g_CluiData.fLayered || (frm && frm->floating)) return InvalidateRect((HWND)wParam,pr?(RECT*)&(pr->rcUpdate):NULL,FALSE);
		if (frm) 
		{
			if (frm->PaintCallbackProc!=NULL)
			{
				frm->PaintData=(sPaintRequest *)pr;
				frm->bQueued=1;
				if (pr)
				{
					HRGN r2;
					if (!IsRectEmpty(&pr->rcUpdate))
					{
						RECT rcClient;
						RECT rcUpdate;
						GetClientRect(frm->hWnd,&rcClient);
						IntersectRect( &rcUpdate, &rcClient, &pr->rcUpdate );
						if ( IsRectEmpty( &rcUpdate ))
							return 0;
						r2=CreateRectRgn( rcUpdate.left, rcUpdate.top, rcUpdate.right, rcUpdate.bottom );
					}
					else
					{
						RECT r;
						GetClientRect(frm->hWnd,&r);
						r2=CreateRectRgn(r.left,r.top,r.right,r.bottom);
					}
					if (!frm->UpdateRgn)
					{
						frm->UpdateRgn=CreateRectRgn(0,0,1,1);
						CombineRgn(frm->UpdateRgn,r2,0,RGN_COPY);                                            
					}
					else CombineRgn(frm->UpdateRgn,frm->UpdateRgn,r2,RGN_OR);
					DeleteObject(r2);
				}   

			}
		}      
		else
		{
			Sync( QueueAllFramesUpdating, (BYTE)1 );
		}
	}
	else 
		Sync( QueueAllFramesUpdating , (BYTE)1 );
	if (!flag_bUpdateQueued||g_flag_bPostWasCanceled)
		if (PostMessage(pcli->hwndContactList,UM_UPDATE,0,0))
		{            
			flag_bUpdateQueued=1;
			g_flag_bPostWasCanceled=0;
		}
		return 1;
}


static int ske_ValidateSingleFrameImage(FRAMEWND * Frame, BOOL SkipBkgBlitting)                              // Calling frame paint proc
{
	if (!g_pCachedWindow) { TRACE("ske_ValidateSingleFrameImage calling without cached\n"); return 0;}
	if (Frame->hWnd==(HWND)-1 && !Frame->PaintCallbackProc)  { TRACE("ske_ValidateSingleFrameImage calling without FrameProc\n"); return 0;}
	{ // if ok update image 
		HDC hdc;
		HBITMAP o,n;
		RECT rcPaint,wnd;
		RECT ru={0};
		int w,h,x,y;
		int w1,h1,x1,y1;

		CLUI_SizingGetWindowRect(pcli->hwndContactList,&wnd);
		rcPaint=Frame->wndSize;
		{
			int dx,dy,bx,by;
			if (g_CluiData.mutexOnEdgeSizing)
			{
				dx=rcPaint.left-wnd.left;
				dy=rcPaint.top-wnd.top;
				bx=rcPaint.right-wnd.right;
				by=rcPaint.bottom-wnd.bottom;
				wnd=g_rcEdgeSizingRect;
				rcPaint.left=wnd.left+dx;
				rcPaint.top=wnd.top+dy;
				rcPaint.right=wnd.right+bx;
				rcPaint.bottom=wnd.bottom+by;
			}
		}
		//OffsetRect(&rcPaint,-wnd.left,-wnd.top);
		w=rcPaint.right-rcPaint.left;
		h=rcPaint.bottom-rcPaint.top;
		if (w<=0 || h<=0) 
		{ 
			TRACE("Frame size smaller than 0\n");
			return 0;
		}
		x=rcPaint.left;
		y=rcPaint.top;
		hdc=CreateCompatibleDC(g_pCachedWindow->hImageDC);
		n=ske_CreateDIB32(w,h);
		o=(HBITMAP)SelectObject(hdc,n);
		{
			if (Frame->UpdateRgn && !SkipBkgBlitting)
			{
				GetRgnBox(Frame->UpdateRgn,&ru);
				{
					RECT rc;
					GetClientRect(Frame->hWnd,&rc);
					if (ru.top<0) ru.top=0;
					if (ru.left<0) ru.left=0;
					if (ru.right>rc.right) ru.right=rc.right;
					if (ru.bottom>rc.bottom) ru.bottom=rc.bottom;
				} 
				if (!IsRectEmpty(&ru))
				{
					x1=ru.left;
					y1=ru.top;
					w1=ru.right-ru.left;
					h1=ru.bottom-ru.top;
				}
				else
				{x1=0; y1=0; w1=w; h1=h;}
				// copy image at hdc
				if (SkipBkgBlitting)  //image already at foreground
				{
					BitBlt(hdc,x1,y1,w1,h1,g_pCachedWindow->hImageDC,x+x1,y+y1,SRCCOPY);  
				}
				else
				{
					BitBlt(hdc,x1,y1,w1,h1,g_pCachedWindow->hBackDC,x+x1,y+y1,SRCCOPY);  
				}
				Frame->PaintCallbackProc(Frame->hWnd,hdc,&ru,Frame->UpdateRgn, Frame->dwFlags,Frame->PaintData);
			}
			else
			{
			    HRGN rgnUpdate;
				RECT r;
				GetClientRect(Frame->hWnd,&r);
				rgnUpdate=CreateRectRgn(r.left,r.top,r.right,r.bottom); 
				ru=r;
				if (!IsRectEmpty(&ru))
				{
					x1=ru.left;
					y1=ru.top;
					w1=ru.right-ru.left;
					h1=ru.bottom-ru.top;
				}
				else
				{x1=0; y1=0; w1=w; h1=h;}
				// copy image at hdc
				if (SkipBkgBlitting)  //image already at foreground
				{
					BitBlt(hdc,x1,y1,w1,h1,g_pCachedWindow->hImageDC,x+x1,y+y1,SRCCOPY);  
				}
				else
				{
					BitBlt(hdc,x1,y1,w1,h1,g_pCachedWindow->hBackDC,x+x1,y+y1,SRCCOPY);  
				}
				Frame->PaintCallbackProc(Frame->hWnd,hdc,&r,rgnUpdate, Frame->dwFlags,Frame->PaintData);
				ru=r;
			    DeleteObject(rgnUpdate);
			}
			DeleteObject(Frame->UpdateRgn);
			Frame->UpdateRgn=0;
		}
		if (!IsRectEmpty(&ru))
		{
			x1=ru.left;
			y1=ru.top;
			w1=ru.right-ru.left;
			h1=ru.bottom-ru.top;
		}
		else
		{x1=0; y1=0; w1=w; h1=h;}
		/*  if (!SkipBkgBlitting)
		{
		BitBlt(g_pCachedWindow->hImageDC,x+x1,y+y1,w1,h1,g_pCachedWindow->hBackDC,x+x1,y+y1,SRCCOPY);
		}

		*/  
		{
			//BLENDFUNCTION bf={AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
			BitBlt(g_pCachedWindow->hImageDC,x+x1,y+y1,w1,h1,hdc,x1,y1,SRCCOPY);
			//BLENDFUNCTION bf={AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
			//MyAlphaBlend(g_pCachedWindow->hImageDC,x+x1,y+y1,w1,h1,hdc,x1,y1,w1,h1,bf);  
		}
		
		if ( fnGetScrollBarInfo && (GetWindowLongPtr(Frame->hWnd,GWL_STYLE) & WS_VSCROLL))
		{
			//Draw vertical scroll bar
			//
			RECT rThumb;
			RECT rUpBtn;
			RECT rDnBtn;
			RECT rLine;
			int dx,dy;
			SCROLLBARINFO si={0};
			si.cbSize=sizeof(SCROLLBARINFO);
			fnGetScrollBarInfo(Frame->hWnd,OBJID_VSCROLL,&si);
			rLine=(si.rcScrollBar);
			rUpBtn=rLine;
			rDnBtn=rLine;
			rThumb=rLine;

			rUpBtn.bottom=rUpBtn.top+si.dxyLineButton;
			rDnBtn.top=rDnBtn.bottom-si.dxyLineButton;
			rThumb.top=rLine.top+si.xyThumbTop;
			rThumb.bottom=rLine.top+si.xyThumbBottom;
			{
				dx=Frame->wndSize.right-rLine.right;
				dy=-rLine.top+Frame->wndSize.top;
			}
			OffsetRect(&rLine,dx,dy);
			OffsetRect(&rUpBtn,dx,dy);
			OffsetRect(&rDnBtn,dx,dy);
			OffsetRect(&rThumb,dx,dy);
			BitBlt(g_pCachedWindow->hImageDC,rLine.left,rLine.top,rLine.right-rLine.left,rLine.bottom-rLine.top,g_pCachedWindow->hBackDC,rLine.left,rLine.top,SRCCOPY);
			{
				char req[255];
				_snprintf(req,sizeof(req),"Main,ID=ScrollBar,Frame=%s,Part=Back",Frame->szName);
				SkinDrawGlyph(g_pCachedWindow->hImageDC,&rLine,&rLine,req);
				_snprintf(req,sizeof(req),"Main,ID=ScrollBar,Frame=%s,Part=Thumb",Frame->szName);
				SkinDrawGlyph(g_pCachedWindow->hImageDC,&rThumb,&rThumb,req);
				_snprintf(req,sizeof(req),"Main,ID=ScrollBar,Frame=%s,Part=UpLineButton",Frame->szName);
				SkinDrawGlyph(g_pCachedWindow->hImageDC,&rUpBtn,&rUpBtn,req);
				_snprintf(req,sizeof(req),"Main,ID=ScrollBar,Frame=%s,Part=DownLineButton",Frame->szName);
				SkinDrawGlyph(g_pCachedWindow->hImageDC,&rDnBtn,&rDnBtn,req);
			}
		}

		SelectObject(hdc,o);
		DeleteObject(n);
		mod_DeleteDC(hdc);
	}
	return 1;
}

int ske_BltBackImage (HWND destHWND, HDC destDC, RECT * BltClientRect)
{
	POINT ptMainWnd={0};
	POINT ptChildWnd={0};
	RECT from={0};
	RECT w={0};
	if (g_CluiData.fDisableSkinEngine)
	{
		FillRect(destDC,BltClientRect,GetSysColorBrush(COLOR_3DFACE));
		return 0;
	}
	ske_ReCreateBackImage(FALSE,NULL);
	if (BltClientRect) w=*BltClientRect;
	else GetClientRect(destHWND,&w);
	ptChildWnd.x=w.left;
	ptChildWnd.y=w.top;
	ClientToScreen(destHWND,&ptChildWnd);
	ClientToScreen(pcli->hwndContactList,&ptMainWnd);
	//TODO if main not relative to client area
	return BitBlt(destDC,w.left,w.top,(w.right-w.left),(w.bottom-w.top),g_pCachedWindow->hBackDC,(ptChildWnd.x-ptMainWnd.x),(ptChildWnd.y-ptMainWnd.y),SRCCOPY);

}
int ske_ReCreateBackImage(BOOL Erase,RECT *w)
{
	HBITMAP hb2;
	RECT wnd={0};
	BOOL IsNewCache=0;
	if (g_CluiData.fDisableSkinEngine) return 0;
	GetClientRect(pcli->hwndContactList,&wnd);
	if (w) wnd=*w;
	//-- Check cached.
	if (g_pCachedWindow==NULL)
	{
		//-- Create New Cache
		{
			g_pCachedWindow=(CURRWNDIMAGEDATA*)mir_calloc(sizeof(CURRWNDIMAGEDATA));
			g_pCachedWindow->hScreenDC=GetDC(NULL);
			g_pCachedWindow->hBackDC=CreateCompatibleDC(g_pCachedWindow->hScreenDC);
			g_pCachedWindow->hImageDC=CreateCompatibleDC(g_pCachedWindow->hScreenDC);
			g_pCachedWindow->Width=wnd.right-wnd.left;
			g_pCachedWindow->Height=wnd.bottom-wnd.top;
			if (g_pCachedWindow->Width!=0 && g_pCachedWindow->Height!=0)
			{
				g_pCachedWindow->hImageDIB=ske_CreateDIB32Point(g_pCachedWindow->Width,g_pCachedWindow->Height,(void**)&(g_pCachedWindow->hImageDIBByte));
				g_pCachedWindow->hBackDIB=ske_CreateDIB32Point(g_pCachedWindow->Width,g_pCachedWindow->Height,(void**)&(g_pCachedWindow->hBackDIBByte));
				g_pCachedWindow->hImageOld=(HBITMAP)SelectObject(g_pCachedWindow->hImageDC,g_pCachedWindow->hImageDIB);
				g_pCachedWindow->hBackOld=(HBITMAP)SelectObject(g_pCachedWindow->hBackDC,g_pCachedWindow->hBackDIB);
			}
		}
		IsNewCache=1;
	}   
	if (g_pCachedWindow->Width!=wnd.right-wnd.left || g_pCachedWindow->Height!=wnd.bottom-wnd.top)		
	{
		HBITMAP hb1=NULL,hb2=NULL;
		g_pCachedWindow->Width=wnd.right-wnd.left;
		g_pCachedWindow->Height=wnd.bottom-wnd.top;
		if (g_pCachedWindow->Width!=0 && g_pCachedWindow->Height!=0)
		{
			hb1=ske_CreateDIB32Point(g_pCachedWindow->Width,g_pCachedWindow->Height,(void**)&(g_pCachedWindow->hImageDIBByte));
			hb2=ske_CreateDIB32Point(g_pCachedWindow->Width,g_pCachedWindow->Height,(void**)&(g_pCachedWindow->hBackDIBByte)); 
			SelectObject(g_pCachedWindow->hImageDC,hb1);
			SelectObject(g_pCachedWindow->hBackDC,hb2);
		}
		else
		{
			SelectObject(g_pCachedWindow->hImageDC,g_pCachedWindow->hImageOld);
			SelectObject(g_pCachedWindow->hBackDC,g_pCachedWindow->hBackOld);
		}
		if (g_pCachedWindow->hImageDIB) DeleteObject(g_pCachedWindow->hImageDIB);
		if (g_pCachedWindow->hBackDIB) DeleteObject(g_pCachedWindow->hBackDIB);
		g_pCachedWindow->hImageDIB=hb1;
		g_pCachedWindow->hBackDIB=hb2;
		IsNewCache=1;
	}
	if ((Erase || IsNewCache )&& (g_pCachedWindow->Width!=0 && g_pCachedWindow->Height!=0))
	{

		hb2=ske_CreateDIB32(g_pCachedWindow->Width,g_pCachedWindow->Height); 
		SelectObject(g_pCachedWindow->hBackDC,hb2);
		DeleteObject(g_pCachedWindow->hBackDIB);
		g_pCachedWindow->hBackDIB=hb2;
		FillRect(g_pCachedWindow->hBackDC,&wnd,GetSysColorBrush(COLOR_BTNFACE));
		SkinDrawGlyph(g_pCachedWindow->hBackDC,&wnd,&wnd,"Main,ID=Background,Opt=Non-Layered");
		ske_SetRectOpaque(g_pCachedWindow->hBackDC,&wnd);
	}
	return 1;
}
int ske_DrawNonFramedObjects(BOOL Erase,RECT *r)
{
	RECT w,wnd;
	if (r) w=*r;
	else CLUI_SizingGetWindowRect(pcli->hwndContactList,&w);
	if (!g_CluiData.fLayered) return ske_ReCreateBackImage(FALSE,0);
	if (g_pCachedWindow==NULL)
		return ske_ValidateFrameImageProc(&w);

	wnd=w;
	OffsetRect(&w, -w.left, -w.top);
	if (Erase)
	{
		HBITMAP hb2;
		hb2=ske_CreateDIB32(g_pCachedWindow->Width,g_pCachedWindow->Height); 
		SelectObject(g_pCachedWindow->hBackDC,hb2);
		DeleteObject(g_pCachedWindow->hBackDIB);
		g_pCachedWindow->hBackDIB=hb2;
	}

	SkinDrawGlyph(g_pCachedWindow->hBackDC,&w,&w,"Main,ID=Background");
	//--Draw frames captions
	{
		int i;
		for(i=0;i<g_nFramesCount;i++)
			if (g_pfwFrames[i].TitleBar.ShowTitleBar && g_pfwFrames[i].visible && !g_pfwFrames[i].floating)
			{
				RECT rc;
				SetRect(&rc,g_pfwFrames[i].wndSize.left,g_pfwFrames[i].wndSize.top-g_nTitleBarHeight-g_CluiData.nGapBetweenTitlebar,g_pfwFrames[i].wndSize.right,g_pfwFrames[i].wndSize.top-g_CluiData.nGapBetweenTitlebar);
				//GetWindowRect(Frames[i].TitleBar.hwnd,&rc);
				//OffsetRect(&rc,-wnd.left,-wnd.top);
				Sync( DrawTitleBar, g_pCachedWindow->hBackDC, &rc, g_pfwFrames[i].id );
			}
	}
	g_mutex_bLockUpdating=1;

	flag_bJustDrawNonFramedObjects=1;
	return 0;
}
int ske_ValidateFrameImageProc(RECT * r)                                // Calling queued frame paint procs and refresh image
{
	RECT wnd={0};
	BOOL IsNewCache=0;
	BOOL IsForceAllPainting=0;
	if (r) wnd=*r;
	else GetWindowRect(pcli->hwndContactList,&wnd);
	if (wnd.right-wnd.left==0 || wnd.bottom-wnd.top==0) return 0;
	g_mutex_bLockUpdating=1;
	//-- Check cached.
	if (g_pCachedWindow==NULL)
	{
		//-- Create New Cache
		{
			g_pCachedWindow=(CURRWNDIMAGEDATA*)mir_calloc(sizeof(CURRWNDIMAGEDATA));
			g_pCachedWindow->hScreenDC=GetDC(NULL);
			g_pCachedWindow->hBackDC=CreateCompatibleDC(g_pCachedWindow->hScreenDC);
			g_pCachedWindow->hImageDC=CreateCompatibleDC(g_pCachedWindow->hScreenDC);
			g_pCachedWindow->Width=wnd.right-wnd.left;
			g_pCachedWindow->Height=wnd.bottom-wnd.top;
			g_pCachedWindow->hImageDIB=ske_CreateDIB32Point(g_pCachedWindow->Width,g_pCachedWindow->Height,(void**)&(g_pCachedWindow->hImageDIBByte));
			g_pCachedWindow->hBackDIB=ske_CreateDIB32Point(g_pCachedWindow->Width,g_pCachedWindow->Height,(void**)&(g_pCachedWindow->hBackDIBByte));
			g_pCachedWindow->hImageOld=(HBITMAP)SelectObject(g_pCachedWindow->hImageDC,g_pCachedWindow->hImageDIB);
			g_pCachedWindow->hBackOld=(HBITMAP)SelectObject(g_pCachedWindow->hBackDC,g_pCachedWindow->hBackDIB);
		}
		IsNewCache=1;
	}   
	if (g_pCachedWindow->Width!=wnd.right-wnd.left || g_pCachedWindow->Height!=wnd.bottom-wnd.top)
	{
		HBITMAP hb1,hb2;
		g_pCachedWindow->Width=wnd.right-wnd.left;
		g_pCachedWindow->Height=wnd.bottom-wnd.top;
		hb1=ske_CreateDIB32Point(g_pCachedWindow->Width,g_pCachedWindow->Height,(void**)&(g_pCachedWindow->hImageDIBByte));
		hb2=ske_CreateDIB32Point(g_pCachedWindow->Width,g_pCachedWindow->Height,(void**)&(g_pCachedWindow->hBackDIBByte)); 
		SelectObject(g_pCachedWindow->hImageDC,hb1);
		SelectObject(g_pCachedWindow->hBackDC,hb2);
		DeleteObject(g_pCachedWindow->hImageDIB);
		DeleteObject(g_pCachedWindow->hBackDIB);
		g_pCachedWindow->hImageDIB=hb1;
		g_pCachedWindow->hBackDIB=hb2;
		IsNewCache=1;
	}
	if (IsNewCache)
	{
		ske_DrawNonFramedObjects(0,&wnd);       
		IsForceAllPainting=1;
	}
	if (flag_bJustDrawNonFramedObjects)
	{
		IsForceAllPainting=1;
		flag_bJustDrawNonFramedObjects=0;
	}
	if (IsForceAllPainting) 
	{ 
		BitBlt(g_pCachedWindow->hImageDC,0,0,g_pCachedWindow->Width,g_pCachedWindow->Height,g_pCachedWindow->hBackDC,0,0,SRCCOPY);
		Sync( QueueAllFramesUpdating, (BYTE)1 );
	}
	//-- Validating frames
	{ 
		int i;
		for(i=0;i<g_nFramesCount;i++)
			if (g_pfwFrames[i].PaintCallbackProc && g_pfwFrames[i].visible && !g_pfwFrames[i].floating )
				if (g_pfwFrames[i].bQueued || IsForceAllPainting)
					ske_ValidateSingleFrameImage(&g_pfwFrames[i],IsForceAllPainting);
	}
	g_mutex_bLockUpdating=1;
	ModernSkinButtonRedrawAll(0);
	g_mutex_bLockUpdating=0;
	if (!mutex_bLockUpdate)  ske_UpdateWindowImageRect(&wnd);
	//-- Clear queue
	{
		Sync( QueueAllFramesUpdating, (BYTE)0 );
		flag_bUpdateQueued=0;
		g_flag_bPostWasCanceled=0;
	}
	return 1;
}

int ske_UpdateWindowImage()
{
	if (MirandaExiting()) 
		return 0;
	if (g_CluiData.fLayered)
	{
		RECT r;
		GetWindowRect(pcli->hwndContactList,&r);
		return ske_UpdateWindowImageRect(&r);
	}
	else
		ske_ReCreateBackImage(FALSE,0);
	ske_ApplyTransluency();
	return 0;
}


int ske_UpdateWindowImageRect(RECT * r)                                     // Update window with current image and 
{
	//if not validity -> ValidateImageProc
	//else Update using current alpha
	RECT wnd=*r;

	if (!g_CluiData.fLayered) return ske_ReCreateBackImage(FALSE,0);
	if (g_pCachedWindow==NULL) return ske_ValidateFrameImageProc(&wnd); 
	if (g_pCachedWindow->Width!=wnd.right-wnd.left || g_pCachedWindow->Height!=wnd.bottom-wnd.top) return ske_ValidateFrameImageProc(&wnd);
	if (g_flag_bFullRepaint) 
	{
		g_flag_bFullRepaint=0; 
		return ske_ValidateFrameImageProc(&wnd);
	}
	ske_JustUpdateWindowImageRect(&wnd);
	return 0;
}

void ske_ApplyTransluency()
{
	int IsTransparancy;
	HWND hwnd=pcli->hwndContactList;
	BOOL layered=(GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED)?TRUE:FALSE;

	IsTransparancy=g_CluiData.fSmoothAnimation || g_bTransparentFlag;
	if (!g_bTransparentFlag && !g_CluiData.fSmoothAnimation && g_CluiData.bCurrentAlpha!=0)
		g_CluiData.bCurrentAlpha=255;
	if (!g_CluiData.fLayered && (/*(g_CluiData.bCurrentAlpha==255)||*/(g_proc_SetLayeredWindowAttributesNew && IsTransparancy)))
	{
		if (!layered) SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
		if (g_proc_SetLayeredWindowAttributesNew) g_proc_SetLayeredWindowAttributesNew(hwnd, RGB(0,0,0), (BYTE)g_CluiData.bCurrentAlpha, LWA_ALPHA);
	}

	AniAva_RedrawAllAvatars(FALSE);
	return;
}

int ske_JustUpdateWindowImage()
{
	RECT r;
	if (!g_CluiData.fLayered)
	{
		ske_ApplyTransluency();
		return 0;
	}
	GetWindowRect(pcli->hwndContactList,&r);
	return ske_JustUpdateWindowImageRect(&r);
}
int ske_JustUpdateWindowImageRect(RECT * rty)
//Update window image
{
	BLENDFUNCTION bf={AC_SRC_OVER, 0,g_CluiData.bCurrentAlpha, AC_SRC_ALPHA };
	POINT dest={0}, src={0};
	int res;
	RECT wnd=*rty;

	RECT rect;
	SIZE sz={0};

	if (!g_CluiData.fLayered)
	{
		ske_ApplyTransluency();
		return 0;
	}
	if (!pcli->hwndContactList) return 0;
	rect=wnd;
	dest.x=rect.left;
	dest.y=rect.top;
	sz.cx=rect.right-rect.left;
	sz.cy=rect.bottom-rect.top;
	if (g_proc_UpdateLayeredWindow && g_CluiData.fLayered)
	{
		if (!(GetWindowLongPtr(pcli->hwndContactList, GWL_EXSTYLE)&WS_EX_LAYERED))
			SetWindowLongPtr(pcli->hwndContactList,GWL_EXSTYLE, GetWindowLongPtr(pcli->hwndContactList, GWL_EXSTYLE) |WS_EX_LAYERED);
		Sync( SetAlpha, g_CluiData.bCurrentAlpha );

		res=g_proc_UpdateLayeredWindow(pcli->hwndContactList,g_pCachedWindow->hScreenDC,&dest,&sz,g_pCachedWindow->hImageDC,&src,RGB(1,1,1),&bf,ULW_ALPHA);
		g_CluiData.fAeroGlass = false;
		CLUI_UpdateAeroGlass();
	}
	else InvalidateRect(pcli->hwndContactList,NULL,TRUE);
	return 0;
}

int ske_DrawImageAt(HDC hdc, RECT *rc)
{
	BLENDFUNCTION bf={AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
	BitBlt(g_pCachedWindow->hImageDC,rc->left,rc->top,rc->right-rc->left,rc->bottom-rc->top,g_pCachedWindow->hBackDC,rc->left,rc->top,SRCCOPY);
	ske_AlphaBlend(g_pCachedWindow->hImageDC,rc->left,rc->top,rc->right-rc->left,rc->bottom-rc->top,hdc,0,0,rc->right-rc->left,rc->bottom-rc->top,bf);    
	if (!g_mutex_bLockUpdating)
		ske_UpdateWindowImage();
	return 0;
}

HBITMAP ske_GetCurrentWindowImage()
{ 
	return g_pCachedWindow->hImageDIB;
}

/*
*  Glyph text routine
*/

static DWORD ske_HexToARGB(char * Hex)
{
	char buf[10]={0};
	char buf2[11]={0};
	char * st;
	BYTE alpha;
	DWORD AARRGGBB=0;
	_snprintf(buf,10,"%s\n",Hex);
	if (buf[1]=='x' || buf[1]=='X')
		_snprintf(buf2,11,"0x%s\n",buf+2);
	else
		_snprintf(buf2,11,"0x%s\n",buf);
	buf2[10]='\0';
	AARRGGBB=strtoul(buf2,&st,16); 
	alpha=(BYTE)((AARRGGBB&0xFF000000)>>24);
	alpha=255-((alpha==0)?255:alpha); 
	AARRGGBB=(alpha<<24)+((AARRGGBB&0x00FF0000)>>16)+((AARRGGBB&0x000000FF)<<16)+(AARRGGBB&0x0000FF00);
	return AARRGGBB;
}

static TCHAR *ske_ReAppend(TCHAR *lfirst, TCHAR * lsecond, int len)
{ 
	int l1=lfirst?lstrlen(lfirst):0;
	int l2=(len?len:(lstrlen(lsecond)+1));
	TCHAR *buf=(TCHAR *)mir_alloc((l1+l2+1)*sizeof(TCHAR));
	if (lfirst) memmove(buf,lfirst,l1*sizeof(TCHAR));
	memmove(buf+l1,lsecond,l2*sizeof(TCHAR));
	if (lfirst) mir_free(lfirst);
	if (len) buf[l1+l2]=_T('\0');
	return buf;
}

TCHAR* ske_ReplaceVar(TCHAR *var)
{
	if (!var) return mir_tstrdup(_T(""));
	if (!lstrcmpi(var,TEXT("Profile")))
	{
		char buf[MAX_PATH]={0};
		CallService(MS_DB_GETPROFILENAME,(WPARAM)MAX_PATH,(LPARAM)buf);
		{
			int i=strlen(buf);
			while (buf[i]!='.' && i>0) i--;
			buf[i]='\0';
		}
		mir_free_and_nill(var);
		return mir_a2u(buf);
	} 

	mir_free_and_nill(var);
	return mir_tstrdup(_T(""));
}
TCHAR *ske_ParseText(TCHAR *stzText)
{
	int len=lstrlen(stzText);
	TCHAR *result=NULL;
	int stpos=0;
	int curpos=0;  

	while(curpos<len)
	{
		//1 find first %
		while(curpos<len && stzText[curpos]!=(TCHAR)'%') curpos++;
		if (curpos<len) //% found
		{
			if (curpos-stpos>0) 
				result=ske_ReAppend(result,stzText+stpos,curpos-stpos);
			stpos=curpos+1;
			curpos++;
			//3 find second % 
			while(curpos<len && stzText[curpos]!=(TCHAR)'%') curpos++;
			if (curpos<len)
			{
				if (curpos-stpos>0) 
				{
					TCHAR *var=(TCHAR *)mir_alloc((curpos-stpos+1)*sizeof(TCHAR));
					memmove(var,stzText+stpos,(curpos-stpos)*sizeof(TCHAR));
					var[curpos-stpos]=(TCHAR)'\0';
					var=ske_ReplaceVar(var);
					result=ske_ReAppend(result,var,0);
					mir_free_and_nill(var);
				}
				else
					result=ske_ReAppend(result,_T("%"),0);
				curpos++;
				stpos=curpos;
			}
			else
			{
				//  if (curpos-stpos>0) 
				//    result=ske_ReAppend(result,stzText+stpos,curpos-stpos);
				break;
			}
		}
		else
		{
			if (curpos-stpos>0) 
				result=ske_ReAppend(result,stzText+stpos,curpos-stpos);
			break;
		}   
	}
	return result;
}
/*
*   Parse text object string, find glyph object and add text to it.
*   szGlyphTextID and Define string is:
*   t[szGlyphTextID]=s[HostObjectID],[Left],[Top],[Right],[Bottom],[LTRBHV],[FontID],[Color1],[reservedforColor2],[Text]
*/
static void OLDske_AddParseTextGlyphObject(char * szGlyphTextID,char * szDefineString,SKINOBJECTSLIST *Skin)
{

	GLYPHOBJECT *globj=NULL;
	{     
		char buf[255]={0};
		GetParamN(szDefineString,buf,sizeof(buf),0,',',TRUE);
		if (strlen(buf))
		{
			SKINOBJECTDESCRIPTOR * lpobj;
			lpobj=ske_FindObjectByName(buf,OT_GLYPHOBJECT,Skin);
			if (lpobj)
				globj=(GLYPHOBJECT*)lpobj->Data;
		}
		if (globj)
		{
			GLYPHTEXT * glText;

			if (!globj->plTextList)
			{
				globj->plTextList=li.List_Create(0,1);
				globj->plTextList->sortFunc=ske_SortTextGlyphObjectFunc;
			}
			glText=(GLYPHTEXT*)mir_calloc(sizeof(GLYPHTEXT));
			glText->szGlyphTextID=mir_strdup(szGlyphTextID);
			glText->szObjectName=mir_strdup(buf);
			glText->iLeft=atoi(GetParamN(szDefineString,buf,sizeof(buf),1,',',TRUE));
			glText->iTop=atoi(GetParamN(szDefineString,buf,sizeof(buf),2,',',TRUE));
			glText->iRight=atoi(GetParamN(szDefineString,buf,sizeof(buf),3,',',TRUE));
			glText->iBottom=atoi(GetParamN(szDefineString,buf,sizeof(buf),4,',',TRUE));
			{
				memset(buf,0,6);
				GetParamN(szDefineString,buf,sizeof(buf),5,',',TRUE);
				buf[0]&=95; buf[1]&=95; buf[2]&=95; buf[3]&=95; buf[4]&=95; buf[5]&=95;   //to uppercase: &01011111 (0-95)
				glText->RelativeFlags=
					(buf[0]=='C'?1:((buf[0]=='R')?2:0))       //[BC][RC][BC][RC] --- Left relative
					|(buf[1]=='C'?4:((buf[1]=='B')?8:0))       //  |   |   |--------- Top relative   
					|(buf[2]=='C'?16:((buf[2]=='R')?32:0))     //  |   |--------------Right relative 
					|(buf[3]=='C'?64:((buf[3]=='B')?128:0));   //  |------------------Bottom relative          
				glText->dwFlags=(buf[4]=='C'?DT_CENTER:((buf[4]=='R')?DT_RIGHT:DT_LEFT))
					|(buf[5]=='C'?DT_VCENTER:((buf[5]=='B')?DT_BOTTOM:DT_TOP));
			}
			glText->szFontID=mir_strdup(GetParamN(szDefineString,buf,sizeof(buf),6,',',TRUE));

			glText->dwColor=ske_HexToARGB(GetParamN(szDefineString,buf,sizeof(buf),7,',',TRUE));
			glText->dwShadow=ske_HexToARGB(GetParamN(szDefineString,buf,sizeof(buf),8,',',TRUE));
			glText->stValueText=mir_a2u(GetParamN(szDefineString,buf,sizeof(buf),9,',',TRUE));
			glText->stText=ske_ParseText(glText->stValueText);
			li.List_Insert(globj->plTextList,(void*)glText,globj->plTextList->realCount);     
			qsort(globj->plTextList->items,globj->plTextList->realCount,sizeof(void*),(int(*)(const void*, const void*))globj->plTextList->sortFunc);
		}
	}
}


static void ske_AddParseTextGlyphObject(char * szGlyphTextID,char * szDefineString,SKINOBJECTSLIST *Skin)
{
	char buf[255]={0};
	GetParamN(szDefineString,buf,sizeof(buf),0,',',TRUE);
	if (strlen(buf))
	{
		GLYPHTEXT * glText;
		glText=(GLYPHTEXT*)mir_calloc(sizeof(GLYPHTEXT));
		glText->szGlyphTextID=mir_strdup(szGlyphTextID);
		glText->szObjectName=mir_strdup(buf);
		glText->iLeft=atoi(GetParamN(szDefineString,buf,sizeof(buf),1,',',TRUE));
		glText->iTop=atoi(GetParamN(szDefineString,buf,sizeof(buf),2,',',TRUE));
		glText->iRight=atoi(GetParamN(szDefineString,buf,sizeof(buf),3,',',TRUE));
		glText->iBottom=atoi(GetParamN(szDefineString,buf,sizeof(buf),4,',',TRUE));
		{
			memset(buf,0,6);
			GetParamN(szDefineString,buf,sizeof(buf),5,',',TRUE);
			buf[0]&=95; buf[1]&=95; buf[2]&=95; buf[3]&=95; buf[4]&=95; buf[5]&=95;   //to uppercase: &01011111 (0-95)
			glText->RelativeFlags=
				(buf[0]=='C'?1:((buf[0]=='R')?2:0))       //[BC][RC][BC][RC] --- Left relative
				|(buf[1]=='C'?4:((buf[1]=='B')?8:0))       //  |   |   |--------- Top relative   
				|(buf[2]=='C'?16:((buf[2]=='R')?32:0))     //  |   |--------------Right relative 
				|(buf[3]=='C'?64:((buf[3]=='B')?128:0));   //  |------------------Bottom relative          
			glText->dwFlags=(buf[4]=='C'?DT_CENTER:((buf[4]=='R')?DT_RIGHT:DT_LEFT))
				|(buf[5]=='C'?DT_VCENTER:((buf[5]=='B')?DT_BOTTOM:DT_TOP));
		}
		glText->szFontID=mir_strdup(GetParamN(szDefineString,buf,sizeof(buf),6,',',TRUE));

		glText->dwColor=ske_HexToARGB(GetParamN(szDefineString,buf,sizeof(buf),7,',',TRUE));
		glText->dwShadow=ske_HexToARGB(GetParamN(szDefineString,buf,sizeof(buf),8,',',TRUE));
		glText->stValueText=mir_a2u(GetParamN(szDefineString,buf,sizeof(buf),9,',',TRUE));
		glText->stText=ske_ParseText(glText->stValueText);
		
		if (!Skin->pTextList)
			Skin->pTextList=li.List_Create(0,1);
		li.List_InsertPtr(Skin->pTextList,glText);
	}
}


/*
*   Parse font definition string.
*   szGlyphTextID and Define string is:
*   f[szFontID]=s[FontTypefaceName],[size],[BIU]
*/
static void ske_AddParseSkinFont(char * szFontID,char * szDefineString,SKINOBJECTSLIST *Skin)
{
	//SortedList * gl_plSkinFonts=NULL;
	SKINFONT * sf =NULL;
	sf=(SKINFONT*)mir_calloc(sizeof(SKINFONT));
	if (sf)
	{
		{
			char buf[255];    
			int fntSize=0;
			BOOL fntBold=FALSE, fntItalic=FALSE, fntUnderline=FALSE;
			LOGFONTA logfont={0};    
			logfont.lfCharSet=DEFAULT_CHARSET;
			logfont.lfOutPrecision=OUT_DEFAULT_PRECIS;
			logfont.lfClipPrecision=CLIP_DEFAULT_PRECIS;
			logfont.lfQuality=DEFAULT_QUALITY;
			logfont.lfPitchAndFamily=DEFAULT_PITCH|FF_DONTCARE;    

			strncpy(logfont.lfFaceName,GetParamN(szDefineString,buf,sizeof(buf),0,',',TRUE),32);
			logfont.lfHeight=atoi(GetParamN(szDefineString,buf,sizeof(buf),1,',',TRUE));
			if (logfont.lfHeight<0)
			{
				HDC hdc=CreateCompatibleDC(NULL);        
				logfont.lfHeight=(long)-MulDiv(logfont.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
				mod_DeleteDC(hdc);
			}
			logfont.lfHeight=-logfont.lfHeight;
			GetParamN(szDefineString,buf,sizeof(buf),2,',',TRUE);
			buf[0]&=95; buf[1]&=95; buf[2]&=95;
			logfont.lfWeight=(buf[0]=='B')?FW_BOLD:FW_NORMAL;
			logfont.lfItalic=(buf[1]=='I')?1:0;
			logfont.lfUnderline=(buf[2]=='U')?1:0;

			sf->hFont=CreateFontIndirectA(&logfont);
			if (sf->hFont)
			{
				sf->szFontID=mir_strdup(szFontID);
				if (!gl_plSkinFonts)
					gl_plSkinFonts=li.List_Create(0,1);
				if (gl_plSkinFonts)
				{
					li.List_Insert(gl_plSkinFonts,(void*)sf,gl_plSkinFonts->realCount);
				}
			}

		}
	}

}

/*
HICON ske_CreateJoinedIcon_Old(HICON hBottom, HICON hTop,BYTE alpha)
{
HDC tempDC;
HICON res=NULL;
HBITMAP oImage,nImage;
HBITMAP nMask;
ICONINFO iNew={0};
ICONINFO iciBottom={0};
ICONINFO iciTop={0};
BITMAP bmp={0};
SIZE sz={0};
{
if (!GetIconInfo(hBottom,&iciBottom)) return NULL;
GetObject(iciBottom.hbmColor,sizeof(BITMAP),&bmp);
sz.cx=bmp.bmWidth; sz.cy=bmp.bmHeight;
if(iciBottom.hbmColor) DeleteObject(iciBottom.hbmColor);
if(iciBottom.hbmMask) DeleteObject(iciBottom.hbmMask);
}
if (sz.cx==0 || sz.cy==0) return NULL;
tempDC=CreateCompatibleDC(NULL);
nImage=ske_CreateDIB32(sz.cx,sz.cy);
oImage=SelectObject(tempDC,nImage);
ske_DrawIconEx(tempDC,0,0,hBottom,sz.cx,sz.cy,0,NULL,DI_NORMAL);
ske_DrawIconEx(tempDC,0,0,hTop,sz.cx,sz.cy,0,NULL,DI_NORMAL|(alpha<<24));
SelectObject(tempDC,oImage);
DeleteDC(tempDC);
{
BYTE * p=malloc(sz.cx*sz.cy/8+10);
nMask=CreateBitmap(sz.cx,sz.cy,1,1,(void*)p);
iNew.fIcon=TRUE;
iNew.hbmColor=nImage;
iNew.hbmMask=nMask;
res=CreateIconIndirect(&iNew);
if (!res) 
TRACE_ERROR();
DeleteObject(nImage);
DeleteObject(nMask);
free(p);
}
return res;
}
*/


/*
*   ske_CheckHasAlfaChannel - checks if image has at least one BYTE in alpha chennel
*                  that is not a 0. (is image real 32 bit or just 24 bit)
*/
static BOOL ske_CheckHasAlfaChannel(BYTE * from, int widthByte, int height)
{
	int i=0,j=0;
	DWORD * pt=(DWORD*)from;
	while (j<height)
	{
		BYTE * add=(BYTE*)pt+widthByte;
		while (pt<(DWORD*)add)
		{
			if ((*pt&0xFF000000)!=0) return TRUE;
			pt++;
		}
		pt=(DWORD*)(from+widthByte*j);
		j++;
	}
	return FALSE;
}

/*
*   ske_CheckIconHasMask - checks if mask image has at least one that is not a 0.
*                  Not sure is ir required or not
*/
static BOOL ske_CheckIconHasMask(BYTE * from)
{
	int i=0;
	for (i=0; i<16*16/8; i++)
	{
		if (from[i]!=0) return TRUE;
	}
	return FALSE;
}

/*
*   ske_GetMaskBit - return value of apropriate mask bit in line at x position
*/
static BOOL ske_GetMaskBit(BYTE *line, int x)
{
	return ((*(line+(x>>3)))&(0x01<<(7-(x&0x07))))!=0;
}
/*
*    ske_Blend  - alpha ske_Blend ARGB values of 2 pixels. X1 - underlaying,
*            X2 - overlaying points.
*/

static DWORD ske_Blend(DWORD X1,DWORD X2, BYTE alpha)
{
	BYTE a1=(BYTE)(X1>>24);
	BYTE a2=(BYTE)(((X2>>24)*alpha)>>8);
	BYTE r1=(BYTE)(X1>>16);
	BYTE r2=(BYTE)(X2>>16);
	BYTE g1=(BYTE)(X1>>8);
	BYTE g2=(BYTE)(X2>>8);
	BYTE b1=(BYTE)(X1);
	BYTE b2=(BYTE)(X2);

	BYTE a_1=~a1;
	BYTE a_2=~a2;
	WORD am=(WORD)a1*a_2;

	/*  it is possible to use >>8 instead of /255 but it is require additional
	*   checking of alphavalues
	*/
	WORD ar=a1+(((WORD)a_1*a2)/255);
	// if a2 more than 0 than result should be more
	// or equal (if a1==0) to a2, else in combination
	// with mask we can get here black points

	ar=(a2>ar)?a2:ar;

	if (ar==0) return 0;

	//else
	{
		WORD arm=ar*255;
		WORD rr=(((WORD)r1*am+(WORD)r2*a2*255))/arm;
		WORD gr=(((WORD)g1*am+(WORD)g2*a2*255))/arm;
		WORD br=(((WORD)b1*am+(WORD)b2*a2*255))/arm;
		return (ar<<24)|(rr<<16)|(gr<<8)|br;
	}

}
/*
*    CreateJoinedIcon  - creates new icon by drawing hTop over hBottom.
*/
HICON ske_CreateJoinedIcon(HICON hBottom, HICON hTop, BYTE alpha)
{
	HDC tempDC;
	HICON res=NULL;
	HBITMAP oImage,nImage;
	HBITMAP nMask;
	BITMAP bmp={0};
	BYTE *ptPixels;
	ICONINFO iNew={0};
	ICONINFO iciBottom={0};
	ICONINFO iciTop={0};

	BITMAP bmp_top={0};
	BITMAP bmp_top_mask={0};

	BITMAP bmp_bottom={0};
	BITMAP bmp_bottom_mask={0};

	tempDC=CreateCompatibleDC(NULL);
	nImage=ske_CreateDIB32Point(16,16,(void**)&ptPixels);
	oImage=(HBITMAP)SelectObject(tempDC,nImage);

	GetIconInfo(hBottom,&iciBottom);
	GetObject(iciBottom.hbmColor,sizeof(BITMAP),&bmp_bottom);
	GetObject(iciBottom.hbmMask,sizeof(BITMAP),&bmp_bottom_mask);

	GetIconInfo(hTop,&iciTop);
	GetObject(iciTop.hbmColor,sizeof(BITMAP),&bmp_top);
	GetObject(iciTop.hbmMask,sizeof(BITMAP),&bmp_top_mask);

	if (bmp_bottom.bmBitsPixel==32 &&bmp_top.bmBitsPixel==32 && IsWinVerXPPlus())
	{
		BYTE * BottomBuffer, * TopBuffer, * BottomMaskBuffer, * TopMaskBuffer;
		BYTE * bb, * tb, * bmb, * tmb;
		BYTE * db=ptPixels;
		int vstep_d=16*4;
		int vstep_b=bmp_bottom.bmWidthBytes;
		int vstep_t=bmp_top.bmWidthBytes;
		int vstep_bm=bmp_bottom_mask.bmWidthBytes;
		int vstep_tm=bmp_top_mask.bmWidthBytes;
		alpha=alpha?alpha:255;
		if (bmp_bottom.bmBits) bb=BottomBuffer=(BYTE*)bmp_bottom.bmBits;
		else
		{
			BottomBuffer=(BYTE*)malloc(bmp_bottom.bmHeight*bmp_bottom.bmWidthBytes);
			GetBitmapBits(iciBottom.hbmColor,bmp_bottom.bmHeight*bmp_bottom.bmWidthBytes,BottomBuffer);
			bb=BottomBuffer+vstep_b*(bmp_bottom.bmHeight-1);
			vstep_b=-vstep_b;
		}
		if (bmp_top.bmBits) tb=TopBuffer=(BYTE*)bmp_top.bmBits;
		else
		{
			TopBuffer=(BYTE*)malloc(bmp_top.bmHeight*bmp_top.bmWidthBytes);
			GetBitmapBits(iciTop.hbmColor,bmp_top.bmHeight*bmp_top.bmWidthBytes,TopBuffer);
			tb=TopBuffer+vstep_t*(bmp_top.bmHeight-1);
			vstep_t=-vstep_t;
		}
		if (bmp_bottom_mask.bmBits)
		{
			BottomMaskBuffer=(BYTE*)bmp_bottom_mask.bmBits;
			bmb=BottomMaskBuffer;         
		}
		else
		{
			BottomMaskBuffer=(BYTE*)malloc(bmp_bottom_mask.bmHeight*bmp_bottom_mask.bmWidthBytes);            
			GetBitmapBits(iciBottom.hbmMask,bmp_bottom_mask.bmHeight*bmp_bottom_mask.bmWidthBytes,BottomMaskBuffer);
			bmb=BottomMaskBuffer+vstep_bm*(bmp_bottom_mask.bmHeight-1);
			vstep_bm=-vstep_bm;

		}
		if (bmp_top_mask.bmBits)
		{
			TopMaskBuffer=(BYTE*)bmp_top_mask.bmBits;
			tmb=TopMaskBuffer;

		}
		else
		{
			TopMaskBuffer=(BYTE*)malloc(bmp_top_mask.bmHeight*bmp_top_mask.bmWidthBytes);
			GetBitmapBits(iciTop.hbmMask,bmp_top_mask.bmHeight*bmp_top_mask.bmWidthBytes,TopMaskBuffer);
			tmb=TopMaskBuffer+vstep_tm*(bmp_top_mask.bmHeight-1);
			vstep_tm=-vstep_tm;
		}
		{
			int x=0; int y=0;
			BOOL topHasAlpha=ske_CheckHasAlfaChannel(TopBuffer,bmp_top.bmWidthBytes,bmp_top.bmHeight);
			BOOL bottomHasAlpha=ske_CheckHasAlfaChannel(BottomBuffer,bmp_bottom.bmWidthBytes,bmp_bottom.bmHeight);
			BOOL topHasMask=ske_CheckIconHasMask(TopMaskBuffer);
			BOOL bottomHasMask=ske_CheckIconHasMask(BottomMaskBuffer);
			for (y=0; y<16; y++)
			{
				for (x=0; x<16; x++)
				{
					BOOL mask_b=ske_GetMaskBit(bmb,x);
					BOOL mask_t=ske_GetMaskBit(tmb,x);
					DWORD bottom_d=((DWORD*)bb)[x];
					DWORD top_d=((DWORD*)tb)[x];
					if (topHasMask)
					{
						if (mask_t==1 && !topHasAlpha )  top_d&=0xFFFFFF;
						else if (!topHasAlpha) top_d|=0xFF000000;
					}
					if (bottomHasMask)
					{
						if (mask_b==1 && !bottomHasAlpha) bottom_d&=0xFFFFFF;
						else if (!bottomHasAlpha) bottom_d|=0xFF000000;
					}
					((DWORD*)db)[x]=ske_Blend(bottom_d,top_d,alpha);
				}
				bb+=vstep_b;
				tb+=vstep_t;
				bmb+=vstep_bm;
				tmb+=vstep_tm;
				db+=vstep_d;
			}
		}
		if (!bmp_bottom.bmBits) free(BottomBuffer);
		if (!bmp_top.bmBits) free(TopBuffer);
		if (!bmp_bottom_mask.bmBits) free(BottomMaskBuffer);
		if (!bmp_top_mask.bmBits) free(TopMaskBuffer);
	}
	else
	{
		ske_DrawIconEx(tempDC,0,0,hBottom,16,16,0,NULL,DI_NORMAL);
		ske_DrawIconEx(tempDC,0,0,hTop,16,16,0,NULL,DI_NORMAL|(alpha<<24));
	}
	DeleteObject(iciBottom.hbmColor);
	DeleteObject(iciTop.hbmColor);
	DeleteObject(iciBottom.hbmMask);
	DeleteObject(iciTop.hbmMask);

	SelectObject(tempDC,oImage);
	DeleteDC(tempDC);
	{
		//BYTE *p=malloc(32);
		//memset(p,0,32);
		BYTE p[32] = {0};
		nMask=CreateBitmap(16,16,1,1,(void*)&p);
		{
			HDC tempDC2=CreateCompatibleDC(NULL);
			HDC tempDC3=CreateCompatibleDC(NULL);
			HBITMAP hbm=CreateCompatibleBitmap(tempDC3,16,16);
			HBITMAP obmp=(HBITMAP)SelectObject(tempDC2,nMask);
			HBITMAP obmp2=(HBITMAP)SelectObject(tempDC3,hbm);
			DrawIconEx(tempDC2,0,0,hBottom,16,16,0,NULL,DI_MASK);
			DrawIconEx(tempDC3,0,0,hTop,16,16,0,NULL,DI_MASK);
			BitBlt(tempDC2,0,0,16,16,tempDC3,0,0,SRCAND);
			SelectObject(tempDC2,obmp);
			SelectObject(tempDC3,obmp2);
			DeleteObject(hbm);
			DeleteDC(tempDC2);
			DeleteDC(tempDC3);
		}
		iNew.fIcon=TRUE;
		iNew.hbmColor=nImage;
		iNew.hbmMask=nMask;
		res=CreateIconIndirect(&iNew);
		DeleteObject(nImage);
		DeleteObject(nMask);
	}
	return res;
}

#define NEWJOINEDSTR( destination, first, separator, last)						\
	destination=(char*)alloca(strlen(first)+strlen(separator)+strlen(last)+1);	\
	if (destination) {													\
	*destination='\0';												\
	strcat(destination,first);										\
	strcat(destination,separator);									\
	strcat(destination,last);										\
	}													   

#define SKINSETSECTION "SkinnedSettings"

BOOL SkinDBGetContactSetting(HANDLE hContact, const char* szSection, const char*szKey, DBVARIANT * retdbv, BOOL * bSkined )
{
	if (!hContact) {  //only for not contact settings
		char * szSkinKey;
		NEWJOINEDSTR(szSkinKey,szSection,"@",szKey);
		if ( !ModernGetSetting(hContact, SKINSETSECTION, szSkinKey, retdbv))	{
			if (bSkined) *bSkined=TRUE;
			return FALSE;
		}	}
	// not skinned
	if (bSkined) bSkined=FALSE;
	return ModernGetSetting(hContact, szSection, szKey, retdbv);
}

BYTE SkinDBGetContactSettingByte(HANDLE hContact, const char* szSection, const char*szKey, BYTE bDefault)
{
	DBVARIANT dbv={0};
	BOOL bSkined=FALSE;
	if ( !SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkined)) {
		if (dbv.type==DBVT_BYTE)
		{
			BYTE retVal=dbv.bVal;
			ModernDBFreeVariant(&dbv);
			return retVal;
		} else {
			ModernDBFreeVariant(&dbv);
			if (!bSkined) return ModernGetSettingByte(hContact, szSection, szKey, bDefault);
		}
	}
	return bDefault;
}

WORD SkinDBGetContactSettingWord(HANDLE hContact, const char* szSection, const char*szKey, WORD wDefault)
{
	BOOL bSkined=FALSE;
	DBVARIANT dbv={0};
	if ( !SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkined)) {
		if (dbv.type==DBVT_WORD)	{
			WORD retVal=dbv.wVal;
			ModernDBFreeVariant(&dbv);
			return retVal;
		} else {
			ModernDBFreeVariant(&dbv);
			if (!bSkined) return ModernGetSettingWord(hContact, szSection, szKey, wDefault);
		}	
	}
	return wDefault;
}

DWORD SkinDBGetContactSettingDword(HANDLE hContact, const char* szSection, const char*szKey, DWORD dwDefault)
{
	DBVARIANT dbv={0};
	BOOL bSkined=FALSE;
	if ( !SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkined)) {
		if (dbv.type==DBVT_DWORD)	{
			DWORD retVal=dbv.dVal;
			ModernDBFreeVariant(&dbv);
			return retVal;
		} else {
			ModernDBFreeVariant(&dbv);
			if (!bSkined) return ModernGetSettingDword(hContact, szSection, szKey, dwDefault);
		}	
	}
	return dwDefault;
}