while (::GetAsyncKeyState( vKey ) & 0x8000U)
					::Sleep( 10 );

// Nestopia - NES/Famicom emulator written in C++
// This file is part of Nestopia.

#include "language/resource.h"
#include "NstResourceCursor.hpp"
#include "NstResourceVersion.hpp"
#include "NstApplicationException.hpp"
#include "NstApplicationLanguage.hpp"
#include <CommCtrl.h>
#include <ObjBase.h>
#include <Shlobj.h>
#include <Shellapi.h>

#if NST_MSVC
#pragma comment(lib,"comctl32")
#endif

namespace Nestopia
{
	namespace Application
	{
		const wchar_t Instance::appClassName[] = L"Nestopia";

		struct Instance::Global
		{
			Global();

			struct Paths
			{
				explicit Paths(HINSTANCE);

				Path exePath;
				Path wrkPath;
				Path configPath;//bg configuration files path
			};

			struct Hooks
			{
				explicit Hooks(HINSTANCE);
				~Hooks();

				void OnCreate(const CREATESTRUCT&,HWND);
				void OnDestroy(HWND);

				static LRESULT CALLBACK CBTProc(int,WPARAM,LPARAM);

				enum
				{
					CHILDREN_RESERVE = 8
				};

				typedef Collection::Vector<HWND> Children;

				HHOOK const handle;
				HWND window;
				Children children;
			};

			const Paths paths;
			const Resource::Version version;
			Language language;
			Hooks hooks;
			IconStyle iconStyle;
		};

		Instance::Global Instance::global;
		Instance::Events::Callbacks Instance::Events::callbacks;

		Instance::Global::Paths::Paths(HINSTANCE const hInstance)
		{
			uint length;

			do
			{
				exePath.Reserve( exePath.Capacity() + 255 );
				length = ::GetModuleFileName( hInstance, exePath.Ptr(), exePath.Capacity() );
			}
			while (length == exePath.Capacity());

			exePath.ShrinkTo( length );
			exePath.MakePretty( false );
			exePath.Defrag();

			wrkPath = ".";
			wrkPath.MakeAbsolute( true );
			wrkPath.Defrag();

			configPath.Reserve(configPath.Capacity() + 255);//bg

			//bg
			bool isLua = false;

			//bg check -lua between command line options
			int nArgs;
			LPWSTR *szArglist = ::CommandLineToArgvW(::GetCommandLineW(), &nArgs);
			if( szArglist != NULL) {
				for(int i=0; i<nArgs; i++) {
					isLua = ::wcscmp(szArglist[i],L"-lua") == 0;
					if (isLua) break;
				}

			   ::LocalFree(szArglist);
			}

			//bg END check

			//bg set configPath
			if(isLua &&

					SUCCEEDED(SHGetFolderPath(NULL,
								 CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE,
								 NULL,
								 0,
								 configPath.Ptr()))) {
									 configPath.ShrinkTo( std::wcslen(configPath.Ptr()) );
									 configPath.MakePretty( false );
									 configPath.Append (L"\\Nestopia",9);
									 configPath.MakePretty( false );
									 configPath.Defrag();
									 ::CreateDirectory(configPath.Ptr(), NULL);
				}

			else {
				::GetModuleFileName( hInstance, configPath.Ptr(), configPath.Capacity() );
				configPath.ShrinkTo(configPath.FindLastOf('\\',configPath.Capacity()));//bg fix issue #265
			};

			configPath.Defrag();
		}

		Instance::Global::Hooks::Hooks(HINSTANCE const hInstance)
		: handle(::SetWindowsHookEx( WH_CBT, CBTProc, hInstance, ::GetCurrentThreadId() ))
		{
			children.Reserve( CHILDREN_RESERVE );
		}

		Instance::Global::Hooks::~Hooks()
		{
			if (handle)
				::UnhookWindowsHookEx( handle );
		}

		#ifdef NST_MSVC_OPTIMIZE
		#pragma optimize("t", on)
		#endif

		void Instance::Global::Hooks::OnCreate(const CREATESTRUCT& createStruct,HWND const hWnd)
		{
			NST_ASSERT( hWnd );

			if (window == createStruct.hwndParent || !window)
			{
				{
					static const wchar_t menuClassName[] = L"#32768"; // documented on MSDN

					enum
					{
						MAX_LENGTH = NST_MAX( sizeof(array(appClassName)) - 1,
                                     NST_MAX( sizeof(array(STATUSCLASSNAME)) - 1, sizeof(array(menuClassName)) - 1 ))
					};

					String::Stack<MAX_LENGTH,wchar_t> name;
					name.ShrinkTo( ::GetClassName( hWnd, name.Ptr(), MAX_LENGTH+1 ) );

					if (name.Equal( menuClassName ) || name.Equal( STATUSCLASSNAME ) || name.Equal( L"IME" ))
						return;

					if (window)
					{
						children.PushBack( hWnd );
						Events::Signal( EVENT_SYSTEM_BUSY );
					}
					else if (name.Equal( appClassName ))
					{
						window = hWnd;
					}
					else
					{
						return;
					}
				}

				const Events::WindowCreateParam param =
				{
					hWnd,
					createStruct.cx > 0 ? createStruct.cx : 0,
					createStruct.cy > 0 ? createStruct.cy : 0
				};

				Events::Signal( EVENT_WINDOW_CREATE, &param );
			}
		}

		void Instance::Global::Hooks::OnDestroy(HWND const hWnd)
		{
			NST_ASSERT( hWnd );

			Children::Iterator const child = children.Find( hWnd );

			if (child)
			{
				Events::Signal( EVENT_SYSTEM_BUSY );
			}
			else if (window != hWnd)
			{
				return;
			}

			{
				const Events::WindowDestroyParam param = { hWnd };
				Events::Signal( EVENT_WINDOW_DESTROY, &param );
			}

			if (child)
				children.Erase( child );
			else
				window = NULL;
		}

		LRESULT CALLBACK Instance::Global::Hooks::CBTProc(const int nCode,const WPARAM wParam,const LPARAM lParam)
		{
			NST_COMPILE_ASSERT( HCBT_CREATEWND >= 0 && HCBT_DESTROYWND >= 0 );

			switch (nCode)
			{
				case HCBT_CREATEWND:

					NST_ASSERT( lParam );

					if (const CREATESTRUCT* const createStruct = reinterpret_cast<const CBT_CREATEWND*>(lParam)->lpcs)
					{
						NST_VERIFY( wParam );
						global.hooks.OnCreate( *createStruct, reinterpret_cast<HWND>(wParam) );
					}

					return 0;

				case HCBT_DESTROYWND:

					NST_VERIFY( wParam );

					if (wParam)
						global.hooks.OnDestroy( reinterpret_cast<HWND>(wParam) );

					return 0;
			}

			return nCode < 0 ? ::CallNextHookEx( global.hooks.handle, nCode, wParam, lParam ) : 0;
		}

		#ifdef NST_MSVC_OPTIMIZE
		#pragma optimize("", on)
		#endif

		Instance::Global::Global()
		:
		paths     ( ::GetModuleHandle(NULL) ),
		version   ( paths.exePath.Ptr(), Resource::Version::PRODUCT ),
		hooks     ( ::GetModuleHandle(NULL) ),
		iconStyle ( ICONSTYLE_NES )
		{
		}

		Instance::Language& Instance::GetLanguage()
		{
			return global.language;
		}

		const String::Heap<char>& Instance::GetVersion()
		{
			return global.version;
		}

		const Path& Instance::GetExePath()
		{
			return global.paths.exePath;
		}

		const Path Instance::GetExePath(const GenericString file)
		{
			return Path( global.paths.exePath.Directory(), file );
		}

		const Path Instance::GetConfigPath(const GenericString file)//bg
		{
			return Path( global.paths.configPath, file );
		}

		const Path Instance::GetFullPath(const GenericString relPath)
		{
			Path curPath( "." );
			curPath.MakeAbsolute( true );

			const BOOL succeeded = ::SetCurrentDirectory( global.paths.wrkPath.Ptr() );

			Path wrkPath( relPath );
			wrkPath.MakeAbsolute();

			if (succeeded)
				::SetCurrentDirectory( curPath.Ptr() );

			return wrkPath;
		}

		const Path Instance::GetLongPath(wcstring const shortPath)
		{
			Path longPath;

			longPath.Reserve( 255 );
			uint length = ::GetLongPathName( shortPath, longPath.Ptr(), longPath.Capacity() + 1 );

			if (length > longPath.Capacity() + 1)
			{
				longPath.Reserve( length - 1 );
				length = ::GetLongPathName( shortPath, longPath.Ptr(), longPath.Capacity() + 1 );
			}

			if (!length)
				return shortPath;

			const wchar_t slashed = GenericString(shortPath).Back();

			longPath.ShrinkTo( length );
			longPath.MakePretty( slashed == '\\' || slashed == '/' );

			return longPath;
		}

		const Path Instance::GetTmpPath(GenericString file)
		{
			Path path;
			path.Reserve( 255 );

			if (uint length = ::GetTempPath( path.Capacity() + 1, path.Ptr() ))
			{
				if (length > path.Capacity() + 1)
				{
					path.Reserve( length - 1 );
					length = ::GetTempPath( path.Capacity() + 1, path.Ptr() );
				}

				if (length)
					path = GetLongPath( path.Ptr() );
			}

			if (path.Length())
				path.MakePretty( true );
			else
				path << ".\\";

			if (file.Empty())
				file = L"nestopia.tmp";

			path << file;

			return path;
		}

		Instance::Events::Callbacks::~Callbacks()
		{
			NST_VERIFY( Empty() );
		}

		void Instance::Events::Add(const Callback& callback)
		{
			NST_ASSERT( bool(callback) && !callbacks.Find( callback ) );
			callbacks.PushBack( callback );
		}

		void Instance::Events::Signal(const Event event,const void* param)
		{
			for (Callbacks::ConstIterator it(callbacks.Begin()), end(callbacks.End()); it != end; ++it)
				(*it)( event, param );
		}

		void Instance::Events::Remove(const void* const instance)
		{
			for (Callbacks::Iterator it(callbacks.Begin()); it != callbacks.End(); ++it)
			{
				if (it->VoidPtr() == instance)
				{
					callbacks.Erase( it );
					break;
				}
			}
		}

		Window::Generic Instance::GetMainWindow()
		{
			return global.hooks.window;
		}

		uint Instance::NumChildWindows()
		{
			return global.hooks.children.Size();
		}

		Window::Generic Instance::GetChildWindow(uint i)
		{
			return global.hooks.children[i];
		}

		Window::Generic Instance::GetActiveWindow()
		{
			HWND hWnd = ::GetActiveWindow();
			return hWnd ? hWnd : global.hooks.window;
		}

		Instance::IconStyle Instance::GetIconStyle()
		{
			return global.iconStyle;
		}

		void Instance::SetIconStyle(IconStyle style)
		{
			global.iconStyle = style;
		}

		void Instance::ShowChildWindows(bool show)
		{
			const uint state = (show ? SW_SHOWNA : SW_HIDE);

			for (Global::Hooks::Children::ConstIterator it(global.hooks.children.Begin()), end(global.hooks.children.End()); it != end; ++it)
				::ShowWindow( *it, state );
		}

		Instance::Waiter::Waiter()
		: hCursor(::SetCursor(Resource::Cursor::GetWait())) {}

		Instance::Locker::Locker()
		: hWnd(GetMainWindow()), enabled(::IsWindowEnabled(hWnd))
		{
			::EnableWindow( hWnd, false );
			::LockWindowUpdate( hWnd );
		}

		bool Instance::Locker::CheckInput(int vKey) const
		{
			if (::GetAsyncKeyState( vKey ) & 0x8000U)
			{

				return true;
			}

			return false;
		}

		Instance::Locker::~Locker()
		{
			::LockWindowUpdate( NULL );
			::EnableWindow( hWnd, enabled );
			for (MSG msg;::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE|PM_QS_INPUT ););
		}

		Instance::Instance()
		{
			if (!static_cast<const Configuration&>(cfg)["preferences"]["application"]["allow-multiple-instances"].Yes())
			{
				::CreateMutex( NULL, true, L"Nestopia Mutex" );

				if (::GetLastError() == ERROR_ALREADY_EXISTS)
				{
					if (Window::Generic window = Window::Generic::Find( appClassName ))
					{
						window.Activate();

						if (const uint length = cfg.GetStartupFile().Length())
						{
							COPYDATASTRUCT cds;

							cds.dwData = COPYDATA_OPENFILE_ID;
							cds.cbData = (length + 1) * sizeof(wchar_t);
							cds.lpData = const_cast<wchar_t*>(cfg.GetStartupFile().Ptr());

							window.Send( WM_COPYDATA, 0, &cds );
						}
					}

					throw Exception();
				}
			}

			if (global.paths.exePath.Empty())
				throw Exception( L"unicows.dll file is missing!" );

			global.language.Load( cfg );

			if (global.hooks.handle == NULL)
				throw Exception( IDS_ERR_FAILED, L"SetWindowsHookEx()" );

			if (FAILED(::CoInitializeEx( NULL, COINIT_APARTMENTTHREADED )))
				throw Exception( IDS_ERR_FAILED, L"CoInitializeEx()" );

			Object::Pod<INITCOMMONCONTROLSEX> initCtrlEx;

			initCtrlEx.dwSize = sizeof(initCtrlEx);
			initCtrlEx.dwICC = ICC_WIN95_CLASSES;
			::InitCommonControlsEx( &initCtrlEx );
		}

		Instance::~Instance()
		{
			::CoUninitialize();
		}

		void Instance::Save()
		{
			global.language.Save( cfg );
		}
	}
}


void init ()
    {

        TimerManager->init();
        LuaNetworkMgr.Init();
        ScriptMgr.init();
        LuaThreadMgr.Init();

    }

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H

#include <nel/misc/window_displayer.h>
#include <server_share/server_def.h>
#include <server_share/lua/lua_engine.h>
#include <server_share/lua/script_mgr.h>
#include <server_share/lua/lua_thread.h>
#include <server_share/lua_net/lua_network.h>
#include <server_share/i18n_def.h>
#include <server_share/timer.h>
#include <nel/net/naming_client.h>
#include <server_share/lua_net/lua_callback_client.h>

using namespace NLMISC;
using namespace NLNET;
using namespace std;

class CClientRobot : public NLNET::IService
{
public:

    /// Init the service, load the universal time.

    bool update ()
    {
        NLMISC::TTicks curr_ticks = CTime::getLocalTime();
		LocalTime.SetCurrTime(curr_ticks);
        TimerManager->tickUpdate();
        ScriptMgr.update();
        LuaNetworkMgr.Update();
        LuaThreadMgr.Update();
        return true;
    }

    void release ()
    {
		TimerManager->release();
        ScriptMgr.release();
        LuaNetworkMgr.Release();
        LuaThreadMgr.Release();
    }

};

// Service instantiation
NLNET_SERVICE_MAIN (CClientRobot, "ROBOT", "client_robot", 0, EmptyCallbackArray, "", "");


while (pxyStack != xyStack){
			pxyStack--;
			vx_uint16 x = pxyStack->x;
			vx_uint16 y = pxyStack->y;
			// look at all the neighbors for strong edge value
		for (int i = 0; i < 8; i++){
			const ago_coord2d_short_t offs = dir_offsets[i];
			vx_int16 x1 = x + offs.x;
			vx_int16 y1 = y + offs.y;
			vx_uint8 *pDst = pDstImage + y1*dstImageStrideInBytes + x1;
			if (*pDst == 127)
			{
				*pDst |= 0x80;		// *pDst = 255
				*((unsigned *)pxyStack) = (y1<<16)|x1;
				pxyStack++;
			}
		}
	}

/*

*/

#include "ago_internal.h"

static const int n_offset[][2][2] = {
	{ { -1, 0 }, { 1, 0 } },
	{ { 1, -1 }, { -1, 1 } },
	{ { 0, -1 }, { 0, 1 } },
	{ { -1, -1 }, { 1, 1 } },
};
static const ago_coord2d_short_t dir_offsets[8] = {
	{ -1, -1 },
	{ 0, -1 },
	{ +1, -1 },
	{ -1, 0 },
	{ +1, 0 },
	{ -1, +1 },
	{ 0, +1 },
	{ +1, +1 },
};

int HafCpu_CannySobel_U16_U8_3x3_L1NORM
	(
		vx_uint32     dstWidth,
		vx_uint32     dstHeight,
		vx_uint16   * pDstImage,
		vx_uint32     dstImageStrideInBytes,
		vx_uint8    * pSrcImage,
		vx_uint32     srcImageStrideInBytes,
		vx_uint8    * pLocalData
	)
{
	int x, y;
	int prefixWidth = ((intptr_t)(pDstImage)) & 15;
	prefixWidth = (prefixWidth == 0) ? 0 : (16 - prefixWidth);
	int postfixWidth = ((int)dstWidth - prefixWidth) & 15;
	int alignedWidth = (int)dstWidth - prefixWidth - postfixWidth;

	pSrcImage += srcImageStrideInBytes;
	vx_uint32 dstride = dstImageStrideInBytes >> 1;
	pDstImage += dstride;		// don't care about border. start processing from row2
	__m128i z = _mm_setzero_si128(), c6 = _mm_set1_epi16(6);
	vx_int16 *r0 = (vx_int16*)(pLocalData + 16);
	vx_int16 *r1 = r0 + ((dstWidth + 15) & ~15);

	for (y = 1; y < (int)dstHeight - 1; y++)
	{
		const vx_uint8* srow0 = pSrcImage - srcImageStrideInBytes;
		const vx_uint8* srow1 = pSrcImage;
		const vx_uint8* srow2 = pSrcImage + srcImageStrideInBytes;
		vx_uint16* drow = (vx_uint16*)pDstImage;

		for (x = 0; x < prefixWidth; x++)
		{
			vx_int16 Gx = (vx_int16)srow0[x + 1] - (vx_int16)srow0[x - 1] + (vx_int16)srow2[x + 1] - (vx_int16)srow2[x - 1] + 2 * ((vx_int16)srow1[x + 1] - (vx_int16)srow1[x - 1]);
			vx_int16 Gy = (vx_int16)srow2[x - 1] + (vx_int16)srow2[x + 1] - (vx_int16)srow0[x - 1] - (vx_int16)srow0[x + 1] + 2 * ((vx_int16)srow2[x] - (vx_int16)srow0[x]);
			vx_int16 tmp = abs(Gx) + abs(Gy);
			tmp <<= 2;
			tmp |= (HafCpu_FastAtan2_Canny(Gx, Gy) & 3);
			drow[x] = tmp;
		}

		// do vertical convolution - SSE
		x = prefixWidth;
		for (; x <= alignedWidth - 8; x += 8)
		{
			__m128i s0 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow0 + x)), z);
			__m128i s1 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow1 + x)), z);
			__m128i s2 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow2 + x)), z);
			__m128i t0 = _mm_add_epi16(_mm_add_epi16(s0, s2), _mm_slli_epi16(s1, 1));
			__m128i t1 = _mm_sub_epi16(s2, s0);
			_mm_store_si128((__m128i*)(r0 + x), t0);
			_mm_store_si128((__m128i*)(r1 + x), t1);
		}

		// do horizontal convolution, interleave the results and store them to dst - SSE
		x = prefixWidth;
		for (; x <= alignedWidth - 8; x += 8)
		{
			__m128i s0 = _mm_loadu_si128((const __m128i*)(r0 + x - 1));
			__m128i s1 = _mm_loadu_si128((const __m128i*)(r0 + x + 1));
			__m128i s2 = _mm_loadu_si128((const __m128i*)(r1 + x - 1));
			__m128i s3 = _mm_loadu_si128((const __m128i*)(r1 + x));
			__m128i s4 = _mm_loadu_si128((const __m128i*)(r1 + x + 1));

			__m128i t0 = _mm_sub_epi16(s1, s0);
			__m128i t1 = _mm_add_epi16(_mm_add_epi16(s2, s4), _mm_slli_epi16(s3, 1));
			t1 = _mm_sub_epi16(z, t1);
			for (int i = 0; i < 8; i++){
				M128I(s1).m128i_i16[i] = HafCpu_FastAtan2_Canny(M128I(t0).m128i_i16[i], M128I(t1).m128i_i16[i]);
			}
			t0 = _mm_add_epi16(_mm_abs_epi16(t0), _mm_abs_epi16(t1));
			// pack with signed saturation
			t0 = _mm_or_si128(_mm_slli_epi16(t0, 2), s1);
			// store magnitude and angle to destination
			_mm_store_si128((__m128i*)(drow + x), t0);
		}

		for (x = alignedWidth + prefixWidth - 1; x < (int)dstWidth; x++)
		{
			vx_int16 Gx = (vx_int16)srow0[x + 1] - (vx_int16)srow0[x - 1] + (vx_int16)srow2[x + 1] - (vx_int16)srow2[x - 1] + 2 * ((vx_int16)srow1[x + 1] - (vx_int16)srow1[x - 1]);
			vx_int16 Gy = (vx_int16)srow2[x - 1] + (vx_int16)srow2[x + 1] - (vx_int16)srow0[x - 1] - (vx_int16)srow0[x + 1] + 2 * ((vx_int16)srow2[x] - (vx_int16)srow0[x]);
			vx_int16 tmp = abs(Gx) + abs(Gy);
			tmp <<= 2;
			tmp |= (HafCpu_FastAtan2_Canny(Gx, Gy) & 3);
			drow[x] = tmp;
		}
		pSrcImage += srcImageStrideInBytes;
		pDstImage += dstride;
	}
	return AGO_SUCCESS;
}

// Using separable filter

int HafCpu_CannySobel_U16_U8_5x5_L1NORM
	(
		vx_uint32     dstWidth,
		vx_uint32     dstHeight,
		vx_uint16   * pDstImage,
		vx_uint32     dstImageStrideInBytes,
		vx_uint8    * pSrcImage,
		vx_uint32     srcImageStrideInBytes,
		vx_uint8    * pLocalData
	)
{
	int x, y;
	int prefixWidth = ((intptr_t)(pDstImage)) & 15;
	prefixWidth = (prefixWidth == 0) ? 0 : (16 - prefixWidth);
	int postfixWidth = ((int)dstWidth - prefixWidth) & 15;
	int alignedWidth = (int)dstWidth - prefixWidth - postfixWidth;

	__m128i z = _mm_setzero_si128(), c6 = _mm_set1_epi16(6);
	vx_uint32 dstride = dstImageStrideInBytes >> 1;
	pDstImage += 2 * dstride;		// don't care about border. start processing from row2
	pSrcImage += 2 * srcImageStrideInBytes;
	vx_int16 *r0 = (vx_int16*)(pLocalData + 16);
	vx_int16 *r1 = r0 + ((dstWidth + 15) & ~15);

	for (y = 2; y < (int)dstHeight - 2; y++)
	{
		const vx_uint8* srow0 = pSrcImage - 2 * srcImageStrideInBytes;
		const vx_uint8* srow1 = pSrcImage - srcImageStrideInBytes;
		const vx_uint8* srow2 = pSrcImage;
		const vx_uint8* srow3 = pSrcImage + srcImageStrideInBytes;
		const vx_uint8* srow4 = pSrcImage + 2 * srcImageStrideInBytes;

		vx_uint16* drow = (vx_uint16*)pDstImage;

		for (x = 0; x < prefixWidth; x++)
		{
			vx_int16 Gx = (vx_int16)srow0[x + 2] + (2 * ((vx_int16)srow0[x + 1])) - (2 * ((vx_int16)srow0[x - 1])) - (vx_int16)srow0[x - 2]
				+ 4 * ((vx_int16)srow1[x + 2] + (2 * ((vx_int16)srow1[x + 1])) - (2 * ((vx_int16)srow1[x - 1])) - (vx_int16)srow1[x - 2])
				+ 6 * ((vx_int16)srow2[x + 2] + (2 * ((vx_int16)srow2[x + 1])) - (2 * ((vx_int16)srow2[x - 1])) - (vx_int16)srow2[x - 2])
				+ 4 * ((vx_int16)srow3[x + 2] + (2 * ((vx_int16)srow3[x + 1])) - (2 * ((vx_int16)srow3[x - 1])) - (vx_int16)srow3[x - 2])
				+ (vx_int16)srow4[x + 2] + (2 * ((vx_int16)srow4[x + 1])) - (2 * ((vx_int16)srow4[x - 1])) - (vx_int16)srow4[x - 2];
			vx_int16 Gy = (vx_int16)srow4[x - 2] + (4 * (vx_int16)srow4[x - 1]) + (6 * (vx_int16)srow4[x]) + (4 * (vx_int16)srow4[x + 1]) + (vx_int16)srow4[x + 2]
				+ 2 * ((vx_int16)srow3[x - 2] + (4 * (vx_int16)srow3[x - 1]) + (6 * (vx_int16)srow3[x]) + (4 * (vx_int16)srow3[x + 1]) + (vx_int16)srow3[x + 2])
				- 2 * ((vx_int16)srow1[x - 2] + (4 * (vx_int16)srow1[x - 1]) + (6 * (vx_int16)srow1[x]) + (4 * (vx_int16)srow1[x + 1]) + (vx_int16)srow1[x + 2])
				- (vx_int16)srow0[x - 2] + (4 * (vx_int16)srow0[x - 1]) + (6 * (vx_int16)srow0[x]) + (4 * (vx_int16)srow0[x + 1]) + (vx_int16)srow0[x + 2];
			vx_int16 tmp = abs(Gx) + abs(Gy);
			tmp <<= 2;
			tmp |= (HafCpu_FastAtan2_Canny(Gx, Gy) & 3);
			drow[x] = tmp;
		}

		// do vertical convolution - SSE
		for (x = prefixWidth; x <= alignedWidth - 8; x += 8)
		{
			__m128i s0 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow0 + x)), z);
			__m128i s1 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow1 + x)), z);
			__m128i s2 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow2 + x)), z);
			__m128i s3 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow3 + x)), z);
			__m128i s4 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow4 + x)), z);

			__m128i t0 = _mm_add_epi16(_mm_slli_epi16(_mm_add_epi16(s1, s3), 2), _mm_mullo_epi16(s2, c6));
			t0 = _mm_add_epi16(t0, _mm_add_epi16(s0, s4));

			__m128i t1 = _mm_slli_epi16(_mm_sub_epi16(s3, s1), 1);
			t1 = _mm_add_epi16(t1, _mm_sub_epi16(s4, s0));
			_mm_store_si128((__m128i*)(r0 + x), t0);
			_mm_store_si128((__m128i*)(r1 + x), t1);
		}

		// do horizontal convolution, interleave the results and store them to dst - SSE
		x = prefixWidth;
		for (; x <= alignedWidth - 8; x += 8)
		{
			__m128i s0 = _mm_loadu_si128((const __m128i*)(r0 + x - 2));
			__m128i s1 = _mm_loadu_si128((const __m128i*)(r0 + x - 1));
			__m128i s2 = _mm_loadu_si128((const __m128i*)(r0 + x + 1));
			__m128i s3 = _mm_loadu_si128((const __m128i*)(r0 + x + 2));

			__m128i s4 = _mm_loadu_si128((const __m128i*)(r1 + x - 2));
			__m128i s5 = _mm_loadu_si128((const __m128i*)(r1 + x - 1));
			__m128i s6 = _mm_loadu_si128((const __m128i*)(r1 + x));
			__m128i s7 = _mm_loadu_si128((const __m128i*)(r1 + x + 1));
			__m128i s8 = _mm_loadu_si128((const __m128i*)(r1 + x + 2));

			__m128i t0 = _mm_slli_epi16(_mm_sub_epi16(s2, s1), 1);
			t0 = _mm_adds_epi16(t0, _mm_sub_epi16(s3, s0));
			__m128i t1 = _mm_slli_epi16(_mm_add_epi16(s5, s7), 2);
			s0 = _mm_mullo_epi16(s6, c6);
			t1 = _mm_add_epi16(t1, _mm_add_epi16(s4, s8));
			t1 = _mm_add_epi16(t1, s0);
			t1 = _mm_sub_epi16(z, t1);
			// find magnitude
			s0 = _mm_add_epi16(_mm_abs_epi16(t0), _mm_abs_epi16(t1));
			for (int i = 0; i < 8; i++){
				M128I(t0).m128i_i16[i] = HafCpu_FastAtan2_Canny(M128I(t0).m128i_i16[i], M128I(t1).m128i_i16[i]);
			}
			s0 = _mm_or_si128(_mm_slli_epi16(s0, 2), t0);
			// store magnitude and angle to destination
			_mm_store_si128((__m128i*)(drow + x), s0);
		}

		for (x = alignedWidth + prefixWidth - 1; x < (int)dstWidth; x++)
		{
			vx_int16 Gx = (vx_int16)srow0[x + 2] + (2 * ((vx_int16)srow0[x + 1])) - (2 * ((vx_int16)srow0[x - 1])) - (vx_int16)srow0[x - 2]
				+ 4 * ((vx_int16)srow1[x + 2] + (2 * ((vx_int16)srow1[x + 1])) - (2 * ((vx_int16)srow1[x - 1])) - (vx_int16)srow1[x - 2])
				+ 6 * ((vx_int16)srow2[x + 2] + (2 * ((vx_int16)srow2[x + 1])) - (2 * ((vx_int16)srow2[x - 1])) - (vx_int16)srow2[x - 2])
				+ 4 * ((vx_int16)srow3[x + 2] + (2 * ((vx_int16)srow3[x + 1])) - (2 * ((vx_int16)srow3[x - 1])) - (vx_int16)srow3[x - 2])
				+ (vx_int16)srow4[x + 2] + (2 * ((vx_int16)srow4[x + 1])) - (2 * ((vx_int16)srow4[x - 1])) - (vx_int16)srow4[x - 2];
			vx_int16 Gy = (vx_int16)srow4[x - 2] + (4 * (vx_int16)srow4[x - 1]) + (6 * (vx_int16)srow4[x]) + (4 * (vx_int16)srow4[x + 1]) + (vx_int16)srow4[x + 2]
				+ 2 * ((vx_int16)srow3[x - 2] + (4 * (vx_int16)srow3[x - 1]) + (6 * (vx_int16)srow3[x]) + (4 * (vx_int16)srow3[x + 1]) + (vx_int16)srow3[x + 2])
				- 2 * ((vx_int16)srow1[x - 2] + (4 * (vx_int16)srow1[x - 1]) + (6 * (vx_int16)srow1[x]) + (4 * (vx_int16)srow1[x + 1]) + (vx_int16)srow1[x + 2])
				- (vx_int16)srow0[x - 2] + (4 * (vx_int16)srow0[x - 1]) + (6 * (vx_int16)srow0[x]) + (4 * (vx_int16)srow0[x + 1]) + (vx_int16)srow0[x + 2];
			vx_int16 tmp = abs(Gx) + abs(Gy);
			tmp <<= 2;
			tmp |= (HafCpu_FastAtan2_Canny(Gx, Gy) & 3);
			drow[x] = tmp;
		}
		pSrcImage += srcImageStrideInBytes;
		pDstImage += dstride;
	}

	return AGO_SUCCESS;
}

int HafCpu_CannySobel_U16_U8_7x7_L1NORM
	(
		vx_uint32     dstWidth,
		vx_uint32     dstHeight,
		vx_uint16   * pDstImage,
		vx_uint32     dstImageStrideInBytes,
		vx_uint8    * pSrcImage,
		vx_uint32     srcImageStrideInBytes,
		vx_uint8    * pLocalData
	)
{
	int x, y;
	int prefixWidth = ((intptr_t)(pDstImage)) & 15;
	prefixWidth = (prefixWidth == 0) ? 0 : (16 - prefixWidth);
	int postfixWidth = ((int)dstWidth - prefixWidth) & 15;
	int alignedWidth = (int)dstWidth - prefixWidth - postfixWidth;

	__m128i z = _mm_setzero_si128(), c5 = _mm_set1_epi16(5), c6 = _mm_set1_epi16(6);
	__m128i c15 = _mm_set1_epi16(15), c20 = _mm_set1_epi16(20);
	__m128i clamp = _mm_set1_epi16(0x3FFF);

	vx_uint32 dstride = dstImageStrideInBytes >> 1;
	pDstImage += 3 * dstride;		// don't care about border. start processing from row2
	pSrcImage += 3 * srcImageStrideInBytes;
	vx_int16 *r0 = (vx_int16*)(pLocalData + 16);
	vx_int16 *r1 = r0 + ((dstWidth + 15) & ~15);

	for (y = 3; y < (int)dstHeight - 3; y++)
	{
		const vx_uint8* srow0 = pSrcImage - 3 * srcImageStrideInBytes;
		const vx_uint8* srow1 = pSrcImage - 2 * srcImageStrideInBytes;
		const vx_uint8* srow2 = pSrcImage - srcImageStrideInBytes;
		const vx_uint8* srow3 = pSrcImage;
		const vx_uint8* srow4 = pSrcImage + srcImageStrideInBytes;
		const vx_uint8* srow5 = pSrcImage + 2 * srcImageStrideInBytes;
		const vx_uint8* srow6 = pSrcImage + 3 * srcImageStrideInBytes;

		vx_uint16* drow = (vx_uint16*)pDstImage;

		for (x = 0; x < prefixWidth; x++)
		{
			vx_int16 Gx = (vx_int16)srow0[x + 3] + (4 * (vx_int16)srow0[x + 2]) + (5 * (vx_int16)srow0[x + 1]) - (5 * (vx_int16)srow0[x - 1]) - (4 * (vx_int16)srow0[x - 2]) - (vx_int16)srow0[x - 3]
				+ 6 * ((vx_int16)srow1[x + 3] + (4 * (vx_int16)srow1[x + 2]) + (5 * (vx_int16)srow1[x + 1]) - (5 * (vx_int16)srow1[x - 1]) - (4 * (vx_int16)srow1[x - 2]) - (vx_int16)srow1[x - 3])
				+ 15 * ((vx_int16)srow2[x + 3] + (4 * (vx_int16)srow2[x + 2]) + (5 * (vx_int16)srow2[x + 1]) - (5 * (vx_int16)srow2[x - 1]) - (4 * (vx_int16)srow2[x - 2]) - (vx_int16)srow2[x - 3])
				+ 20 * ((vx_int16)srow3[x + 3] + (4 * (vx_int16)srow3[x + 2]) + (5 * (vx_int16)srow3[x + 1]) - (5 * (vx_int16)srow3[x - 1]) - (4 * (vx_int16)srow3[x - 2]) - (vx_int16)srow3[x - 3])
				+ 15 * ((vx_int16)srow4[x + 3] + (4 * (vx_int16)srow4[x + 2]) + (5 * (vx_int16)srow4[x + 1]) - (5 * (vx_int16)srow4[x - 1]) - (4 * (vx_int16)srow4[x - 2]) - (vx_int16)srow4[x - 3])
				+ 6 * ((vx_int16)srow5[x + 3] + (4 * (vx_int16)srow5[x + 2]) + (5 * (vx_int16)srow5[x + 1]) - (5 * (vx_int16)srow5[x - 1]) - (4 * (vx_int16)srow5[x - 2]) - (vx_int16)srow5[x - 3])
				+ (vx_int16)srow6[x + 3] + (4 * (vx_int16)srow6[x + 2]) + (5 * (vx_int16)srow6[x + 1]) - (5 * (vx_int16)srow6[x - 1]) - (4 * (vx_int16)srow6[x - 2]) - (vx_int16)srow6[x - 3];
			vx_int16 Gy = (vx_int16)srow6[x - 3] + (vx_int16)srow6[x + 3] + (6 * ((vx_int16)srow6[x - 2] + (vx_int16)srow6[x + 2])) + (15 * ((vx_int16)srow6[x - 1] + (vx_int16)srow6[x + 1])) + (20 * (vx_int16)srow6[x])
				+ 4 * ((vx_int16)srow5[x - 3] + (vx_int16)srow5[x + 3] + (6 * ((vx_int16)srow5[x - 2] + (vx_int16)srow5[x + 2])) + (15 * ((vx_int16)srow5[x - 1] + (vx_int16)srow5[x + 1])) + (20 * (vx_int16)srow5[x]))
				+ 5 * ((vx_int16)srow4[x - 3] + (vx_int16)srow4[x + 3] + (6 * ((vx_int16)srow4[x - 2] + (vx_int16)srow4[x + 2])) + (15 * ((vx_int16)srow4[x - 1] + (vx_int16)srow4[x + 1])) + (20 * (vx_int16)srow4[x]))
				- 5 * ((vx_int16)srow2[x - 3] + (vx_int16)srow2[x + 3] + (6 * ((vx_int16)srow2[x - 2] + (vx_int16)srow2[x + 2])) + (15 * ((vx_int16)srow2[x - 1] + (vx_int16)srow2[x + 1])) + (20 * (vx_int16)srow2[x]))
				- 4 * ((vx_int16)srow1[x - 3] + (vx_int16)srow1[x + 3] + (6 * ((vx_int16)srow1[x - 2] + (vx_int16)srow1[x + 2])) + (15 * ((vx_int16)srow1[x - 1] + (vx_int16)srow1[x + 1])) + (20 * (vx_int16)srow1[x]))
				- ((vx_int16)srow0[x - 3] + (vx_int16)srow0[x + 3] + (6 * ((vx_int16)srow0[x - 2] + (vx_int16)srow0[x + 2])) + (15 * ((vx_int16)srow0[x - 1] + (vx_int16)srow0[x + 1])) + (20 * (vx_int16)srow0[x]));
			vx_int16 tmp = abs(Gx) + abs(Gy);
			tmp <<= 2;
			tmp |= (HafCpu_FastAtan2_Canny(Gx, Gy) & 3);
			drow[x] = tmp;
		}

		// do vertical convolution - SSE
		for (x = prefixWidth; x <= alignedWidth - 8; x += 8)
		{
			__m128i s0 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow0 + x)), z);
			__m128i s1 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow1 + x)), z);
			__m128i s2 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow2 + x)), z);
			__m128i s3 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow3 + x)), z);
			__m128i s4 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow4 + x)), z);
			__m128i s5 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow5 + x)), z);
			__m128i s6 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow6 + x)), z);

			__m128i t0 = _mm_add_epi16(_mm_mullo_epi16(_mm_add_epi16(s1, s5), c6), _mm_mullo_epi16(s3, c20));
			__m128i t2 = _mm_mullo_epi16(_mm_add_epi16(s2, s4), c15);
			t0 = _mm_add_epi16(t0, _mm_add_epi16(s0, s6));
			__m128i t1 = _mm_slli_epi16(_mm_sub_epi16(s5, s1), 2);
			t0 = _mm_add_epi16(t0, t2);

			t2 = _mm_mullo_epi16(_mm_sub_epi16(s4, s2), c5);
			t0 = _mm_srai_epi16(t0, 2);
			t1 = _mm_add_epi16(t1, _mm_sub_epi16(s6, s0));
			t1 = _mm_add_epi16(t1, t2);
			t1 = _mm_srai_epi16(t1, 2);

			_mm_store_si128((__m128i*)(r0 + x), t0);
			_mm_store_si128((__m128i*)(r1 + x), t1);
		}

		// do horizontal convolution, interleave the results and store them to dst - SSE
		x = prefixWidth;
		for (; x <= alignedWidth - 8; x += 8)
		{
			__m128i s0 = _mm_loadu_si128((const __m128i*)(r0 + x - 3));
			__m128i s1 = _mm_loadu_si128((const __m128i*)(r0 + x - 2));
			__m128i s2 = _mm_loadu_si128((const __m128i*)(r0 + x - 1));
			__m128i s3 = _mm_loadu_si128((const __m128i*)(r0 + x + 1));
			__m128i s4 = _mm_loadu_si128((const __m128i*)(r0 + x + 2));
			__m128i s5 = _mm_loadu_si128((const __m128i*)(r0 + x + 3));

			__m128i t0 = _mm_slli_epi16(_mm_subs_epi16(s4, s1), 2);
			__m128i t1 = _mm_mullo_epi16(_mm_subs_epi16(s3, s2), c5);
			t0 = _mm_adds_epi16(t0, _mm_subs_epi16(s5, s0));
			t0 = _mm_adds_epi16(t0, t1);

			s0 = _mm_loadu_si128((const __m128i*)(r1 + x - 3));
			s1 = _mm_loadu_si128((const __m128i*)(r1 + x - 2));
			s2 = _mm_loadu_si128((const __m128i*)(r1 + x - 1));
			s3 = _mm_loadu_si128((const __m128i*)(r1 + x));
			s4 = _mm_loadu_si128((const __m128i*)(r1 + x + 1));
			s5 = _mm_loadu_si128((const __m128i*)(r1 + x + 2));
			__m128i s6 = _mm_loadu_si128((const __m128i*)(r1 + x + 3));

			t1 = _mm_adds_epi16(_mm_mullo_epi16(_mm_add_epi16(s1, s5), c6), _mm_mullo_epi16(s3, c20));
			__m128i t2 = _mm_mullo_epi16(_mm_add_epi16(s2, s4), c15);
			t1 = _mm_adds_epi16(t1, _mm_adds_epi16(s0, s6));
			t1 = _mm_adds_epi16(t1, t2);
			t1 = _mm_subs_epi16(z, t1);
			// find magnitude
			s0 = _mm_add_epi16(_mm_abs_epi16(t0), _mm_abs_epi16(t1));
			s0 = _mm_min_epi16(s0, clamp);
			for (int i = 0; i < 8; i++){
				M128I(t0).m128i_i16[i] = HafCpu_FastAtan2_Canny(M128I(t0).m128i_i16[i], M128I(t1).m128i_i16[i]);
			}
			s0 = _mm_or_si128(_mm_slli_epi16(s0, 2), t0);
			// store magnitude and angle to destination
			_mm_store_si128((__m128i*)(drow + x), s0);
		}

		for (x = alignedWidth + prefixWidth - 1; x < (int)dstWidth; x++)
		{
			vx_int16 Gx = (vx_int16)srow0[x + 3] + (4 * (vx_int16)srow0[x + 2]) + (5 * (vx_int16)srow0[x + 1]) - (5 * (vx_int16)srow0[x - 1]) - (4 * (vx_int16)srow0[x - 2]) - (vx_int16)srow0[x - 3]
				+ 6 * ((vx_int16)srow1[x + 3] + (4 * (vx_int16)srow1[x + 2]) + (5 * (vx_int16)srow1[x + 1]) - (5 * (vx_int16)srow1[x - 1]) - (4 * (vx_int16)srow1[x - 2]) - (vx_int16)srow1[x - 3])
				+ 15 * ((vx_int16)srow2[x + 3] + (4 * (vx_int16)srow2[x + 2]) + (5 * (vx_int16)srow2[x + 1]) - (5 * (vx_int16)srow2[x - 1]) - (4 * (vx_int16)srow2[x - 2]) - (vx_int16)srow2[x - 3])
				+ 20 * ((vx_int16)srow3[x + 3] + (4 * (vx_int16)srow3[x + 2]) + (5 * (vx_int16)srow3[x + 1]) - (5 * (vx_int16)srow3[x - 1]) - (4 * (vx_int16)srow3[x - 2]) - (vx_int16)srow3[x - 3])
				+ 15 * ((vx_int16)srow4[x + 3] + (4 * (vx_int16)srow4[x + 2]) + (5 * (vx_int16)srow4[x + 1]) - (5 * (vx_int16)srow4[x - 1]) - (4 * (vx_int16)srow4[x - 2]) - (vx_int16)srow4[x - 3])
				+ 6 * ((vx_int16)srow5[x + 3] + (4 * (vx_int16)srow5[x + 2]) + (5 * (vx_int16)srow5[x + 1]) - (5 * (vx_int16)srow5[x - 1]) - (4 * (vx_int16)srow5[x - 2]) - (vx_int16)srow5[x - 3])
				+ (vx_int16)srow6[x + 3] + (4 * (vx_int16)srow6[x + 2]) + (5 * (vx_int16)srow6[x + 1]) - (5 * (vx_int16)srow6[x - 1]) - (4 * (vx_int16)srow6[x - 2]) - (vx_int16)srow6[x - 3];
			vx_int16 Gy = (vx_int16)srow6[x - 3] + (vx_int16)srow6[x + 3] + (6 * ((vx_int16)srow6[x - 2] + (vx_int16)srow6[x + 2])) + (15 * ((vx_int16)srow6[x - 1] + (vx_int16)srow6[x + 1])) + (20 * (vx_int16)srow6[x])
				+ 4 * ((vx_int16)srow5[x - 3] + (vx_int16)srow5[x + 3] + (6 * ((vx_int16)srow5[x - 2] + (vx_int16)srow5[x + 2])) + (15 * ((vx_int16)srow5[x - 1] + (vx_int16)srow5[x + 1])) + (20 * (vx_int16)srow5[x]))
				+ 5 * ((vx_int16)srow4[x - 3] + (vx_int16)srow4[x + 3] + (6 * ((vx_int16)srow4[x - 2] + (vx_int16)srow4[x + 2])) + (15 * ((vx_int16)srow4[x - 1] + (vx_int16)srow4[x + 1])) + (20 * (vx_int16)srow4[x]))
				- 5 * ((vx_int16)srow2[x - 3] + (vx_int16)srow2[x + 3] + (6 * ((vx_int16)srow2[x - 2] + (vx_int16)srow2[x + 2])) + (15 * ((vx_int16)srow2[x - 1] + (vx_int16)srow2[x + 1])) + (20 * (vx_int16)srow2[x]))
				- 4 * ((vx_int16)srow1[x - 3] + (vx_int16)srow1[x + 3] + (6 * ((vx_int16)srow1[x - 2] + (vx_int16)srow1[x + 2])) + (15 * ((vx_int16)srow1[x - 1] + (vx_int16)srow1[x + 1])) + (20 * (vx_int16)srow1[x]))
				- ((vx_int16)srow0[x - 3] + (vx_int16)srow0[x + 3] + (6 * ((vx_int16)srow0[x - 2] + (vx_int16)srow0[x + 2])) + (15 * ((vx_int16)srow0[x - 1] + (vx_int16)srow0[x + 1])) + (20 * (vx_int16)srow0[x]));
			vx_int16 tmp = abs(Gx) + abs(Gy);
			tmp <<= 2;
			tmp |= (HafCpu_FastAtan2_Canny(Gx, Gy) & 3);
			drow[x] = tmp;
		}
		pSrcImage += srcImageStrideInBytes;
		pDstImage += dstride;
	}
	return AGO_SUCCESS;
}

int HafCpu_CannySobelSuppThreshold_U8XY_U8_3x3_L1NORM
	(
		vx_uint32              capacityOfXY,
		ago_coord2d_ushort_t   xyStack[],
		vx_uint32            * pxyStackTop,
		vx_uint32              dstWidth,
		vx_uint32              dstHeight,
		vx_uint8             * pDst,
		vx_uint32              dstStrideInBytes,
		vx_uint8             * pSrcImage,
		vx_uint32              srcImageStrideInBytes,
		vx_uint16               hyst_lower,
		vx_uint16               hyst_upper,
		vx_uint8			 * pScratch
	)
{
	vx_int16 *Gx, *Gy;
	vx_uint8 * pTemp;
	vx_uint32 dstride = ((dstWidth + 15)&~15);
	Gx = (vx_int16 *)pScratch;
	Gy = (vx_int16 *)(pScratch + dstride*sizeof(vx_int16));
	pTemp = pScratch + 2*dstride*sizeof(vx_int16);

	// compute Sobel gradients
	HafCpu_Sobel_S16S16_U8_3x3_GXY(dstWidth, dstHeight - 2, Gx + dstride, dstride * 2, Gy + dstride, dstride * 2, pSrcImage + srcImageStrideInBytes, srcImageStrideInBytes, pTemp);

	// compute L1 norm and phase
	unsigned int y = 1;
	vx_int16 *pGx = Gx + dstride;
	vx_int16 *pGy = Gy + dstride;
	vx_int16 *pMag = Gx;
	while (y < dstHeight)
	{
		vx_uint16 *pdst = (vx_uint16*)pMag;		// to store the result

		for (unsigned int x = 1; x < dstWidth; x++)
		{
			vx_uint8 orn;	// orientation

			float scale = (float)128 / 180.f;
			float arct = HafCpu_FastAtan2_deg(pGx[x], pGy[x]);
			// normalize and convert to degrees 0-180
			orn = (((int)(arct*scale) + 16) >> 5)&7;		// quantize to 8 (22.5 degrees)
			if (orn >= 4)orn -= 4;
			vx_int16 val = (vx_int16)(abs(pGx[x]) + abs(pGy[x]));
			pdst[x] = (vx_uint16)((val << 2) | orn);				// store both mag and orientation
		}
		pGx += dstride;
		pGy += dstride;
		pMag += dstride;
		y++;
	}

	// do minmax suppression: from Gx
	ago_coord2d_ushort_t *pxyStack = xyStack;
	for (y = 1; y < dstHeight - 1; y++)
	{
		vx_uint8* pOut = pDst + y*dstStrideInBytes;
		vx_int16 *pSrc = (vx_int16 *)(Gx + y * dstride);	// we are processing from 2nd row
		for (unsigned int x = 1; x < dstWidth - 1; x++, pSrc++)
		{
			vx_int32 edge;
			// get the Mag and angle
			int mag = (pSrc[0] >> 2);
			int ang = pSrc[0] & 3;
			int offset0 = n_offset[ang][0][1] * dstride + n_offset[ang][0][0];
			int offset1 = n_offset[ang][1][1] * dstride + n_offset[ang][1][0];
			edge = ((mag >(pSrc[offset0] >> 2)) && (mag >(pSrc[offset1] >> 2))) ? mag : 0;
			if (edge > hyst_upper){
				pOut[x] = (vx_int8)255;
				// add the cordinates to stacktop
				pxyStack->x = x;	// store x and y co-ordinates
				pxyStack->y = y;	// store x and y co-ordinates
				pxyStack++;
			}
			else if (edge <= hyst_lower){
				pOut[x] = 0;
			}
			else pOut[x] = 127;
		}
	}
	*pxyStackTop = (vx_uint32)(pxyStack - xyStack);

	return AGO_SUCCESS;
}

int HafCpu_CannySobelSuppThreshold_U8XY_U8_5x5_L1NORM
	(
		vx_uint32              capacityOfXY,
		ago_coord2d_ushort_t   xyStack[],
		vx_uint32            * pxyStackTop,
		vx_uint32              dstWidth,
		vx_uint32              dstHeight,
		vx_uint8             * pDst,
		vx_uint32              dstStrideInBytes,
		vx_uint8             * pSrcImage,
		vx_uint32              srcImageStrideInBytes,
		vx_uint16               hyst_lower,
		vx_uint16               hyst_upper
	)
{
	return AGO_ERROR_HAFCPU_NOT_IMPLEMENTED;
}

int HafCpu_CannySobelSuppThreshold_U8XY_U8_7x7_L1NORM
	(
		vx_uint32              capacityOfXY,
		ago_coord2d_ushort_t   xyStack[],
		vx_uint32            * pxyStackTop,
		vx_uint32              dstWidth,
		vx_uint32              dstHeight,
		vx_uint8             * pDst,
		vx_uint32              dstStrideInBytes,
		vx_uint8             * pSrcImage,
		vx_uint32              srcImageStrideInBytes,
		vx_uint16               hyst_lower,
		vx_uint16               hyst_upper
	)
{
	return AGO_ERROR_HAFCPU_NOT_IMPLEMENTED;
}

int HafCpu_CannySuppThreshold_U8XY_U16_3x3
	(
		vx_uint32              capacityOfXY,
		ago_coord2d_ushort_t   xyStack[],
		vx_uint32            * pxyStackTop,
		vx_uint32              dstWidth,
		vx_uint32              dstHeight,
		vx_uint8             * pDst,
		vx_uint32              dstStrideInBytes,
		vx_uint16            * pSrc,
		vx_uint32              srcStrideInBytes,
		vx_uint16               hyst_lower,
		vx_uint16               hyst_upper
	)
{
	// do minmax suppression: from Gx
	vx_uint32 sstride = srcStrideInBytes>>1;
	ago_coord2d_ushort_t *pxyStack = xyStack;
	for (unsigned int y = 1; y < dstHeight - 1; y++)
	{
		vx_uint8* pOut = pDst + y*dstStrideInBytes;
		vx_uint16 *pLocSrc = pSrc + y * sstride + 1;	// we are processing from 2nd row
		for (unsigned int x = 1; x < dstWidth - 1; x++, pLocSrc++)
		{
			vx_int32 edge;
			// get the Mag and angle
			int mag = (pLocSrc[0] >> 2);
			int ang = pLocSrc[0] & 3;
			int offset0 = n_offset[ang][0][1] * sstride + n_offset[ang][0][0];
			int offset1 = n_offset[ang][1][1] * sstride + n_offset[ang][1][0];
			edge = ((mag >(pLocSrc[offset0] >> 2)) && (mag >(pLocSrc[offset1] >> 2))) ? mag : 0;
			if (edge > hyst_upper){
				pOut[x] = (vx_int8)255;
				// add the cordinates to stacktop
				pxyStack->x = x;	// store x and y co-ordinates
				pxyStack->y = y;	// store x and y co-ordinates
				pxyStack++;

			}
			else if (edge <= hyst_lower){
				pOut[x] = 0;
			}
			else pOut[x] = 127;
		}
	}
	*pxyStackTop = (vx_uint32)(pxyStack - xyStack);
	return AGO_SUCCESS;
}

int HafCpu_CannyEdgeTrace_U8_U8XY
	(
		vx_uint32              dstWidth,
		vx_uint32              dstHeight,
		vx_uint8             * pDstImage,
		vx_uint32              dstImageStrideInBytes,
		vx_uint32              capacityOfXY,
		ago_coord2d_ushort_t   xyStack[],
		vx_uint32              xyStackTop
	)
{
	ago_coord2d_ushort_t *pxyStack = xyStack + xyStackTop;

	// go through the entire destination and convert all 127 to 0
	const __m128i mm127 = _mm_set1_epi8((char)127);
	for (unsigned int y = 0; y < dstHeight; y++) {
		__m128i * src = (__m128i *)pDstImage;
		vx_uint32 width = (dstWidth + 15) >> 4;

		for (unsigned int x = 0; x < width; x++) {
			__m128i mask;
			__m128i pixels = _mm_load_si128(src);
			mask = _mm_cmpeq_epi8(pixels, mm127);
			pixels = _mm_andnot_si128(mask, pixels);
			_mm_store_si128(src++, pixels);
		}
		pDstImage += dstImageStrideInBytes;
	}
	return AGO_SUCCESS;
}

int HafCpu_CannySobel_U16_U8_3x3_L2NORM
(
	vx_uint32     dstWidth,
	vx_uint32     dstHeight,
	vx_uint16   * pDstImage,
	vx_uint32     dstImageStrideInBytes,
	vx_uint8    * pSrcImage,
	vx_uint32     srcImageStrideInBytes,
	vx_uint8    * pLocalData
)
{
	int x, y;
	int prefixWidth = ((intptr_t)(pDstImage)) & 15;
	prefixWidth = (prefixWidth == 0) ? 0 : (16 - prefixWidth);
	int postfixWidth = ((int)dstWidth - prefixWidth) & 15;
	int alignedWidth = (int)dstWidth - prefixWidth - postfixWidth;

	pSrcImage += srcImageStrideInBytes;
	vx_uint32 dstride = dstImageStrideInBytes >> 1;
	pDstImage += dstride;		// don't care about border. start processing from row2
	__m128i z = _mm_setzero_si128(), c6 = _mm_set1_epi16(6);
	vx_int16 *r0 = (vx_int16*)(pLocalData + 16);
	vx_int16 *r1 = r0 + ((dstWidth + 15) & ~15);

	for (y = 1; y < (int)dstHeight - 1; y++)
	{
		const vx_uint8* srow0 = pSrcImage - srcImageStrideInBytes;
		const vx_uint8* srow1 = pSrcImage;
		const vx_uint8* srow2 = pSrcImage + srcImageStrideInBytes;
		vx_uint16* drow = (vx_uint16*)pDstImage;

		for (x = 0; x < prefixWidth; x++)
		{
			vx_int16 Gx = (vx_int16)srow0[x + 1] - (vx_int16)srow0[x - 1] + (vx_int16)srow2[x + 1] - (vx_int16)srow2[x - 1] + 2 * ((vx_int16)srow1[x + 1] - (vx_int16)srow1[x - 1]);
			vx_int16 Gy = (vx_int16)srow2[x - 1] + (vx_int16)srow2[x + 1] - (vx_int16)srow0[x - 1] - (vx_int16)srow0[x + 1] + 2 * ((vx_int16)srow2[x] - (vx_int16)srow0[x]);
			vx_int16 tmp = (vx_int16)sqrt((Gx*Gx) + (Gy*Gy));
			tmp <<= 2;
			tmp |= (HafCpu_FastAtan2_Canny(Gx, Gy) & 3);
			drow[x] = tmp;
		}

		// do vertical convolution - SSE
		x = prefixWidth;
		for (; x <= alignedWidth - 8; x += 8)
		{
			__m128i s0 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow0 + x)), z);
			__m128i s1 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow1 + x)), z);
			__m128i s2 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow2 + x)), z);
			__m128i t0 = _mm_add_epi16(_mm_add_epi16(s0, s2), _mm_slli_epi16(s1, 1));
			__m128i t1 = _mm_sub_epi16(s2, s0);
			_mm_store_si128((__m128i*)(r0 + x), t0);
			_mm_store_si128((__m128i*)(r1 + x), t1);
		}

		// do horizontal convolution, interleave the results and store them to dst - SSE
		x = prefixWidth;
		for (; x <= alignedWidth - 8; x += 8)
		{
			__m128i s0 = _mm_loadu_si128((const __m128i*)(r0 + x - 1));
			__m128i s1 = _mm_loadu_si128((const __m128i*)(r0 + x + 1));
			__m128i s2 = _mm_loadu_si128((const __m128i*)(r1 + x - 1));
			__m128i s3 = _mm_loadu_si128((const __m128i*)(r1 + x));
			__m128i s4 = _mm_loadu_si128((const __m128i*)(r1 + x + 1));

			__m128i t0 = _mm_sub_epi16(s1, s0);
			__m128i t1 = _mm_add_epi16(_mm_add_epi16(s2, s4), _mm_slli_epi16(s3, 1));
			t1 = _mm_sub_epi16(z, t1);
			s0 = _mm_mullo_epi16(t0, t0);
			s1 = _mm_mullo_epi16(t1, t1);
			// unpack to dwords for multiplication
			s2 = _mm_unpackhi_epi16(s0, z);
			s0 = _mm_unpacklo_epi16(s0, z);
			s3 = _mm_unpackhi_epi16(s1, z);
			s1 = _mm_unpacklo_epi16(s1, z);
			__m128 f0 = _mm_cvtepi32_ps(s0);
			__m128 f1 = _mm_cvtepi32_ps(s2);
			__m128 f2 = _mm_cvtepi32_ps(s1);
			__m128 f3 = _mm_cvtepi32_ps(s3);
			f0 = _mm_add_ps(f0, f2);
			f1 = _mm_add_ps(f1, f3);
			f0 = _mm_sqrt_ps(f0);
			f1 = _mm_sqrt_ps(f1);

			for (int i = 0; i < 8; i++){
				M128I(s1).m128i_i16[i] = HafCpu_FastAtan2_Canny(M128I(t0).m128i_i16[i], M128I(t1).m128i_i16[i]);
			}
			t0 = _mm_cvtps_epi32(f0);
			t1 = _mm_cvtps_epi32(f1);
			// pack with signed saturation
			t0 = _mm_packus_epi32(t0, t1);
			t0 = _mm_or_si128(_mm_slli_epi16(t0, 2), s1);
			// store magnitude and angle to destination
			_mm_store_si128((__m128i*)(drow + x), t0);
		}

		for (x = alignedWidth + prefixWidth - 1; x < (int)dstWidth; x++)
		{
			vx_int16 Gx = (vx_int16)srow0[x + 1] - (vx_int16)srow0[x - 1] + (vx_int16)srow2[x + 1] - (vx_int16)srow2[x - 1] + 2 * ((vx_int16)srow1[x + 1] - (vx_int16)srow1[x - 1]);
			vx_int16 Gy = (vx_int16)srow2[x - 1] + (vx_int16)srow2[x + 1] - (vx_int16)srow0[x - 1] - (vx_int16)srow0[x + 1] + 2 * ((vx_int16)srow2[x] - (vx_int16)srow0[x]);
			vx_int16 tmp = (vx_int16)sqrt((Gx*Gx) + (Gy*Gy));
			tmp <<= 2;
			tmp |= (HafCpu_FastAtan2_Canny(Gx, Gy) & 3);
			drow[x] = tmp;
		}
		pSrcImage += srcImageStrideInBytes;
		pDstImage += dstride;
	}
	return AGO_SUCCESS;
}

int HafCpu_CannySobel_U16_U8_5x5_L2NORM
	(
	vx_uint32     dstWidth,
	vx_uint32     dstHeight,
	vx_uint16   * pDstImage,
	vx_uint32     dstImageStrideInBytes,
	vx_uint8    * pSrcImage,
	vx_uint32     srcImageStrideInBytes,
	vx_uint8    * pLocalData
	)
{
	int x, y;
	int prefixWidth = ((intptr_t)(pDstImage)) & 15;
	prefixWidth = (prefixWidth == 0) ? 0 : (16 - prefixWidth);
	int postfixWidth = ((int)dstWidth - prefixWidth) & 15;
	int alignedWidth = (int)dstWidth - prefixWidth - postfixWidth;

	__m128i z = _mm_setzero_si128(), c6 = _mm_set1_epi16(6);
	vx_uint32 dstride = dstImageStrideInBytes >> 1;
	pDstImage += 2 * dstride;		// don't care about border. start processing from row2
	pSrcImage += 2 * srcImageStrideInBytes;
	vx_int16 *r0 = (vx_int16*)(pLocalData + 16);
	vx_int16 *r1 = r0 + ((dstWidth + 15) & ~15);

	for (y = 2; y < (int)dstHeight - 2; y++)
	{
		const vx_uint8* srow0 = pSrcImage - 2 * srcImageStrideInBytes;
		const vx_uint8* srow1 = pSrcImage - srcImageStrideInBytes;
		const vx_uint8* srow2 = pSrcImage;
		const vx_uint8* srow3 = pSrcImage + srcImageStrideInBytes;
		const vx_uint8* srow4 = pSrcImage + 2 * srcImageStrideInBytes;

		vx_uint16* drow = (vx_uint16*)pDstImage;

		for (x = 0; x < prefixWidth; x++)
		{
			vx_int16 Gx = (vx_int16)srow0[x + 2] + (2 * ((vx_int16)srow0[x + 1])) - (2 * ((vx_int16)srow0[x - 1])) - (vx_int16)srow0[x - 2]
				+ 4 * ((vx_int16)srow1[x + 2] + (2 * ((vx_int16)srow1[x + 1])) - (2 * ((vx_int16)srow1[x - 1])) - (vx_int16)srow1[x - 2])
				+ 6 * ((vx_int16)srow2[x + 2] + (2 * ((vx_int16)srow2[x + 1])) - (2 * ((vx_int16)srow2[x - 1])) - (vx_int16)srow2[x - 2])
				+ 4 * ((vx_int16)srow3[x + 2] + (2 * ((vx_int16)srow3[x + 1])) - (2 * ((vx_int16)srow3[x - 1])) - (vx_int16)srow3[x - 2])
				+ (vx_int16)srow4[x + 2] + (2 * ((vx_int16)srow4[x + 1])) - (2 * ((vx_int16)srow4[x - 1])) - (vx_int16)srow4[x - 2];
			vx_int16 Gy = (vx_int16)srow4[x - 2] + (4 * (vx_int16)srow4[x - 1]) + (6 * (vx_int16)srow4[x]) + (4 * (vx_int16)srow4[x + 1]) + (vx_int16)srow4[x + 2]
				+ 2 * ((vx_int16)srow3[x - 2] + (4 * (vx_int16)srow3[x - 1]) + (6 * (vx_int16)srow3[x]) + (4 * (vx_int16)srow3[x + 1]) + (vx_int16)srow3[x + 2])
				- 2 * ((vx_int16)srow1[x - 2] + (4 * (vx_int16)srow1[x - 1]) + (6 * (vx_int16)srow1[x]) + (4 * (vx_int16)srow1[x + 1]) + (vx_int16)srow1[x + 2])
				- (vx_int16)srow0[x - 2] + (4 * (vx_int16)srow0[x - 1]) + (6 * (vx_int16)srow0[x]) + (4 * (vx_int16)srow0[x + 1]) + (vx_int16)srow0[x + 2];
			vx_int16 tmp = (vx_int16)sqrt((Gx*Gx) + (Gy*Gy));
			tmp <<= 2;
			tmp |= (HafCpu_FastAtan2_Canny(Gx, Gy) & 3);
			drow[x] = tmp;
		}

		// do vertical convolution
		for (x = prefixWidth; x <= alignedWidth - 8; x += 8)
		{
			__m128i s0 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow0 + x)), z);
			__m128i s1 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow1 + x)), z);
			__m128i s2 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow2 + x)), z);
			__m128i s3 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow3 + x)), z);
			__m128i s4 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow4 + x)), z);

			__m128i t0 = _mm_add_epi16(_mm_slli_epi16(_mm_add_epi16(s1, s3), 2), _mm_mullo_epi16(s2, c6));
			t0 = _mm_add_epi16(t0, _mm_add_epi16(s0, s4));

			__m128i t1 = _mm_slli_epi16(_mm_sub_epi16(s3, s1), 1);
			t1 = _mm_add_epi16(t1, _mm_sub_epi16(s4, s0));
			_mm_store_si128((__m128i*)(r0 + x), t0);
			_mm_store_si128((__m128i*)(r1 + x), t1);
		}

		// do horizontal convolution, interleave the results and store them to dst
		x = prefixWidth;
		for (; x <= alignedWidth - 8; x += 8)
		{
			__m128i s0 = _mm_loadu_si128((const __m128i*)(r0 + x - 2));
			__m128i s1 = _mm_loadu_si128((const __m128i*)(r0 + x - 1));
			__m128i s2 = _mm_loadu_si128((const __m128i*)(r0 + x + 1));
			__m128i s3 = _mm_loadu_si128((const __m128i*)(r0 + x + 2));

			__m128i s4 = _mm_loadu_si128((const __m128i*)(r1 + x - 2));
			__m128i s5 = _mm_loadu_si128((const __m128i*)(r1 + x - 1));
			__m128i s6 = _mm_loadu_si128((const __m128i*)(r1 + x));
			__m128i s7 = _mm_loadu_si128((const __m128i*)(r1 + x + 1));
			__m128i s8 = _mm_loadu_si128((const __m128i*)(r1 + x + 2));

			__m128i t0 = _mm_slli_epi16(_mm_sub_epi16(s2, s1), 1);
			t0 = _mm_adds_epi16(t0, _mm_sub_epi16(s3, s0));
			__m128i t1 = _mm_slli_epi16(_mm_add_epi16(s5, s7), 2);
			s0 = _mm_mullo_epi16(s6, c6);
			t1 = _mm_add_epi16(t1, _mm_add_epi16(s4, s8));
			t1 = _mm_adds_epi16(t1, s0);
			t1 = _mm_sub_epi16(z, t1);
			// unpack for multiplication
			s0 = _mm_unpacklo_epi16(t0, t1);
			s2 = _mm_unpackhi_epi16(t0, t1);
			s0 = _mm_madd_epi16(s0, s0);
			s2 = _mm_madd_epi16(s2, s2);

			__m128 f0 = _mm_cvtepi32_ps(s0);
			__m128 f1 = _mm_cvtepi32_ps(s2);
			f0 = _mm_sqrt_ps(f0);
			f1 = _mm_sqrt_ps(f1);

			for (int i = 0; i < 8; i++){
				M128I(s1).m128i_i16[i] = HafCpu_FastAtan2_Canny(M128I(t0).m128i_i16[i], M128I(t1).m128i_i16[i]);
			}
			t0 = _mm_cvtps_epi32(f0);
			t1 = _mm_cvtps_epi32(f1);
			// pack with signed saturation
			t0 = _mm_packus_epi32(t0, t1);
			t0 = _mm_or_si128(_mm_slli_epi16(t0, 2), s1);
			// store magnitude and angle to destination
			_mm_store_si128((__m128i*)(drow + x), t0);
		}

		for (x = alignedWidth + prefixWidth - 1; x < (int)dstWidth; x++)
		{
			vx_int16 Gx = (vx_int16)srow0[x + 2] + (2 * ((vx_int16)srow0[x + 1])) - (2 * ((vx_int16)srow0[x - 1])) - (vx_int16)srow0[x - 2]
				+ 4 * ((vx_int16)srow1[x + 2] + (2 * ((vx_int16)srow1[x + 1])) - (2 * ((vx_int16)srow1[x - 1])) - (vx_int16)srow1[x - 2])
				+ 6 * ((vx_int16)srow2[x + 2] + (2 * ((vx_int16)srow2[x + 1])) - (2 * ((vx_int16)srow2[x - 1])) - (vx_int16)srow2[x - 2])
				+ 4 * ((vx_int16)srow3[x + 2] + (2 * ((vx_int16)srow3[x + 1])) - (2 * ((vx_int16)srow3[x - 1])) - (vx_int16)srow3[x - 2])
				+ (vx_int16)srow4[x + 2] + (2 * ((vx_int16)srow4[x + 1])) - (2 * ((vx_int16)srow4[x - 1])) - (vx_int16)srow4[x - 2];
			vx_int16 Gy = (vx_int16)srow4[x - 2] + (4 * (vx_int16)srow4[x - 1]) + (6 * (vx_int16)srow4[x]) + (4 * (vx_int16)srow4[x + 1]) + (vx_int16)srow4[x + 2]
				+ 2 * ((vx_int16)srow3[x - 2] + (4 * (vx_int16)srow3[x - 1]) + (6 * (vx_int16)srow3[x]) + (4 * (vx_int16)srow3[x + 1]) + (vx_int16)srow3[x + 2])
				- 2 * ((vx_int16)srow1[x - 2] + (4 * (vx_int16)srow1[x - 1]) + (6 * (vx_int16)srow1[x]) + (4 * (vx_int16)srow1[x + 1]) + (vx_int16)srow1[x + 2])
				- (vx_int16)srow0[x - 2] + (4 * (vx_int16)srow0[x - 1]) + (6 * (vx_int16)srow0[x]) + (4 * (vx_int16)srow0[x + 1]) + (vx_int16)srow0[x + 2];
			vx_int16 tmp = (vx_int16)sqrt((Gx*Gx) + (Gy*Gy));
			tmp <<= 2;
			tmp |= (HafCpu_FastAtan2_Canny(Gx, Gy) & 3);
			drow[x] = tmp;
		}

		pSrcImage += srcImageStrideInBytes;
		pDstImage += dstride;
	}
	return AGO_SUCCESS;
}

int HafCpu_CannySobel_U16_U8_7x7_L2NORM
	(
	vx_uint32     dstWidth,
	vx_uint32     dstHeight,
	vx_uint16   * pDstImage,
	vx_uint32     dstImageStrideInBytes,
	vx_uint8    * pSrcImage,
	vx_uint32     srcImageStrideInBytes,
	vx_uint8    * pLocalData
	)
{
	int x, y;
	int prefixWidth = ((intptr_t)(pDstImage)) & 15;
	prefixWidth = (prefixWidth == 0) ? 0 : (16 - prefixWidth);
	int postfixWidth = ((int)dstWidth - prefixWidth) & 15;
	int alignedWidth = (int)dstWidth - prefixWidth - postfixWidth;

	__m128i z = _mm_setzero_si128(), c5 = _mm_set1_epi16(5), c6 = _mm_set1_epi16(6);
	__m128i c15 = _mm_set1_epi16(15), c20 = _mm_set1_epi16(20);
	__m128i clamp = _mm_set1_epi16(0x3FFF);

	vx_uint32 dstride = dstImageStrideInBytes >> 1;
	pDstImage += 3 * dstride;		// don't care about border. start processing from row2
	pSrcImage += 3 * srcImageStrideInBytes;
	vx_int16 *r0 = (vx_int16*)(pLocalData + 16);
	vx_int16 *r1 = r0 + ((dstWidth + 15) & ~15);

	for (y = 3; y < (int)dstHeight - 3; y++)
	{
		const vx_uint8* srow0 = pSrcImage - 3 * srcImageStrideInBytes;
		const vx_uint8* srow1 = pSrcImage - 2 * srcImageStrideInBytes;
		const vx_uint8* srow2 = pSrcImage - srcImageStrideInBytes;
		const vx_uint8* srow3 = pSrcImage;
		const vx_uint8* srow4 = pSrcImage + srcImageStrideInBytes;
		const vx_uint8* srow5 = pSrcImage + 2 * srcImageStrideInBytes;
		const vx_uint8* srow6 = pSrcImage + 3 * srcImageStrideInBytes;

		vx_uint16* drow = (vx_uint16*)pDstImage;

		for (x = 0; x < prefixWidth; x++)
		{
			vx_int16 Gx = (vx_int16)srow0[x + 3] + (4 * (vx_int16)srow0[x + 2]) + (5 * (vx_int16)srow0[x + 1]) - (5 * (vx_int16)srow0[x - 1]) - (4 * (vx_int16)srow0[x - 2]) - (vx_int16)srow0[x - 3]
				+ 6 * ((vx_int16)srow1[x + 3] + (4 * (vx_int16)srow1[x + 2]) + (5 * (vx_int16)srow1[x + 1]) - (5 * (vx_int16)srow1[x - 1]) - (4 * (vx_int16)srow1[x - 2]) - (vx_int16)srow1[x - 3])
				+ 15 * ((vx_int16)srow2[x + 3] + (4 * (vx_int16)srow2[x + 2]) + (5 * (vx_int16)srow2[x + 1]) - (5 * (vx_int16)srow2[x - 1]) - (4 * (vx_int16)srow2[x - 2]) - (vx_int16)srow2[x - 3])
				+ 20 * ((vx_int16)srow3[x + 3] + (4 * (vx_int16)srow3[x + 2]) + (5 * (vx_int16)srow3[x + 1]) - (5 * (vx_int16)srow3[x - 1]) - (4 * (vx_int16)srow3[x - 2]) - (vx_int16)srow3[x - 3])
				+ 15 * ((vx_int16)srow4[x + 3] + (4 * (vx_int16)srow4[x + 2]) + (5 * (vx_int16)srow4[x + 1]) - (5 * (vx_int16)srow4[x - 1]) - (4 * (vx_int16)srow4[x - 2]) - (vx_int16)srow4[x - 3])
				+ 6 * ((vx_int16)srow5[x + 3] + (4 * (vx_int16)srow5[x + 2]) + (5 * (vx_int16)srow5[x + 1]) - (5 * (vx_int16)srow5[x - 1]) - (4 * (vx_int16)srow5[x - 2]) - (vx_int16)srow5[x - 3])
				+ (vx_int16)srow6[x + 3] + (4 * (vx_int16)srow6[x + 2]) + (5 * (vx_int16)srow6[x + 1]) - (5 * (vx_int16)srow6[x - 1]) - (4 * (vx_int16)srow6[x - 2]) - (vx_int16)srow6[x - 3];
			vx_int16 Gy = (vx_int16)srow6[x - 3] + (vx_int16)srow6[x + 3] + (6 * ((vx_int16)srow6[x - 2] + (vx_int16)srow6[x + 2])) + (15 * ((vx_int16)srow6[x - 1] + (vx_int16)srow6[x + 1])) + (20 * (vx_int16)srow6[x])
				+ 4 * ((vx_int16)srow5[x - 3] + (vx_int16)srow5[x + 3] + (6 * ((vx_int16)srow5[x - 2] + (vx_int16)srow5[x + 2])) + (15 * ((vx_int16)srow5[x - 1] + (vx_int16)srow5[x + 1])) + (20 * (vx_int16)srow5[x]))
				+ 5 * ((vx_int16)srow4[x - 3] + (vx_int16)srow4[x + 3] + (6 * ((vx_int16)srow4[x - 2] + (vx_int16)srow4[x + 2])) + (15 * ((vx_int16)srow4[x - 1] + (vx_int16)srow4[x + 1])) + (20 * (vx_int16)srow4[x]))
				- 5 * ((vx_int16)srow2[x - 3] + (vx_int16)srow2[x + 3] + (6 * ((vx_int16)srow2[x - 2] + (vx_int16)srow2[x + 2])) + (15 * ((vx_int16)srow2[x - 1] + (vx_int16)srow2[x + 1])) + (20 * (vx_int16)srow2[x]))
				- 4 * ((vx_int16)srow1[x - 3] + (vx_int16)srow1[x + 3] + (6 * ((vx_int16)srow1[x - 2] + (vx_int16)srow1[x + 2])) + (15 * ((vx_int16)srow1[x - 1] + (vx_int16)srow1[x + 1])) + (20 * (vx_int16)srow1[x]))
				- ((vx_int16)srow0[x - 3] + (vx_int16)srow0[x + 3] + (6 * ((vx_int16)srow0[x - 2] + (vx_int16)srow0[x + 2])) + (15 * ((vx_int16)srow0[x - 1] + (vx_int16)srow0[x + 1])) + (20 * (vx_int16)srow0[x]));
			vx_int16 tmp = (vx_int16)sqrt((Gx*Gx) + (Gy*Gy));
			tmp <<= 2;
			tmp |= (HafCpu_FastAtan2_Canny(Gx, Gy) & 3);
			drow[x] = tmp;
		}

		// do vertical convolution
		for (x = prefixWidth; x <= alignedWidth - 8; x += 8)
		{
			__m128i s0 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow0 + x)), z);
			__m128i s1 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow1 + x)), z);
			__m128i s2 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow2 + x)), z);
			__m128i s3 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow3 + x)), z);
			__m128i s4 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow4 + x)), z);
			__m128i s5 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow5 + x)), z);
			__m128i s6 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow6 + x)), z);

			__m128i t0 = _mm_add_epi16(_mm_mullo_epi16(_mm_add_epi16(s1, s5), c6), _mm_mullo_epi16(s3, c20));
			__m128i t2 = _mm_mullo_epi16(_mm_add_epi16(s2, s4), c15);
			t0 = _mm_add_epi16(t0, _mm_add_epi16(s0, s6));
			__m128i t1 = _mm_slli_epi16(_mm_sub_epi16(s5, s1), 2);
			t0 = _mm_add_epi16(t0, t2);

			t2 = _mm_mullo_epi16(_mm_sub_epi16(s4, s2), c5);
			t1 = _mm_add_epi16(t1, _mm_sub_epi16(s6, s0));
			t0 = _mm_srai_epi16(t0, 2);
			t1 = _mm_add_epi16(t1, t2);
			t1 = _mm_srai_epi16(t1, 2);

			_mm_store_si128((__m128i*)(r0 + x), t0);
			_mm_store_si128((__m128i*)(r1 + x), t1);
		}

		// do horizontal convolution, interleave the results and store them to dst
		x = prefixWidth;
		for (; x <= alignedWidth - 8; x += 8)
		{
			__m128i s0 = _mm_loadu_si128((const __m128i*)(r0 + x - 3));
			__m128i s1 = _mm_loadu_si128((const __m128i*)(r0 + x - 2));
			__m128i s2 = _mm_loadu_si128((const __m128i*)(r0 + x - 1));
			__m128i s3 = _mm_loadu_si128((const __m128i*)(r0 + x + 1));
			__m128i s4 = _mm_loadu_si128((const __m128i*)(r0 + x + 2));
			__m128i s5 = _mm_loadu_si128((const __m128i*)(r0 + x + 3));

			__m128i t0 = _mm_slli_epi16(_mm_subs_epi16(s4, s1), 2);
			__m128i t1 = _mm_mullo_epi16(_mm_subs_epi16(s3, s2), c5);
			t0 = _mm_adds_epi16(t0, _mm_subs_epi16(s5, s0));
			t0 = _mm_adds_epi16(t0, t1);

			s0 = _mm_loadu_si128((const __m128i*)(r1 + x - 3));
			s1 = _mm_loadu_si128((const __m128i*)(r1 + x - 2));
			s2 = _mm_loadu_si128((const __m128i*)(r1 + x - 1));
			s3 = _mm_loadu_si128((const __m128i*)(r1 + x));
			s4 = _mm_loadu_si128((const __m128i*)(r1 + x + 1));
			s5 = _mm_loadu_si128((const __m128i*)(r1 + x + 2));
			__m128i s6 = _mm_loadu_si128((const __m128i*)(r1 + x + 3));

			t1 = _mm_adds_epi16(_mm_mullo_epi16(_mm_add_epi16(s1, s5), c6), _mm_mullo_epi16(s3, c20));
			__m128i t2 = _mm_mullo_epi16(_mm_add_epi16(s2, s4), c15);
			t1 = _mm_adds_epi16(t1, _mm_adds_epi16(s0, s6));
			t1 = _mm_adds_epi16(t1, t2);
			t1 = _mm_subs_epi16(z, t1);
			// unpack for multiplication
			s0 = _mm_unpacklo_epi16(t0, t1);
			s2 = _mm_unpackhi_epi16(t0, t1);
			s0 = _mm_madd_epi16(s0, s0);
			s2 = _mm_madd_epi16(s2, s2);

			__m128 f0 = _mm_cvtepi32_ps(s0);
			__m128 f1 = _mm_cvtepi32_ps(s2);
			f0 = _mm_sqrt_ps(f0);
			f1 = _mm_sqrt_ps(f1);
			for (int i = 0; i < 8; i++){
				M128I(s1).m128i_i16[i] = HafCpu_FastAtan2_Canny(M128I(t0).m128i_i16[i], M128I(t1).m128i_i16[i]);
			}
			t0 = _mm_cvtps_epi32(f0);
			t1 = _mm_cvtps_epi32(f1);
			// pack with signed saturation
			t0 = _mm_packus_epi32(t0, t1);
			t0 = _mm_or_si128(_mm_slli_epi16(t0, 2), s1);
			// store magnitude and angle to destination
			_mm_store_si128((__m128i*)(drow + x), t0);
		}

		for (x = alignedWidth + prefixWidth - 1; x < (int)dstWidth; x++)
		{
			vx_int16 Gx = (vx_int16)srow0[x + 3] + (4 * (vx_int16)srow0[x + 2]) + (5 * (vx_int16)srow0[x + 1]) - (5 * (vx_int16)srow0[x - 1]) - (4 * (vx_int16)srow0[x - 2]) - (vx_int16)srow0[x - 3]
				+ 6 * ((vx_int16)srow1[x + 3] + (4 * (vx_int16)srow1[x + 2]) + (5 * (vx_int16)srow1[x + 1]) - (5 * (vx_int16)srow1[x - 1]) - (4 * (vx_int16)srow1[x - 2]) - (vx_int16)srow1[x - 3])
				+ 15 * ((vx_int16)srow2[x + 3] + (4 * (vx_int16)srow2[x + 2]) + (5 * (vx_int16)srow2[x + 1]) - (5 * (vx_int16)srow2[x - 1]) - (4 * (vx_int16)srow2[x - 2]) - (vx_int16)srow2[x - 3])
				+ 20 * ((vx_int16)srow3[x + 3] + (4 * (vx_int16)srow3[x + 2]) + (5 * (vx_int16)srow3[x + 1]) - (5 * (vx_int16)srow3[x - 1]) - (4 * (vx_int16)srow3[x - 2]) - (vx_int16)srow3[x - 3])
				+ 15 * ((vx_int16)srow4[x + 3] + (4 * (vx_int16)srow4[x + 2]) + (5 * (vx_int16)srow4[x + 1]) - (5 * (vx_int16)srow4[x - 1]) - (4 * (vx_int16)srow4[x - 2]) - (vx_int16)srow4[x - 3])
				+ 6 * ((vx_int16)srow5[x + 3] + (4 * (vx_int16)srow5[x + 2]) + (5 * (vx_int16)srow5[x + 1]) - (5 * (vx_int16)srow5[x - 1]) - (4 * (vx_int16)srow5[x - 2]) - (vx_int16)srow5[x - 3])
				+ (vx_int16)srow6[x + 3] + (4 * (vx_int16)srow6[x + 2]) + (5 * (vx_int16)srow6[x + 1]) - (5 * (vx_int16)srow6[x - 1]) - (4 * (vx_int16)srow6[x - 2]) - (vx_int16)srow6[x - 3];
			vx_int16 Gy = (vx_int16)srow6[x - 3] + (vx_int16)srow6[x + 3] + (6 * ((vx_int16)srow6[x - 2] + (vx_int16)srow6[x + 2])) + (15 * ((vx_int16)srow6[x - 1] + (vx_int16)srow6[x + 1])) + (20 * (vx_int16)srow6[x])
				+ 4 * ((vx_int16)srow5[x - 3] + (vx_int16)srow5[x + 3] + (6 * ((vx_int16)srow5[x - 2] + (vx_int16)srow5[x + 2])) + (15 * ((vx_int16)srow5[x - 1] + (vx_int16)srow5[x + 1])) + (20 * (vx_int16)srow5[x]))
				+ 5 * ((vx_int16)srow4[x - 3] + (vx_int16)srow4[x + 3] + (6 * ((vx_int16)srow4[x - 2] + (vx_int16)srow4[x + 2])) + (15 * ((vx_int16)srow4[x - 1] + (vx_int16)srow4[x + 1])) + (20 * (vx_int16)srow4[x]))
				- 5 * ((vx_int16)srow2[x - 3] + (vx_int16)srow2[x + 3] + (6 * ((vx_int16)srow2[x - 2] + (vx_int16)srow2[x + 2])) + (15 * ((vx_int16)srow2[x - 1] + (vx_int16)srow2[x + 1])) + (20 * (vx_int16)srow2[x]))
				- 4 * ((vx_int16)srow1[x - 3] + (vx_int16)srow1[x + 3] + (6 * ((vx_int16)srow1[x - 2] + (vx_int16)srow1[x + 2])) + (15 * ((vx_int16)srow1[x - 1] + (vx_int16)srow1[x + 1])) + (20 * (vx_int16)srow1[x]))
				- ((vx_int16)srow0[x - 3] + (vx_int16)srow0[x + 3] + (6 * ((vx_int16)srow0[x - 2] + (vx_int16)srow0[x + 2])) + (15 * ((vx_int16)srow0[x - 1] + (vx_int16)srow0[x + 1])) + (20 * (vx_int16)srow0[x]));
			vx_int16 tmp = (vx_int16)sqrt((Gx*Gx) + (Gy*Gy));
			tmp <<= 2;
			tmp |= (HafCpu_FastAtan2_Canny(Gx, Gy) & 3);
			drow[x] = tmp;
		}
		pSrcImage += srcImageStrideInBytes;
		pDstImage += dstride;
	}
	return AGO_SUCCESS;
}


switch (m_joyButton->getChangeSetCondition())
                {
                case JoyButton::SetChangeOneWay:
                    temptext = "one-way";
                    break;

                case JoyButton::SetChangeTwoWay:
                    temptext = "two-way";
                    break;

                case JoyButton::SetChangeWhileHeld:
                    temptext = "while-held";
                    break;

                default:
                    break;
                }

/* antimicrox Gamepad to KB+M event mapper
 *
 */

#include "joybuttonxml.h"
#include "joybutton.h"
#include "joybuttonslotxml.h"

#include <QDebug>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>

JoyButtonXml::JoyButtonXml(JoyButton *joyButton, QObject *parent)
    : QObject(parent)
{
    m_joyButton = joyButton;
}

bool JoyButtonXml::readButtonConfig(QXmlStreamReader *xml)
{
    bool found = false;

    if ((xml->name() == "toggle") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();

        if (temptext == "true")
            m_joyButton->setToggle(true);
    } else if ((xml->name() == "turbointerval") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        int tempchoice = temptext.toInt();
        m_joyButton->setTurboInterval(tempchoice);
    } else if ((xml->name() == "turbomode") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();

        if (temptext == "normal")
            m_joyButton->setTurboMode(JoyButton::NormalTurbo);
        else if (temptext == "gradient")
            m_joyButton->setTurboMode(JoyButton::GradientTurbo);
        else if (temptext == "pulse")
            m_joyButton->setTurboMode(JoyButton::PulseTurbo);
    } else if ((xml->name() == "useturbo") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();

        if (temptext == "true")
            m_joyButton->setUseTurbo(true);
    } else if ((xml->name() == "mousespeedx") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        int tempchoice = temptext.toInt();
        m_joyButton->setMouseSpeedX(tempchoice);
    } else if ((xml->name() == "mousespeedy") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        int tempchoice = temptext.toInt();
        m_joyButton->setMouseSpeedY(tempchoice);
    } else if ((xml->name() == "cycleresetactive") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();

        if (temptext == "true")
            m_joyButton->setCycleResetStatus(true);
    } else if ((xml->name() == "cycleresetinterval") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        int tempchoice = temptext.toInt();

        if (tempchoice >= GlobalVariables::JoyButton::MINCYCLERESETTIME)
            m_joyButton->setCycleResetTime(tempchoice);
    } else if ((xml->name() == "slots") && xml->isStartElement())
    {
        found = true;
        xml->readNextStartElement();

        while (!xml->atEnd() && (!xml->isEndElement() && (xml->name() != "slots")))
        {
            if ((xml->name() == "slot") && xml->isStartElement())
            {
                JoyButtonSlot *buttonslot = new JoyButtonSlot(m_joyButton);
                JoyButtonSlotXml *buttonSlotXml = new JoyButtonSlotXml(buttonslot);
                buttonSlotXml->readConfig(xml);
                delete buttonSlotXml;
                buttonSlotXml = nullptr;

                if (buttonslot->isValidSlot())
                {
                    bool inserted = m_joyButton->insertAssignedSlot(buttonslot, false);

                    if (!inserted)
                    {
                        if (buttonslot->getSlotMode() == JoyButtonSlot::JoyMix && buttonslot->getMixSlots() != nullptr)
                        {
                            qDeleteAll(*buttonslot->getMixSlots());
                            buttonslot->getMixSlots()->clear();
                            delete buttonslot->getMixSlots();
                            buttonslot->assignMixSlotsToNull();
                        }

                        buttonslot->deleteLater();
                        buttonslot = nullptr;
                    }
                } else
                {
                    if (buttonslot->getSlotMode() == JoyButtonSlot::JoyMix && buttonslot->getMixSlots() != nullptr)
                    {
                        qDeleteAll(*buttonslot->getMixSlots());
                        buttonslot->getMixSlots()->clear();
                        delete buttonslot->getMixSlots();
                        buttonslot->assignMixSlotsToNull();
                    }

                    buttonslot->deleteLater();
                    buttonslot = nullptr;
                }
            } else
            {
                xml->skipCurrentElement();
            }

            xml->readNextStartElement();
        }
    } else if ((xml->name() == "setselect") && xml->isStartElement())
    {
        if (!m_joyButton->isModifierButton())
        {
            found = true;
            QString temptext = xml->readElementText();
            int tempchoice = temptext.toInt();

            if ((tempchoice >= 0) && (tempchoice <= GlobalVariables::InputDevice::NUMBER_JOYSETS))
                m_joyButton->setChangeSetSelection(tempchoice - 1, false);
        }
    } else if ((xml->name() == "setselectcondition") && xml->isStartElement())
    {
        if (!m_joyButton->isModifierButton())
        {
            found = true;
            QString temptext = xml->readElementText();
            JoyButton::SetChangeCondition tempcondition = JoyButton::SetChangeDisabled;

            if (temptext == "one-way")
                tempcondition = JoyButton::SetChangeOneWay;
            else if (temptext == "two-way")
                tempcondition = JoyButton::SetChangeTwoWay;
            else if (temptext == "while-held")
                tempcondition = JoyButton::SetChangeWhileHeld;

            if (tempcondition != JoyButton::SetChangeDisabled)
                m_joyButton->setChangeSetCondition(tempcondition, false, false);
        }
    } else if ((xml->name() == "mousemode") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();

        if (temptext == "cursor")
            m_joyButton->setMouseMode(JoyButton::MouseCursor);
        else if (temptext == "spring")
            m_joyButton->setMouseMode(JoyButton::MouseSpring);
    } else if ((xml->name() == "mouseacceleration") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();

        if (temptext == "linear")
            m_joyButton->setMouseCurve(JoyButton::LinearCurve);
        else if (temptext == "quadratic")
            m_joyButton->setMouseCurve(JoyButton::QuadraticCurve);
        else if (temptext == "cubic")
            m_joyButton->setMouseCurve(JoyButton::CubicCurve);
        else if (temptext == "quadratic-extreme")
            m_joyButton->setMouseCurve(JoyButton::QuadraticExtremeCurve);
        else if (temptext == "power")
            m_joyButton->setMouseCurve(JoyButton::PowerCurve);
        else if (temptext == "precision")
            m_joyButton->setMouseCurve(JoyButton::EnhancedPrecisionCurve);
        else if (temptext == "easing-quadratic")
            m_joyButton->setMouseCurve(JoyButton::EasingQuadraticCurve);
        else if (temptext == "easing-cubic")
            m_joyButton->setMouseCurve(JoyButton::EasingCubicCurve);
    } else if ((xml->name() == "mousespringwidth") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        int tempchoice = temptext.toInt();
        m_joyButton->setSpringWidth(tempchoice);
    } else if ((xml->name() == "mousespringheight") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        int tempchoice = temptext.toInt();
        m_joyButton->setSpringHeight(tempchoice);
    } else if ((xml->name() == "mousesensitivity") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        double tempchoice = temptext.toDouble();
        m_joyButton->setSensitivity(tempchoice);
    } else if ((xml->name() == "actionname") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();

        if (!temptext.isEmpty())
            m_joyButton->setActionName(temptext);
    } else if ((xml->name() == "wheelspeedx") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        int tempchoice = temptext.toInt();
        m_joyButton->setWheelSpeed(tempchoice, 'X');
    } else if ((xml->name() == "wheelspeedy") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        int tempchoice = temptext.toInt();
        m_joyButton->setWheelSpeed(tempchoice, 'Y');
    } else if ((xml->name() == "relativespring") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();

        if (temptext == "true")
            m_joyButton->setSpringRelativeStatus(true);
    } else if ((xml->name() == "easingduration") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        double tempchoice = temptext.toDouble();
        m_joyButton->setEasingDuration(tempchoice);
    } else if ((xml->name() == "extraacceleration") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();

        if (temptext == "true")
            m_joyButton->setExtraAccelerationStatus(true);
    } else if ((xml->name() == "accelerationmultiplier") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        double tempchoice = temptext.toDouble();
        m_joyButton->setExtraAccelerationMultiplier(tempchoice);
    } else if ((xml->name() == "startaccelmultiplier") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        double tempchoice = temptext.toDouble();
        m_joyButton->setStartAccelMultiplier(tempchoice);
    } else if ((xml->name() == "minaccelthreshold") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        double tempchoice = temptext.toDouble();
        m_joyButton->setMinAccelThreshold(tempchoice);
    } else if ((xml->name() == "maxaccelthreshold") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        double tempchoice = temptext.toDouble();
        m_joyButton->setMaxAccelThreshold(tempchoice);
    } else if ((xml->name() == "accelextraduration") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        double tempchoice = temptext.toDouble();
        m_joyButton->setAccelExtraDuration(tempchoice);
    } else if ((xml->name() == "extraaccelerationcurve") && xml->isStartElement())
    {
        found = true;

        QString temptext = xml->readElementText();
        JoyButton::JoyExtraAccelerationCurve tempcurve = JoyButton::DEFAULTEXTRAACCELCURVE;

        if (temptext == "linear")
            tempcurve = JoyButton::LinearAccelCurve;
        else if (temptext == "easeoutsine")
            tempcurve = JoyButton::EaseOutSineCurve;
        else if (temptext == "easeoutquad")
            tempcurve = JoyButton::EaseOutQuadAccelCurve;
        else if (temptext == "easeoutcubic")
            tempcurve = JoyButton::EaseOutCubicAccelCurve;

        m_joyButton->setExtraAccelerationCurve(tempcurve);
    } else if ((xml->name() == "springreleaseradius") && xml->isStartElement())
    {
        found = true;
        QString temptext = xml->readElementText();
        int tempchoice = temptext.toInt();

        if (!m_joyButton->isRelativeSpring())
            m_joyButton->setSpringDeadCircleMultiplier(tempchoice);
    }

    return found;
}

void JoyButtonXml::readConfig(QXmlStreamReader *xml)
{
    if (xml->isStartElement() && (xml->name() == m_joyButton->getXmlName()))
    {
        xml->readNextStartElement();

        while (!xml->atEnd() && (!xml->isEndElement() && (xml->name() != m_joyButton->getXmlName())))
        {
            bool found = readButtonConfig(xml);

            if (!found)
                xml->skipCurrentElement();
            else
                m_joyButton->buildActiveZoneSummaryString();

            xml->readNextStartElement();
        }
    }
}

void JoyButtonXml::writeConfig(QXmlStreamWriter *xml)
{
    if (!m_joyButton->isDefault())
    {
        xml->writeStartElement(m_joyButton->getXmlName());
        xml->writeAttribute("index", QString::number(m_joyButton->getRealJoyNumber()));

        if (m_joyButton->getToggleState() != GlobalVariables::JoyButton::DEFAULTTOGGLE)
            xml->writeTextElement("toggle", m_joyButton->getToggleState() ? "true" : "false");

        if (m_joyButton->getTurboInterval() != GlobalVariables::JoyButton::DEFAULTTURBOINTERVAL)
            xml->writeTextElement("turbointerval", QString::number(m_joyButton->getTurboInterval()));

        if ((m_joyButton->getTurboMode() != JoyButton::DEFAULTTURBOMODE) &&
            (m_joyButton->getTurboMode() == JoyButton::GradientTurbo))
            xml->writeTextElement("turbomode", "gradient");
        else if ((m_joyButton->getTurboMode() != JoyButton::DEFAULTTURBOMODE) &&
                 (m_joyButton->getTurboMode() == JoyButton::PulseTurbo))
            xml->writeTextElement("turbomode", "pulse");

        if (m_joyButton->isUsingTurbo() != GlobalVariables::JoyButton::DEFAULTUSETURBO)
            xml->writeTextElement("useturbo", m_joyButton->isUsingTurbo() ? "true" : "false");

        if (m_joyButton->getMouseSpeedX() != GlobalVariables::JoyButton::DEFAULTMOUSESPEEDX)
            xml->writeTextElement("mousespeedx", QString::number(m_joyButton->getMouseSpeedX()));

        if (m_joyButton->getMouseSpeedY() != GlobalVariables::JoyButton::DEFAULTMOUSESPEEDY)
            xml->writeTextElement("mousespeedy", QString::number(m_joyButton->getMouseSpeedY()));

        if ((m_joyButton->getMouseMode() != JoyButton::DEFAULTMOUSEMODE) &&
            (m_joyButton->getMouseMode() == JoyButton::MouseCursor))
        {
            xml->writeTextElement("mousemode", "cursor");
        } else if ((m_joyButton->getMouseMode() != JoyButton::DEFAULTMOUSEMODE) &&
                   (m_joyButton->getMouseMode() == JoyButton::MouseSpring))
        {
            xml->writeTextElement("mousemode", "spring");
            xml->writeTextElement("mousespringwidth", QString::number(m_joyButton->getSpringWidth()));
            xml->writeTextElement("mousespringheight", QString::number(m_joyButton->getSpringHeight()));
        }

        if (m_joyButton->getMouseCurve() != JoyButton::DEFAULTMOUSECURVE)
        {
            switch (m_joyButton->getMouseCurve())
            {
            case JoyButton::LinearCurve:
                xml->writeTextElement("mouseacceleration", "linear");
                break;

            case JoyButton::QuadraticCurve:
                xml->writeTextElement("mouseacceleration", "quadratic");
                break;

            case JoyButton::CubicCurve:
                xml->writeTextElement("mouseacceleration", "cubic");
                break;

            case JoyButton::QuadraticExtremeCurve:
                xml->writeTextElement("mouseacceleration", "quadratic-extreme");
                break;

            case JoyButton::PowerCurve:
                xml->writeTextElement("mouseacceleration", "power");
                xml->writeTextElement("mousesensitivity", QString::number(m_joyButton->getSensitivity()));
                break;

            case JoyButton::EnhancedPrecisionCurve:
                xml->writeTextElement("mouseacceleration", "precision");
                break;

            case JoyButton::EasingQuadraticCurve:
                xml->writeTextElement("mouseacceleration", "easing-quadratic");
                break;

            case JoyButton::EasingCubicCurve:
                xml->writeTextElement("mouseacceleration", "easing-cubic");
                break;
            }
        }

        if (m_joyButton->getWheelSpeedX() != GlobalVariables::JoyButton::DEFAULTWHEELX)
            xml->writeTextElement("wheelspeedx", QString::number(m_joyButton->getWheelSpeedX()));

        if (m_joyButton->getWheelSpeedY() != GlobalVariables::JoyButton::DEFAULTWHEELY)
            xml->writeTextElement("wheelspeedy", QString::number(m_joyButton->getWheelSpeedY()));

        if (!m_joyButton->isModifierButton())
        {
            if (m_joyButton->getChangeSetCondition() != JoyButton::SetChangeDisabled)
            {
                xml->writeTextElement("setselect", QString::number(m_joyButton->getSetSelection() + 1));
                QString temptext = QString();

                xml->writeTextElement("setselectcondition", temptext);
            }
        }

        if (!m_joyButton->getActionName().isEmpty())
            xml->writeTextElement("actionname", m_joyButton->getActionName());

        if (m_joyButton->isCycleResetActive())
            xml->writeTextElement("cycleresetactive", "true");

        if (m_joyButton->getCycleResetTime() >= GlobalVariables::JoyButton::MINCYCLERESETTIME)
            xml->writeTextElement("cycleresetinterval", QString::number(m_joyButton->getCycleResetTime()));

        if (m_joyButton->isRelativeSpring())
            xml->writeTextElement("relativespring", "true");

        if (!qFuzzyCompare(m_joyButton->getEasingDuration(), GlobalVariables::JoyButton::DEFAULTEASINGDURATION))
            xml->writeTextElement("easingduration", QString::number(m_joyButton->getEasingDuration()));

        if (m_joyButton->isExtraAccelerationEnabled())
            xml->writeTextElement("extraacceleration", "true");

        if (!qFuzzyCompare(m_joyButton->getExtraAccelerationMultiplier(), GlobalVariables::JoyButton::DEFAULTEXTRACCELVALUE))
            xml->writeTextElement("accelerationmultiplier", QString::number(m_joyButton->getExtraAccelerationMultiplier()));

        if (!qFuzzyCompare(m_joyButton->getStartAccelMultiplier(), GlobalVariables::JoyButton::DEFAULTSTARTACCELMULTIPLIER))
            xml->writeTextElement("startaccelmultiplier", QString::number(m_joyButton->getStartAccelMultiplier()));

        if (!qFuzzyCompare(m_joyButton->getMinAccelThreshold(), GlobalVariables::JoyButton::DEFAULTMINACCELTHRESHOLD))
            xml->writeTextElement("minaccelthreshold", QString::number(m_joyButton->getMinAccelThreshold()));

        if (!qFuzzyCompare(m_joyButton->getMaxAccelThreshold(), GlobalVariables::JoyButton::DEFAULTMAXACCELTHRESHOLD))
            xml->writeTextElement("maxaccelthreshold", QString::number(m_joyButton->getMaxAccelThreshold()));

        if (!qFuzzyCompare(m_joyButton->getAccelExtraDuration(), GlobalVariables::JoyButton::DEFAULTACCELEASINGDURATION))
            xml->writeTextElement("accelextraduration", QString::number(m_joyButton->getAccelExtraDuration()));

        if (m_joyButton->getSpringDeadCircleMultiplier() != GlobalVariables::JoyButton::DEFAULTSPRINGRELEASERADIUS)
            xml->writeTextElement("springreleaseradius", QString::number(m_joyButton->getSpringDeadCircleMultiplier()));

        if (m_joyButton->getExtraAccelerationCurve() != JoyButton::DEFAULTEXTRAACCELCURVE)
        {
            QString temp = QString();

            switch (m_joyButton->getExtraAccelerationCurve())
            {
            case JoyButton::LinearAccelCurve:
                temp = "linear";
                break;

            case JoyButton::EaseOutSineCurve:
                temp = "easeoutsine";
                break;

            case JoyButton::EaseOutQuadAccelCurve:
                temp = "easeoutquad";
                break;

            case JoyButton::EaseOutCubicAccelCurve:
                temp = "easeoutcubic";
                break;
            }

            if (!temp.isEmpty())
                xml->writeTextElement("extraaccelerationcurve", temp);
        }

        // Write information about assigned slots.
        if (!m_joyButton->getAssignedSlots()->isEmpty())
        {
            xml->writeStartElement("slots");
            QListIterator<JoyButtonSlot *> iter(*m_joyButton->getAssignedSlots());

            while (iter.hasNext())
            {
                JoyButtonSlot *buttonslot = iter.next();
                JoyButtonSlotXml *buttonslotxml = new JoyButtonSlotXml(buttonslot);
                buttonslotxml->writeConfig(xml);
                delete buttonslotxml;
                buttonslotxml = nullptr;
            }

            xml->writeEndElement();
        }

        xml->writeEndElement();
    }
}