/***********************************************************************
THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY
DEVELOPER: Zihan Chen(vczh)
***********************************************************************/
#include "GacUI.Windows.h"

/***********************************************************************
.\WINNATIVEDPIAWARENESS.CPP
***********************************************************************/
#define _WINSOCKAPI_
#include <ShellScalingApi.h>

#pragma comment(lib, "Shcore.lib")

namespace vl
{
	namespace presentation
	{
		namespace windows
		{
#define USE_API(NAME, API) static auto proc_##API = (decltype(&API))(GetProcAddress(GetModuleHandle(L#NAME), #API))

			void InitDpiAwareness(bool dpiAware)
			{
				USE_API(user32, SetProcessDpiAwarenessContext);
				USE_API(shcore, SetProcessDpiAwareness);

				if(proc_SetProcessDpiAwarenessContext)
				{
					proc_SetProcessDpiAwarenessContext(dpiAware ? DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2:  DPI_AWARENESS_CONTEXT_UNAWARE);
					return;
				}

				if(proc_SetProcessDpiAwareness)
				{
					proc_SetProcessDpiAwareness(dpiAware ? PROCESS_PER_MONITOR_DPI_AWARE : PROCESS_DPI_UNAWARE);
					return;
				}
			}

			void DpiAwared_GetDpiForMonitor(HMONITOR monitor, UINT* x, UINT* y)
			{
				USE_API(shcore, GetDpiForMonitor);

				if (proc_GetDpiForMonitor)
				{
					if (proc_GetDpiForMonitor(monitor, MDT_DEFAULT, x, y) == S_OK)
					{
						return;
					}
				}
				*x = 96;
				*y = 96;
			}

			void DpiAwared_GetDpiForWindow(HWND handle, UINT* x, UINT* y)
			{
				USE_API(user32, GetDpiForWindow);

				if (proc_GetDpiForWindow)
				{
					*x = *y = proc_GetDpiForWindow(handle);
				}
				else
				{
					HMONITOR monitor = MonitorFromWindow(handle, MONITOR_DEFAULTTONULL);
					if (monitor == NULL)
					{
						*x = *y = 96;
					}
					else
					{
						DpiAwared_GetDpiForMonitor(monitor, x, y);
					}
				}
			}

			void DpiAwared_AdjustWindowRect(LPRECT rect, HWND handle, UINT dpi)
			{
				USE_API(user32, AdjustWindowRectExForDpi);

				if (proc_AdjustWindowRectExForDpi)
				{
					proc_AdjustWindowRectExForDpi(rect, (DWORD)GetWindowLongPtr(handle, GWL_STYLE), FALSE, (DWORD)GetWindowLongPtr(handle, GWL_EXSTYLE), dpi);
				}
				else
				{
					AdjustWindowRect(rect, (DWORD)GetWindowLongPtr(handle, GWL_STYLE), FALSE);
				}
			}

			int DpiAwared_GetSystemMetrics(int index, UINT dpi)
			{
				USE_API(user32, GetSystemMetricsForDpi);

				if (proc_GetSystemMetricsForDpi)
				{
					return proc_GetSystemMetricsForDpi(index, dpi);
				}
				else
				{
					return GetSystemMetrics(index);
				}
			}

#undef USE_API
		}
	}
}

/***********************************************************************
.\WINNATIVEWINDOW.CPP
***********************************************************************/
#define _WINSOCKAPI_
#include <CommCtrl.h>

#pragma comment(lib, "Imm32.lib")
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "Comctl32.lib")

namespace vl
{
	namespace presentation
	{
		namespace windows
		{
			using namespace collections;

			LPCWSTR defaultIconResourceName = nullptr;

			HICON CreateWindowDefaultIcon(vint size = 0)
			{
				if (!defaultIconResourceName) return NULL;
				return (HICON)LoadImage(GetModuleHandle(NULL), defaultIconResourceName, IMAGE_ICON, (int)size, (int)size, (size ? 0 : LR_DEFAULTSIZE) | LR_SHARED);
			}

			void SetWindowDefaultIcon(UINT resourceId)
			{
				CHECK_ERROR(defaultIconResourceName == nullptr, L"vl::presentation::windows::SetWindowDefaultIcon(UINT)#This function can only be called once.");
				defaultIconResourceName = MAKEINTRESOURCE(resourceId);
			}

			HWND GetHWNDFromNativeWindowHandle(INativeWindow* window)
			{
				if(!window) return NULL;
				IWindowsForm* form=GetWindowsForm(window);
				if(!form) return NULL;
				return form->GetWindowHandle();
			}

/***********************************************************************
WindowsClass
***********************************************************************/

			class WinClass : public Object
			{
			protected:
				WString									name;
				WNDCLASSEX								windowClass;
				ATOM									windowAtom;

			public:
				WinClass(WString _name, bool shadow, bool ownDC, WNDPROC procedure, HINSTANCE hInstance)
				{
					name=_name;
					ZeroMemory(&windowClass, sizeof(windowClass));
					windowClass.cbSize=sizeof(windowClass);
					windowClass.style=CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | (shadow?CS_DROPSHADOW:0) | (ownDC?CS_OWNDC:0);
					windowClass.lpfnWndProc=procedure;
					windowClass.cbClsExtra=0;
					windowClass.cbWndExtra=0;
					windowClass.hInstance=hInstance;
					if (defaultIconResourceName)
					{
						windowClass.hIcon = CreateWindowDefaultIcon();
					}
					windowClass.hCursor=NULL;//LoadCursor(NULL,IDC_ARROW);
					windowClass.hbrBackground=GetSysColorBrush(COLOR_BTNFACE);
					windowClass.lpszMenuName=NULL;
					windowClass.lpszClassName=name.Buffer();
					windowClass.hIconSm=NULL;
					windowAtom=RegisterClassEx(&windowClass);
				}

				bool IsAvailable()
				{
					return windowAtom!=0;
				}

				WString GetName()
				{
					return name;
				}

				ATOM GetClassAtom()
				{
					return windowAtom;
				}
			};

/***********************************************************************
WindowsForm
***********************************************************************/

			class WindowsForm : public Object, public INativeWindow, public IWindowsForm
			{
				template<typename TBase>
				friend class WindowsAutomationServiceBase;
			protected:
				
				LONG_PTR InternalGetExStyle()
				{
					return GetWindowLongPtr(handle, GWL_EXSTYLE);
				}

				void InternalSetExStyle(LONG_PTR exStyle)
				{
					LONG_PTR result = SetWindowLongPtr(handle, GWL_EXSTYLE, exStyle);
					SetWindowPos(handle, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
				}

				LONG_PTR InternalGetStyle()
				{
					return GetWindowLongPtr(handle, GWL_STYLE);
				}

				void InternalSetStyle(LONG_PTR style)
				{
					SetWindowLongPtr(handle, GWL_STYLE, style);
					SetWindowPos(handle, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
				}

				LONG_PTR TurnOnStyle(LONG_PTR combination, LONG_PTR style)
				{
					return combination | style;
				}

				LONG_PTR TurnOffStyle(LONG_PTR combination, LONG_PTR style)
				{
					return combination & (~style);
				}

				bool GetExStyle(LONG_PTR exStyle)
				{
					return (InternalGetExStyle() & exStyle) != 0;
				}

				void SetExStyle(LONG_PTR exStyle, bool available)
				{
					if (available)
					{
						InternalSetExStyle(TurnOnStyle(InternalGetExStyle(), exStyle));
					}
					else
					{
						InternalSetExStyle(TurnOffStyle(InternalGetExStyle(), exStyle));
					}
				}

				bool GetStyle(LONG_PTR style)
				{
					return (InternalGetStyle() & style) != 0;
				}

				void SetStyle(LONG_PTR style, bool available)
				{
					if (available)
					{
						InternalSetStyle(TurnOnStyle(InternalGetStyle(), style));
					}
					else
					{
						InternalSetStyle(TurnOffStyle(InternalGetStyle(), style));
					}
				}
#pragma push_macro("_CONTROL")
#if defined _CONTROL
#undef _CONTROL
#endif
				NativeWindowMouseInfo ConvertMouse(WPARAM wParam, LPARAM lParam, bool wheelMessage, bool nonClient)
				{
					NativeWindowMouseInfo info;

					info.nonClient = false;
					if (nonClient)
					{
						switch (wParam)
						{
						case HTMINBUTTON:
						case HTMAXBUTTON:
						case HTCLOSE:
							break;
						default:
							info.nonClient = true;
							break;
						}
					}

					if(wheelMessage)
					{
						info.wheel=GET_WHEEL_DELTA_WPARAM(wParam);
						wParam=GET_KEYSTATE_WPARAM(wParam);
					}
					else
					{
						info.wheel=0;
					}

					if (nonClient)
					{
						info.ctrl = WinIsKeyPressing(VKEY::KEY_CONTROL);
						info.shift = WinIsKeyPressing(VKEY::KEY_SHIFT);
						info.left= WinIsKeyPressing(VKEY::KEY_LBUTTON);
						info.middle= WinIsKeyPressing(VKEY::KEY_MBUTTON);
						info.right = WinIsKeyPressing(VKEY::KEY_RBUTTON);
						
						POINTS point = MAKEPOINTS(lParam);
						NativePoint offset = GetClientBoundsInScreen().LeftTop();
						info.x = point.x - offset.x.value;
						info.y = point.y - offset.y.value;
					}
					else
					{
						info.ctrl=(wParam & MK_CONTROL)!=0;
						info.shift=(wParam & MK_SHIFT)!= 0;
						info.left=(wParam & MK_LBUTTON)!= 0;
						info.middle=(wParam & MK_MBUTTON)!= 0;
						info.right=(wParam & MK_RBUTTON)!= 0;

						POINTS point = MAKEPOINTS(lParam);

						if (wheelMessage)
						{
							NativePoint offset = GetClientBoundsInScreen().LeftTop();
							info.x = point.x - offset.x.value;
							info.y = point.y - offset.y.value;
						}
						else
						{
							info.x = point.x;
							info.y = point.y;
						}
					}
					return info;
				}

				NativeWindowKeyInfo ConvertKey(WPARAM wParam, LPARAM lParam)
				{
					NativeWindowKeyInfo info;
					info.code=(VKEY)wParam;
					info.ctrl=WinIsKeyPressing(VKEY::KEY_CONTROL);
					info.shift=WinIsKeyPressing(VKEY::KEY_SHIFT);
					info.alt=WinIsKeyPressing(VKEY::KEY_MENU);
					info.capslock=WinIsKeyToggled(VKEY::KEY_CAPITAL);
					info.autoRepeatKeyDown = (((vuint32_t)lParam) >> 30) % 2 == 1;
					return info;
				}

				NativeWindowCharInfo ConvertChar(WPARAM wParam)
				{
					NativeWindowCharInfo info;
					info.code=(wchar_t)wParam;
					info.ctrl=WinIsKeyPressing(VKEY::KEY_CONTROL);
					info.shift=WinIsKeyPressing(VKEY::KEY_SHIFT);
					info.alt=WinIsKeyPressing(VKEY::KEY_MENU);
					info.capslock=WinIsKeyToggled(VKEY::KEY_CAPITAL);
					return info;
				}
#pragma pop_macro("_CONTROL")

				void TrackMouse(bool enable)
				{
					TRACKMOUSEEVENT trackMouseEvent;
					trackMouseEvent.cbSize=sizeof(trackMouseEvent);
					trackMouseEvent.hwndTrack=handle;
					trackMouseEvent.dwFlags=(enable?0:TME_CANCEL) | TME_HOVER | TME_LEAVE;
					trackMouseEvent.dwHoverTime=HOVER_DEFAULT;
					TrackMouseEvent(&trackMouseEvent);
				}

				void UpdateCompositionForContent()
				{
					HIMC imc = ImmGetContext(handle);
					COMPOSITIONFORM cf;
					cf.dwStyle = CFS_POINT;
					cf.ptCurrentPos.x = (int)caretPoint.x.value;
					cf.ptCurrentPos.y = (int)caretPoint.y.value;
					ImmSetCompositionWindow(imc, &cf);
					ImmReleaseContext(handle, imc);
				}

				bool supressClosePopups = false;

				static void ClosePopupsOf(WindowsForm* owner, SortedList<WindowsForm*>& exceptions)
				{
					// TODO: (enumerable) foreach
					for (vint i = 0; i < owner->childWindows.Count(); i++)
					{
						auto popup = owner->childWindows[i];
						if (popup->windowMode != Normal && popup->IsVisible())
						{
							if (!exceptions.Contains(popup))
							{
								popup->Hide(false);
							}
						}
						ClosePopupsOf(popup, exceptions);
					}
				}

				bool HandleMessageInternal(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result)
				{
					if (!supressClosePopups)
					{
						bool closePopups = false;
						WindowsForm* activatedWindow = nullptr;
						WindowsForm* rootWindow = nullptr;
						SortedList<WindowsForm*> exceptions;

						switch (uMsg)
						{
						case WM_ACTIVATEAPP:
							if (wParam == FALSE)
							{
								closePopups = true;
							}
							break;
						case WM_ACTIVATE:
							switch (LOWORD(wParam))
							{
							case WA_ACTIVE:
							case WA_CLICKACTIVE:
								activatedWindow = this;
								closePopups = true;
								break;
							}
							break;
						case WM_LBUTTONDOWN:
						case WM_MBUTTONDOWN:
						case WM_RBUTTONDOWN:
						case WM_NCLBUTTONDOWN:
						case WM_NCMBUTTONDOWN:
						case WM_NCRBUTTONDOWN:
							activatedWindow = this;
							closePopups = true;
							break;
						}

						if (activatedWindow)
						{
							rootWindow = activatedWindow;
							exceptions.Add(rootWindow);
							while (auto parentWindow = rootWindow->parentWindow)
							{
								rootWindow = parentWindow;
								exceptions.Add(parentWindow);
							}
						}

						if (closePopups)
						{
							List<IWindowsForm*> allRootWindows;
							GetAllCreatedWindows(allRootWindows, true);

							// TODO: (enumerable) foreach
							for (vint i = 0; i < allRootWindows.Count(); i++)
							{
								if (auto windowsForm = dynamic_cast<WindowsForm*>(allRootWindows[i]))
								{
									ClosePopupsOf(windowsForm, exceptions);
								}
							}
						}
					}

					bool nonClient = false;

					switch(uMsg)
					{
					// ************************************** moving and sizing
					case WM_MOVING:case WM_SIZING:
						{
							LPRECT rawBounds=(LPRECT)lParam;
							NativeRect bounds(rawBounds->left, rawBounds->top, rawBounds->right, rawBounds->bottom);
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->Moving(bounds, false, (uMsg == WM_SIZING));
							}
							if(		rawBounds->left!=bounds.Left().value
								||	rawBounds->top!=bounds.Top().value
								||	rawBounds->right!=bounds.Right().value
								||	rawBounds->bottom!=bounds.Bottom().value)
							{
								rawBounds->left=(int)bounds.Left().value;
								rawBounds->top=(int)bounds.Top().value;
								rawBounds->right=(int)bounds.Right().value;
								rawBounds->bottom=(int)bounds.Bottom().value;
								result=TRUE;
							}
						}
						break;
					case WM_MOVE:case WM_SIZE:
						{
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->Moved();
							}
						}
						break;
					case WM_DPICHANGED:
						{
							dpiX = LOWORD(wParam);
							dpiY = HIWORD(wParam);
							UpdateDpiAwaredFields(false);
							auto newRect = (RECT*)lParam;
							MoveWindow(handle, newRect->left, newRect->top, (newRect->right - newRect->left), (newRect->bottom - newRect->top), FALSE);

							// TODO: (enumerable) foreach
							for (vint i = 0; i < listeners.Count(); i++)
							{
								listeners[i]->DpiChanged(true);
							}

							// TODO: (enumerable) foreach
							for (vint i = 0; i < listeners.Count(); i++)
							{
								listeners[i]->DpiChanged(false);
							}
						}
						break;
					// ************************************** state
					case WM_ENABLE:
						{
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								if(wParam==TRUE)
								{
									listeners[i]->Enabled();
								}
								else
								{
									listeners[i]->Disabled();
								}
							}
						}
						break;
					case WM_MOUSEACTIVATE:
						if (!enabledActivate)
						{
							result = MA_NOACTIVATE;
							return true;
						}
						break;
					case WM_ACTIVATE:
						{
							// TODO: (enumerable) foreach
							for (vint i = 0; i < listeners.Count(); i++)
							{
								if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE)
								{
									listeners[i]->GotFocus();
									listeners[i]->RenderingAsActivated();
								}
								else
								{
									listeners[i]->LostFocus();
									listeners[i]->RenderingAsDeactivated();
								}
							}
						}
						break;
					case WM_SHOWWINDOW:
						if (lParam == 0)
						{
							if (wParam == TRUE)
							{
								// TODO: (enumerable) foreach
								for (vint i = 0; i < listeners.Count(); i++)
								{
									listeners[i]->Opened();
								}
							}
							else
							{
								// TODO: (enumerable) foreach
								for (vint i = 0; i < listeners.Count(); i++)
								{
									listeners[i]->Closed();
								}
							}
						}
						break;
					case WM_CLOSE:
						{
							bool cancel = false;
							// TODO: (enumerable) foreach
							for (vint i = 0; i < listeners.Count(); i++)
							{
								listeners[i]->BeforeClosing(cancel);
							}
							if (!cancel)
							{
								// TODO: (enumerable) foreach
								for (vint i = 0; i < listeners.Count(); i++)
								{
									listeners[i]->AfterClosing();
								}
							}
							return cancel;
						}
						break;
					// ************************************** mouse
					case WM_NCLBUTTONDOWN:
						if (!customFrameMode) break;
						nonClient = true;
					case WM_LBUTTONDOWN:
						{
							NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->LeftButtonDown(info);
							}
						}
						break;
					case WM_NCLBUTTONUP:
						if (!customFrameMode) break;
						nonClient = true;
					case WM_LBUTTONUP:
						{
							NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->LeftButtonUp(info);
							}
						}
						break;
					case WM_NCLBUTTONDBLCLK:
						if (!customFrameMode) break;
						nonClient = true;
					case WM_LBUTTONDBLCLK:
						{
							NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->LeftButtonDoubleClick(info);
							}
						}
						break;
					case WM_NCRBUTTONDOWN:
						if (!customFrameMode) break;
						nonClient = true;
					case WM_RBUTTONDOWN:
						{
							NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->RightButtonDown(info);
							}
						}
						break;
					case WM_NCRBUTTONUP:
						if (!customFrameMode) break;
						nonClient = true;
					case WM_RBUTTONUP:
						{
							NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->RightButtonUp(info);
							}
						}
						break;
					case WM_NCRBUTTONDBLCLK:
						if (!customFrameMode) break;
						nonClient = true;
					case WM_RBUTTONDBLCLK:
						{
							NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->RightButtonDoubleClick(info);
							}
						}
						break;
					case WM_NCMBUTTONDOWN:
						if (!customFrameMode) break;
						nonClient = true;
					case WM_MBUTTONDOWN:
						{
							NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->MiddleButtonDown(info);
							}
						}
						break;
					case WM_NCMBUTTONUP:
						if (!customFrameMode) break;
						nonClient = true;
					case WM_MBUTTONUP:
						{
							NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->MiddleButtonUp(info);
							}
						}
						break;
					case WM_NCMBUTTONDBLCLK:
						if (!customFrameMode) break;
						nonClient = true;
					case WM_MBUTTONDBLCLK:
						{
							NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->MiddleButtonDoubleClick(info);
							}
						}
						break;
					case WM_NCMOUSEMOVE:
						if (!customFrameMode) break;
						nonClient = true;
					case WM_MOUSEMOVE:
						{
							NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
							if(info.x!=mouseLastX || info.y!=mouseLastY)
							{
								if(!mouseHoving)
								{
									mouseHoving=true;
									// TODO: (enumerable) foreach
									for(vint i=0;i<listeners.Count();i++)
									{
										listeners[i]->MouseEntered();
									}
									TrackMouse(true);
								}
								// TODO: (enumerable) foreach
								for(vint i=0;i<listeners.Count();i++)
								{
									listeners[i]->MouseMoving(info);
								}
							}
						}
						break;
					// ************************************** wheel
					case WM_MOUSEHWHEEL:
						{
							NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, true, false);
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->HorizontalWheel(info);
							}
						}
						break;
					case WM_MOUSEWHEEL:
						{
							NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, true, false);
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->VerticalWheel(info);
							}
						}
						break;
					// ************************************** mouse state
					case WM_NCMOUSELEAVE:
						nonClient = true;
					case WM_MOUSELEAVE:
						if (customFrameMode == nonClient)
						{
							mouseLastX=-1;
							mouseLastY=-1;
							mouseHoving=false;
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->MouseLeaved();
							}
						}
						break;
					case WM_NCMOUSEHOVER:
					case WM_MOUSEHOVER:
						{
							TrackMouse(true);
						}
						break;
					// ************************************** key
					case WM_KEYUP:
						{
							NativeWindowKeyInfo info=ConvertKey(wParam, lParam);
							info.autoRepeatKeyDown = false;
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->KeyUp(info);
							}
						}
						break;
					case WM_KEYDOWN:
						{
							NativeWindowKeyInfo info=ConvertKey(wParam, lParam);
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->KeyDown(info);
							}
						}
						break;
					case WM_SYSKEYUP:
						{
							NativeWindowKeyInfo info=ConvertKey(wParam, lParam);
							info.autoRepeatKeyDown = false;
							if (supressingAlt && !info.ctrl && !info.shift && info.code == VKEY::KEY_MENU)
							{
								supressingAlt = false;
								break;
							}
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->KeyUp(info);
							}
						}
						break;
					case WM_SYSKEYDOWN:
						{
							NativeWindowKeyInfo info=ConvertKey(wParam, lParam);
							if (supressingAlt && !info.ctrl && !info.shift && info.code == VKEY::KEY_MENU)
							{
								break;
							}
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->KeyDown(info);
							}
						}
						break;
					case WM_CHAR:
						{
							NativeWindowCharInfo info=ConvertChar(wParam);
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->Char(info);
							}
						}
						break;
					// ************************************** painting
					case WM_PAINT:
						{
							// TODO: (enumerable) foreach
							for(vint i=0;i<listeners.Count();i++)
							{
								listeners[i]->Paint();
							}
						}
						break;
					case WM_ERASEBKGND:
						result = 0;
						return true;
					case WM_NCPAINT:
					case WM_SYNCPAINT:
						if(customFrameMode)
						{
							result=0;
							return true;
						}
						break;
					// ************************************** IME
					case WM_IME_SETCONTEXT:
						if(wParam==TRUE)
						{
							HIMC imc = ImmGetContext(handle);
							ImmAssociateContext(hwnd, imc);
							ImmReleaseContext(handle, imc);
						}
						break;
					case WM_IME_STARTCOMPOSITION:
						UpdateCompositionForContent();
						break;
					// ************************************** hit test
					case WM_NCHITTEST:
						{
							POINTS location = MAKEPOINTS(lParam);
							NativePoint windowLocation = GetBounds().LeftTop();
							location.x -= (SHORT)windowLocation.x.value;
							location.y -= (SHORT)windowLocation.y.value;
							switch (PerformHitTest(From(listeners), { location.x,location.y }))
							{
							case INativeWindowListener::BorderNoSizing:
								result = HTBORDER;
								return true;
							case INativeWindowListener::BorderLeft:
								result = HTLEFT;
								return true;
							case INativeWindowListener::BorderRight:
								result = HTRIGHT;
								return true;
							case INativeWindowListener::BorderTop:
								result = HTTOP;
								return true;
							case INativeWindowListener::BorderBottom:
								result = HTBOTTOM;
								return true;
							case INativeWindowListener::BorderLeftTop:
								result = HTTOPLEFT;
								return true;
							case INativeWindowListener::BorderRightTop:
								result = HTTOPRIGHT;
								return true;
							case INativeWindowListener::BorderLeftBottom:
								result = HTBOTTOMLEFT;
								return true;
							case INativeWindowListener::BorderRightBottom:
								result = HTBOTTOMRIGHT;
								return true;
							case INativeWindowListener::Title:
								result = HTCAPTION;
								return true;
							case INativeWindowListener::ButtonMinimum:
								result = HTMINBUTTON;
								return true;
							case INativeWindowListener::ButtonMaximum:
								result = HTMAXBUTTON;
								return true;
							case INativeWindowListener::ButtonClose:
								result = HTCLOSE;
								return true;
							case INativeWindowListener::Client:
								result = HTCLIENT;
								return true;
							case INativeWindowListener::Icon:
								result = HTSYSMENU;
								return true;
							}
						}
						break;
					// ************************************** MISC
					case WM_SETCURSOR:
						{
							DWORD hitTestResult=LOWORD(lParam);
							if(hitTestResult==HTCLIENT)
							{
								HCURSOR cursorHandle=cursor->GetCursorHandle();
								if(GetCursor()!=cursorHandle)
								{
									SetCursor(cursorHandle);
								}
								result=TRUE;
								return true;
							}
						}
						break;
					case WM_NCCALCSIZE:
						if((BOOL)wParam && customFrameMode)
						{
							result=0;
							return true;
						}
						break;
					case WM_NCACTIVATE:
						if(customFrameMode)
						{
							if(wParam==TRUE)
							{
								result=FALSE;
							}
							else
							{
								result=TRUE;
							}
							return true;
						}
						break;
					}

					// handling custom frame
					if (customFrameMode)
					{
						switch (uMsg)
						{
						case WM_NCLBUTTONDOWN:
							switch (wParam)
							{
							case HTMINBUTTON:
							case HTMAXBUTTON:
							case HTCLOSE:
								result = 0;
								return true;
							}
							break;
						case WM_NCLBUTTONUP:
						case WM_LBUTTONUP:
							{
								NativeWindowMouseInfo info = ConvertMouse(wParam, lParam, false, nonClient);
								// TODO: (enumerable) this for-loop needs to be removed, because it is not looping, just leave the body
								for (vint i = 0; i < listeners.Count(); i++)
								{
									switch (PerformHitTest(From(listeners), { info.x,info.y }))
									{
									case INativeWindowListener::ButtonMinimum:
										ShowMinimized();
										return false;
									case INativeWindowListener::ButtonMaximum:
										if (GetSizeState() == INativeWindow::Maximized)
										{
											ShowRestored();
										}
										else
										{
											ShowMaximized();
										}
										return false;
									case INativeWindowListener::ButtonClose:
										Hide(true);
										return false;
									}
								}
							}
							break;
						}
					}
					return false;
				}
			protected:
				HWND								handle;
				WString								title;
				WindowsCursor*						cursor = nullptr;
				NativePoint							caretPoint;
				WindowsForm*						parentWindow = nullptr;
				List<WindowsForm*>					childWindows;
				WindowMode							windowMode;
				bool								isMainWindow = false;
				List<INativeWindowListener*>		listeners;
				vint								mouseLastX = -1;
				vint								mouseLastY = -1;
				bool								mouseHoving = false;
				Interface*							graphicsHandler = nullptr;
				bool								customFrameMode = false;
				bool								enabledActivate = true;
				List<Ptr<INativeMessageHandler>>	messageHandlers;
				bool								supressingAlt = false;
				Ptr<bool>							flagDisposed = Ptr(new bool(false));
				NativeMargin						customFramePadding;
				Ptr<GuiImageData>					defaultIcon;
				Ptr<GuiImageData>					replacementIcon;
				HICON								replacementHIcon = NULL;
				vint								dpiX = 0;
				vint								dpiY = 0;

				void UpdateDpiAwaredFields(bool refreshDpiXY)
				{
					if (refreshDpiXY)
					{
						UINT x = 0;
						UINT y = 0;
						DpiAwared_GetDpiForWindow(handle, &x, &y);
						dpiX = (vint)x;
						dpiY = (vint)y;
					}
					auto padding = (vint)(DpiAwared_GetSystemMetrics(SM_CXSIZEFRAME, (UINT)dpiX) + DpiAwared_GetSystemMetrics(SM_CXPADDEDBORDER, (UINT)dpiX));
					customFramePadding = NativeMargin(padding, padding, padding, padding);
				}
			public:
				WindowsForm(HWND parent, WString className, HINSTANCE hInstance, INativeWindow::WindowMode _windowMode)
					: windowMode(_windowMode)
				{
					{
						DWORD exStyle = WS_EX_APPWINDOW | WS_EX_CONTROLPARENT;
						DWORD style = WS_BORDER | WS_CAPTION | WS_SIZEBOX | WS_SYSMENU | WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_MAXIMIZEBOX | WS_MINIMIZEBOX;
						handle = CreateWindowEx(exStyle, className.Buffer(), L"", style, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, parent, NULL, hInstance, NULL);
					}
					if (windowMode == INativeWindow::Normal)
					{
						// use WS_POPUP in CreateWindowEx, because CW_USERDEFAULT is interpreted as 0, unlike WS_OVERLAPPED
						// if this is not a popup window, replace WS_POPUP with WS_OVERLAPPED
						auto style = InternalGetStyle();
						style = TurnOnStyle(style, WS_OVERLAPPED);
						style = TurnOffStyle(style, WS_POPUP);
						InternalSetStyle(style);
					}
					UpdateDpiAwaredFields(true);
				}

				~WindowsForm()
				{
					if (parentWindow)
					{
						parentWindow->childWindows.Remove(this);
					}
					// TODO: (enumerable) foreach
					for (vint i = childWindows.Count() - 1; i >= 0; i--)
					{
						childWindows[i]->SetParent(parentWindow);
					}

					*flagDisposed.Obj() = true;
					List<INativeWindowListener*> copiedListeners;
					CopyFrom(copiedListeners, listeners);
					// TODO: (enumerable) foreach
					for (vint i = 0; i < copiedListeners.Count(); i++)
					{
						INativeWindowListener* listener = copiedListeners[i];
						if (listeners.Contains(listener))
						{
							listener->Destroyed();
						}
					}
					DestroyWindow(handle);
				}

				void SetIsMainWindow()
				{
					isMainWindow = true;
				}

				void InvokeDestroying()
				{
					// TODO: (enumerable) foreach
					for(vint i=0;i<listeners.Count();i++)
					{
						listeners[i]->Destroying();
					}
				}

				bool HandleMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result)
				{
#define CHECK_DISPOSED if (*flag.Obj()) return skip
					auto flag = flagDisposed;
					bool skip = false;
					{
						for (auto handler : messageHandlers)
						{
							handler->BeforeHandle(hwnd, uMsg, wParam, lParam, skip);
							CHECK_DISPOSED;
						}
						if (skip)
						{
							return true;
						}
					}
					skip = HandleMessageInternal(hwnd, uMsg, wParam, lParam, result);
					CHECK_DISPOSED;
					if (GetWindowsFormFromHandle(hwnd))
					{
						for (auto handler : messageHandlers)
						{
							handler->AfterHandle(hwnd, uMsg, wParam, lParam, skip, result);
							CHECK_DISPOSED;
						}
					}
					return skip;
#undef CHECK_DISPOSED
				}

				HWND GetWindowHandle()override
				{
					return handle;
				}

				Interface* GetGraphicsHandler()override
				{
					return graphicsHandler;
				}

				void SetGraphicsHandler(Interface* handler)override
				{
					graphicsHandler=handler;
				}

				bool InstallMessageHandler(Ptr<INativeMessageHandler> handler)override
				{
					if (messageHandlers.Contains(handler.Obj()))
					{
						return false;
					}
					messageHandlers.Add(handler);
					return true;
				}

				bool UninstallMessageHandler(Ptr<INativeMessageHandler> handler)override
				{
					vint index = messageHandlers.IndexOf(handler.Obj());
					if (index == -1)return false;
					messageHandlers.RemoveAt(handler);
					return true;
				}

				bool IsActivelyRefreshing()override
				{
					return true;
				}

				NativeSize GetRenderingOffset()override
				{
					return { 0,0 };
				}

				Point Convert(NativePoint value)override
				{
					return Point((vint)value.x.value * 96 / dpiX, (vint)value.y.value * 96 / dpiY);
				}

				NativePoint Convert(Point value)override
				{
					return NativePoint(value.x * dpiX / 96, value.y * dpiY / 96);
				}

				Size Convert(NativeSize value)override
				{
					return Size((vint)value.x.value * 96 / dpiX, (vint)value.y.value * 96 / dpiY);
				}

				NativeSize Convert(Size value)override
				{
					return NativeSize(value.x * dpiX / 96, value.y * dpiY / 96);
				}

				Margin Convert(NativeMargin value)override
				{
					return Margin(
						(vint)value.left.value * 96 / dpiX,
						(vint)value.top.value * 96 / dpiY,
						(vint)value.right.value * 96 / dpiX,
						(vint)value.bottom.value * 96 / dpiY
					);
				}

				NativeMargin Convert(Margin value)override
				{
					return NativeMargin(
						(vint)value.left * dpiX / 96,
						(vint)value.top * dpiY / 96,
						(vint)value.right * dpiX / 96,
						(vint)value.bottom * dpiY / 96
					);
				}

				NativeRect GetBounds()override
				{
					RECT rect;
					GetWindowRect(handle, &rect);
					return NativeRect(rect.left, rect.top, rect.right, rect.bottom);
				}

				void SetBounds(const NativeRect& bounds)override
				{
					NativeRect newBounds=bounds;
					// TODO: (enumerable) foreach
					for(vint i=0;i<listeners.Count();i++)
					{
						listeners[i]->Moving(newBounds, true, false);
					}
					MoveWindow(handle, (int)newBounds.Left().value, (int)newBounds.Top().value, (int)newBounds.Width().value, (int)newBounds.Height().value, FALSE);
				}

				NativeSize GetClientSize()override
				{
					return GetClientBoundsInScreen().GetSize();
				}

				void SetClientSize(NativeSize size)override
				{
					if (customFrameMode)
					{
						RECT bounds;
						GetWindowRect(handle, &bounds);
						SetBounds(NativeRect(NativePoint(bounds.left, bounds.top), size));
					}
					else
					{
						RECT required = { 0,0,(int)size.x.value,(int)size.y.value };
						RECT bounds;
						GetWindowRect(handle, &bounds);
						DpiAwared_AdjustWindowRect(&required, handle, (UINT)dpiX);
						SetBounds(NativeRect(NativePoint(bounds.left, bounds.top), NativeSize(required.right - required.left, required.bottom - required.top)));
					}
				}

				NativeRect GetClientBoundsInScreen()override
				{
					if(customFrameMode)
					{
						return GetBounds();
					}
					else
					{
						RECT required={0,0,0,0};
						RECT bounds;
						GetWindowRect(handle, &bounds);
						DpiAwared_AdjustWindowRect(&required, handle, (UINT)dpiX);
						return NativeRect(
							NativePoint(
								(bounds.left-required.left),
								(bounds.top-required.top)
								),
							NativeSize(
								(bounds.right-bounds.left)-(required.right-required.left),
								(bounds.bottom-bounds.top)-(required.bottom-required.top)
								)
							);
					}
				}

				void SuggestMinClientSize(NativeSize size)override
				{
					// Ignored
				}

				WString GetTitle()override
				{
					return title;
				}

				void SetTitle(const WString& _title)override
				{
					title=_title;
					SetWindowText(handle, title.Buffer());
				}

				INativeCursor* GetWindowCursor()override
				{
					return cursor;
				}

				void SetWindowCursor(INativeCursor* _cursor)override
				{
					WindowsCursor* newCursor=dynamic_cast<WindowsCursor*>(_cursor);
					if(newCursor && cursor!=newCursor)
					{
						cursor=newCursor;
						if(mouseHoving && IsVisible())
						{
							SetCursor(cursor->GetCursorHandle());
						}
					}
				}
				
				NativePoint GetCaretPoint()override
				{
					return caretPoint;
				}

				void SetCaretPoint(NativePoint point)override
				{
					caretPoint=point;
					UpdateCompositionForContent();
				}

				INativeWindow* GetParent()override
				{
					return parentWindow;
				}

				void SetParent(INativeWindow* parent)override
				{
					if (parentWindow)
					{
						parentWindow->childWindows.Remove(this);
					}

					if ((parentWindow = dynamic_cast<WindowsForm*>(parent)))
					{
						parentWindow->childWindows.Add(this);
						// ::SetParent(handle, parentWindow->handle);
						SetWindowLongPtr(handle, GWLP_HWNDPARENT, (LONG_PTR)parentWindow->handle);
					}
					else
					{
						// ::SetParent(handle, NULL);
						SetWindowLongPtr(handle, GWLP_HWNDPARENT, NULL);
					}
				}

				WindowMode GetWindowMode()override
				{
					return windowMode;
				}

				void EnableCustomFrameMode()override
				{
					customFrameMode=true;
				}

				void DisableCustomFrameMode()override
				{
					customFrameMode=false;
				}

				bool IsCustomFrameModeEnabled()override
				{
					return customFrameMode;
				}

				NativeMargin GetCustomFramePadding()override
				{
					if (GetSizeBox() || GetTitleBar())
					{
						return customFramePadding;
					}
					else
					{
						return NativeMargin(0, 0, 0, 0);
					}
				}

				Ptr<GuiImageData> GetIcon()override
				{
					if (replacementIcon && replacementIcon->GetImage())
					{
						return replacementIcon;
					}
					else
					{
						if (!defaultIcon)
						{
							auto icon = CreateWindowDefaultIcon(16);
							if (icon == NULL)
							{
								icon = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 16, 16, LR_SHARED);
							}
							if (icon != NULL)
							{
								defaultIcon = Ptr(new GuiImageData(CreateImageFromHICON(icon), 0));
							}
						}
						return defaultIcon;
					}
				}

				static double GetSizeScore(vint size)
				{
					if (size > 32)
					{
						return 32.0 / size;
					}
					else if (size < 32)
					{
						return size / 32.0 - 1;
					}
					else
					{
						return 1.0;
					}
				}

				static vint GetBppFromFormat(const WICPixelFormatGUID& format)
				{
					if (format == GUID_WICPixelFormat1bppIndexed)	return 1;
					if (format == GUID_WICPixelFormat2bppIndexed)	return 2;
					if (format == GUID_WICPixelFormat4bppIndexed)	return 4;
					if (format == GUID_WICPixelFormat8bppIndexed)	return 8;
					if (format == GUID_WICPixelFormatBlackWhite)	return 1;
					if (format == GUID_WICPixelFormat2bppGray)		return 2;
					if (format == GUID_WICPixelFormat4bppGray)		return 4;
					if (format == GUID_WICPixelFormat8bppGray)		return 8;
					if (format == GUID_WICPixelFormat8bppAlpha)		return 8;
					if (format == GUID_WICPixelFormat16bppBGR555)	return 16;
					if (format == GUID_WICPixelFormat16bppBGR565)	return 16;
					if (format == GUID_WICPixelFormat16bppBGRA5551)	return 16;
					if (format == GUID_WICPixelFormat16bppGray)		return 16;
					if (format == GUID_WICPixelFormat24bppBGR)		return 24;
					if (format == GUID_WICPixelFormat24bppRGB)		return 24;
					if (format == GUID_WICPixelFormat32bppBGR)		return 32;
					if (format == GUID_WICPixelFormat32bppBGRA)		return 32;
					if (format == GUID_WICPixelFormat32bppPBGRA)	return 32;
					return -1;
				}

				void SetIcon(Ptr<GuiImageData> icon)override
				{
					replacementIcon = icon;
					HICON newReplacementHIcon = NULL;
					if (replacementIcon && replacementIcon->GetImage())
					{
						stream::MemoryStream memoryStream;

						replacementIcon->GetImage()->SaveToStream(memoryStream, INativeImage::Icon);
						if (memoryStream.Size() > 0)
						{
							newReplacementHIcon = CreateIconFromResource((PBYTE)memoryStream.GetInternalBuffer(), (DWORD)memoryStream.Size(), TRUE, 0x00030000);
							if (newReplacementHIcon != NULL)
							{
								goto SKIP;
							}
						}

						INativeImageFrame* selectedFrame = nullptr;
						for (vint i = 0; i < replacementIcon->GetImage()->GetFrameCount(); i++)
						{
							auto frame = replacementIcon->GetImage()->GetFrame(i);
							auto size = frame->GetSize();
							if (size.x == size.y)
							{
								auto bitmap = GetWICBitmap(frame);
								WICPixelFormatGUID format;
								HRESULT hr = bitmap->GetPixelFormat(&format);
								if (hr != S_OK) continue;

								if (!selectedFrame)
								{
									selectedFrame = frame;
								}
								else
								{
									auto score = GetSizeScore(size.x);
									auto scoreSelected = GetSizeScore(selectedFrame->GetSize().x);
									if (score > scoreSelected)
									{
										selectedFrame = frame;
									}
									else if (score == scoreSelected)
									{
										WICPixelFormatGUID selectedFormat;
										auto selectedBitmap = GetWICBitmap(selectedFrame);
										hr = selectedBitmap->GetPixelFormat(&selectedFormat);

										auto bpp = GetBppFromFormat(format);
										auto bppSelected = GetBppFromFormat(selectedFormat);
										if (bpp > bppSelected)
										{
											selectedFrame = frame;
										}
									}
								}
							}
						}

						if (selectedFrame)
						{
							bool succeeded = false;
							WindowsBitmapImage newBitmap(replacementIcon->GetImage()->GetImageService(), GetWICBitmap(selectedFrame), replacementIcon->GetImage()->GetFormat());
							newBitmap.SaveToStream(memoryStream, INativeImage::Bmp);
							if (memoryStream.Size() > 0)
							{
								auto pBuffer = (char*)memoryStream.GetInternalBuffer();
								tagBITMAPFILEHEADER bfh = *(tagBITMAPFILEHEADER*)pBuffer;
								tagBITMAPINFOHEADER bih = *(tagBITMAPINFOHEADER*)(pBuffer + sizeof(tagBITMAPFILEHEADER));
								RGBQUAD rgb = *(RGBQUAD*)(pBuffer + sizeof(tagBITMAPFILEHEADER) + sizeof(tagBITMAPINFOHEADER));

								BITMAPINFO bi;
								bi.bmiColors[0] = rgb;
								bi.bmiHeader = bih;

								char* pPixels = (pBuffer + bfh.bfOffBits);
								char* ppvBits;
								auto hBitmap = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, (void**)&ppvBits, NULL, 0);
								if (hBitmap != NULL)
								{
									SetDIBits(NULL, hBitmap, 0, bih.biHeight, pPixels, &bi, DIB_RGB_COLORS);
									auto himl = ImageList_Create(32, 32, ILC_COLOR32, 1, 1);
									if (himl != NULL)
									{
										int addResult = ImageList_Add(himl, hBitmap, NULL);
										newReplacementHIcon = ImageList_GetIcon(himl, 0, ILD_NORMAL);
										ImageList_Destroy(himl);
									}
									DeleteObject(hBitmap);
								}
							}
						}
					}

				SKIP:
					{
						HICON hAppIcon = newReplacementHIcon;
						if (hAppIcon == NULL) hAppIcon = CreateWindowDefaultIcon();
						if (hAppIcon == NULL) hAppIcon = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
						bool isVisible = IsVisible();
						if (isVisible) SendMessage(handle, WM_SETREDRAW, (WPARAM)FALSE, NULL);
						SendMessage(handle, WM_SETICON, ICON_BIG, (LPARAM)hAppIcon);
						SendMessage(handle, WM_SETICON, ICON_SMALL, (LPARAM)hAppIcon);
						if (isVisible) SendMessage(handle, WM_SETREDRAW, (WPARAM)TRUE, NULL);

						if (isMainWindow)
						{
							SendMessage(GetWindow(handle, GW_OWNER), WM_SETICON, ICON_BIG, (LPARAM)hAppIcon);
							SendMessage(GetWindow(handle, GW_OWNER), WM_SETICON, ICON_SMALL, (LPARAM)hAppIcon);
						}
					}

					if (replacementHIcon != NULL)
					{
						DestroyIcon(replacementHIcon);
					}
					replacementHIcon = newReplacementHIcon;
				}

				WindowSizeState GetSizeState()override
				{
					if(IsIconic(handle))
					{
						return INativeWindow::Minimized;
					}
					else if(IsZoomed(handle))
					{
						return INativeWindow::Maximized;
					}
					else
					{
						return INativeWindow::Restored;
					}
				}

				void Show()override
				{
					SetForegroundWindow(handle);
					ShowWindow(handle, SW_SHOWNORMAL);
				}

				void ShowDeactivated()override
				{
					ShowWindow(handle, SW_SHOWNOACTIVATE);
					SetWindowPos(handle, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
				}

				void ShowRestored()override
				{
					ShowWindow(handle, SW_RESTORE);
				}

				void ShowMaximized()override
				{
					ShowWindow(handle, SW_SHOWMAXIMIZED);
				}

				void ShowMinimized()override
				{
					ShowWindow(handle, SW_SHOWMINIMIZED);
				}

				void Hide(bool closeWindow)override
				{
					PostMessage(handle, WM_CLOSE, NULL, NULL);
				}

				bool IsVisible()override
				{
					return IsWindowVisible(handle)!=0;
				}

				void Enable()override
				{
					EnableWindow(handle, TRUE);
				}

				void Disable()override
				{
					EnableWindow(handle, FALSE);
				}

				bool IsEnabled()override
				{
					return IsWindowEnabled(handle)!=0;
				}

				void SetActivate()override
				{
					SetActiveWindow(handle);
				}

				bool IsActivated()override
				{
					return GetActiveWindow()==handle;
				}

				bool IsRenderingAsActivated()override
				{
					// TODO: should render as activated when
					//   is activated
					//   is a parent window of one that rendering as activated
					return IsActivated();
				}

				void ShowInTaskBar()override
				{
					SetExStyle(WS_EX_APPWINDOW, true);
				}

				void HideInTaskBar()override
				{
					SetExStyle(WS_EX_APPWINDOW, false);
				}

				bool IsAppearedInTaskBar()override
				{
					return GetExStyle(WS_EX_APPWINDOW);
				}

				void EnableActivate()override
				{
					enabledActivate = true;
					SetExStyle(WS_EX_NOACTIVATE, false);
				}

				void DisableActivate()override
				{
					enabledActivate = false;
					SetExStyle(WS_EX_NOACTIVATE, true);
				}

				bool IsEnabledActivate()override
				{
					return enabledActivate;
				}

				bool RequireCapture()override
				{
					SetCapture(handle);
					return true;
				}

				bool ReleaseCapture()override
				{
					::ReleaseCapture();
					return true;
				}

				bool IsCapturing()override
				{
					return GetCapture()==handle;
				}

				bool GetMaximizedBox()override
				{
					return GetStyle(WS_MAXIMIZEBOX);
				}

				void SetMaximizedBox(bool visible)override
				{
					SetStyle(WS_MAXIMIZEBOX, visible);
				}

				bool GetMinimizedBox()override
				{
					return GetStyle(WS_MINIMIZEBOX);
				}

				void SetMinimizedBox(bool visible)override
				{
					SetStyle(WS_MINIMIZEBOX, visible);
				}

				bool GetBorder()override
				{
					return GetStyle(WS_BORDER);
				}

				void SetBorder(bool visible)override
				{
					SetStyle(WS_BORDER, visible);
				}

				bool GetSizeBox()override
				{
					return GetStyle(WS_SIZEBOX);
				}

				void SetSizeBox(bool visible)override
				{
					SetStyle(WS_SIZEBOX, visible);
				}

				bool GetIconVisible()override
				{
					return GetStyle(WS_SYSMENU);
				}

				void SetIconVisible(bool visible)override
				{
					SetStyle(WS_SYSMENU, visible);
				}

				bool GetTitleBar()override
				{
					return GetStyle(WS_CAPTION);
				}

				void SetTitleBar(bool visible)override
				{
					SetStyle(WS_CAPTION, visible);
				}

				bool GetTopMost()override
				{
					return GetExStyle(WS_EX_TOPMOST);
				}

				void SetTopMost(bool topmost)override
				{
					SetWindowPos(handle, (topmost ? HWND_TOPMOST : HWND_NOTOPMOST), 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_FRAMECHANGED);
				}

				void SupressAlt()override
				{
					if (!supressingAlt)
					{
						supressingAlt = true;
						PostMessage(handle, WM_SYSKEYDOWN, VK_MENU, 0);
						PostMessage(handle, WM_SYSKEYUP, VK_MENU, 0);
					}
				}

				bool InstallListener(INativeWindowListener* listener)override
				{
					if(listeners.Contains(listener))
					{
						return false;
					}
					else
					{
						listeners.Add(listener);
						return true;
					}
				}

				bool UninstallListener(INativeWindowListener* listener)override
				{
					if(listeners.Contains(listener))
					{
						listeners.Remove(listener);
						return true;
					}
					else
					{
						return false;
					}
				}

				void RedrawContent()override
				{
					if(graphicsHandler)
					{
						SendMessage(this->handle, WM_PAINT, NULL, NULL);
					}
				}
			};

/***********************************************************************
WindowsController
***********************************************************************/

			LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
			LRESULT CALLBACK GodProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

			class WindowsController : public Object, public virtual INativeController, public virtual INativeWindowService
			{
				friend class WindowsAutomationService;
			protected:
				WinClass							windowClass;
				WinClass							godClass;
				HINSTANCE							hInstance;
				HWND								godWindow;
				Dictionary<HWND, WindowsForm*>		windows;
				WindowsForm*						mainWindow = nullptr;
				HWND								mainWindowHandle = 0;

				SharedCallbackService				callbackService;
				SharedAsyncService					asyncService;
				WindowsResourceService				resourceService;
				WindowsClipboardService				clipboardService;
				WindowsImageService					imageService;
				WindowsScreenService				screenService;
				WindowsInputService					inputService;
				WindowsDialogService				dialogService;

			public:
				WindowsController(HINSTANCE _hInstance)
					:hInstance(_hInstance)
					,windowClass(L"VczhWindow", false, false, WndProc, _hInstance)
					,godClass(L"GodWindow", false, false, GodProc, _hInstance)
					,screenService(&GetHWNDFromNativeWindowHandle)
					,dialogService(&GetHWNDFromNativeWindowHandle)
				{
					godWindow=CreateWindowEx(WS_EX_CONTROLPARENT, godClass.GetName().Buffer(), L"GodWindow", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
					clipboardService.SetOwnerHandle(godWindow);
					inputService.SetOwnerHandle(godWindow);
				}

				~WindowsController()
				{
					inputService.StopTimer();
					clipboardService.SetOwnerHandle(NULL);
					DestroyWindow(godWindow);
				}

				WindowsForm* GetWindowsFormFromHandle(HWND hwnd)
				{
					vint index = windows.Keys().IndexOf(hwnd);
					if (index == -1)return 0;
					return windows.Values()[index];
				}

				void GetAllCreatedWindows(collections::List<IWindowsForm*>& createdWindows, bool rootWindowOnly)
				{
					if (rootWindowOnly)
					{
						for (auto window : windows.Values())
						{
							if (window->GetWindowMode() == INativeWindow::Normal)
							{
								createdWindows.Add(window);
							}
						}
					}
					else
					{
						CopyFrom(createdWindows, windows.Values());
					}
				}

				bool HandleMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result)
				{
					bool skipDefaultProcedure=false;
					{
						vint index = windows.Keys().IndexOf(hwnd);
						if (index != -1)
						{
							WindowsForm* window = windows.Values().Get(index);
							skipDefaultProcedure = window->HandleMessage(hwnd, uMsg, wParam, lParam, result);
							switch (uMsg)
							{
							case WM_CLOSE:
								if (!skipDefaultProcedure)
								{
									ShowWindow(window->GetWindowHandle(), SW_HIDE);
									if (window != mainWindow)
									{
										skipDefaultProcedure = true;
									}
								}
								break;
							case WM_DESTROY:
								DestroyNativeWindow(window);
								break;
							}
						}
					}
					{
						if (hwnd == mainWindowHandle && uMsg == WM_DESTROY)
						{
							for (auto window : windows.Values())
							{
								if (window->IsVisible())
								{
									window->Hide(true);
								}
							}
							List<WindowsForm*> normalWindows;
							CopyFrom(
								normalWindows,
								From(windows.Values())
									.Where([](WindowsForm* window)
									{
										return window->GetWindowMode() == INativeWindow::Normal;
									})
								);
							for (auto window : normalWindows)
							{
								DestroyNativeWindow(window);
							}
							// TODO: (enumerable) foreach:reversed
							for (vint i = windows.Count() - 1; i >= 0; i--)
							{
								auto window = windows.Values()[i];
								DestroyNativeWindow(window);
							}
							
							PostQuitMessage(0);
						}
					}

					if (uMsg == WM_MOVING || uMsg == WM_SIZING)
					{
						asyncService.ExecuteAsyncTasks();
					}
					return skipDefaultProcedure;
				}

				//=======================================================================

				const NativeWindowFrameConfig& GetMainWindowFrameConfig()
				{
					return NativeWindowFrameConfig::Default;
				}

				const NativeWindowFrameConfig& GetNonMainWindowFrameConfig()
				{
					return NativeWindowFrameConfig::Default;
				}

				INativeWindow* CreateNativeWindow(INativeWindow::WindowMode windowMode)override
				{
					WindowsForm* window = new WindowsForm(godWindow, windowClass.GetName(), hInstance, windowMode);
					windows.Add(window->GetWindowHandle(), window);
					callbackService.InvokeNativeWindowCreated(window);
					window->SetWindowCursor(resourceService.GetDefaultSystemCursor());
					return window;
				}

				void DestroyNativeWindow(INativeWindow* window)override
				{
					auto windowsForm = dynamic_cast<WindowsForm*>(window);
					windowsForm->InvokeDestroying();
					if (windowsForm != 0 && windows.Keys().Contains(windowsForm->GetWindowHandle()))
					{
						callbackService.InvokeNativeWindowDestroying(window);
						windows.Remove(windowsForm->GetWindowHandle());
						if (mainWindow == windowsForm)
						{
							mainWindow = nullptr;
						}
						delete windowsForm;
					}
				}

				INativeWindow* GetMainWindow()override
				{
					return mainWindow;
				}

				inline bool RunOneCycleInternal()
				{
					MSG message;
					if (!GetMessage(&message, NULL, 0, 0)) return false;
					TranslateMessage(&message);
					DispatchMessage(&message);
					asyncService.ExecuteAsyncTasks();
					return true;
				}

				void Run(INativeWindow* window)override
				{
					mainWindow = dynamic_cast<WindowsForm*>(GetWindowsForm(window));
					mainWindowHandle = mainWindow->GetWindowHandle();
					mainWindow->SetIsMainWindow();
					mainWindow->Show();
					while (RunOneCycleInternal());
				}

				bool RunOneCycle()override
				{
					return RunOneCycleInternal();
				}

				INativeWindow* GetWindow(NativePoint location)override
				{
					POINT p;
					p.x=(int)location.x.value;
					p.y=(int)location.y.value;
					HWND handle=WindowFromPoint(p);
					vint index=windows.Keys().IndexOf(handle);
					if(index==-1)
					{
						return 0;
					}
					else
					{
						return windows.Values().Get(index);
					}
				}

				//=======================================================================

				INativeCallbackService* CallbackService()
				{
					return &callbackService;
				}

				INativeResourceService* ResourceService()
				{
					return &resourceService;
				}
				
				INativeAsyncService* AsyncService()
				{
					return &asyncService;
				}

				INativeClipboardService* ClipboardService()
				{
					return &clipboardService;
				}

				INativeImageService* ImageService()
				{
					return &imageService;
				}

				INativeScreenService* ScreenService()
				{
					return &screenService;
				}

				INativeWindowService* WindowService()
				{
					return this;
				}

				INativeInputService* InputService()
				{
					return &inputService;
				}

				INativeDialogService* DialogService()
				{
					return &dialogService;
				}

				INativeAutomationService* AutomationService()
				{
					// Use INativeAutomationService::UnavailableService
					return nullptr;
				}

				WString GetExecutablePath()
				{
					Array<wchar_t> buffer(65536);
					GetModuleFileName(NULL, &buffer[0], (DWORD)buffer.Count());
					return &buffer[0];
				}

				//=======================================================================

				void InvokeGlobalTimer()
				{
					callbackService.InvokeGlobalTimer();
				}

				void InvokeClipboardUpdated()
				{
					callbackService.InvokeClipboardUpdated();
				}

				void InvokeGlobalShortcutkeyActivated(vint id)
				{
					callbackService.InvokeGlobalShortcutKeyActivated(id);
				}
			};

/***********************************************************************
Windows Procedure
***********************************************************************/

			WindowsController* windowsController = nullptr;

			LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
			{
				if (windowsController)
				{
					LRESULT result = 0;
					if (windowsController->HandleMessage(hwnd, uMsg, wParam, lParam, result))
					{
						return result;
					}
				}
				return DefWindowProc(hwnd, uMsg, wParam, lParam);
			}

			LRESULT CALLBACK GodProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
			{
				if (windowsController)
				{
					switch (uMsg)
					{
					case WM_TIMER:
						windowsController->InvokeGlobalTimer();
						break;
					case WM_CLIPBOARDUPDATE:
						windowsController->InvokeClipboardUpdated();
						break;
					case WM_HOTKEY:
						if (wParam > 0)
						{
							windowsController->InvokeGlobalShortcutkeyActivated((vint)wParam);
						}
						break;
					}
				}
				return DefWindowProc(hwnd, uMsg, wParam, lParam);
			}

/***********************************************************************
Windows Platform Native Controller
***********************************************************************/

			void StartWindowsNativeController(HINSTANCE hInstance)
			{
				CHECK_ERROR(!windowsController, L"vl::presentation::windows::StartWindowsNativeController(HINSTANCE)#The Windows native controller has been started.");
				windowsController = new WindowsController(hInstance);
			}

			INativeController* GetWindowsNativeController()
			{
				return windowsController;
			}

			IWindowsForm* GetWindowsFormFromHandle(HWND hwnd)
			{
				if (windowsController)
				{
					return windowsController->GetWindowsFormFromHandle(hwnd);
				}
				return nullptr;
			}

			IWindowsForm* GetWindowsForm(INativeWindow* window)
			{
				return dynamic_cast<WindowsForm*>(window);
			}

			void GetAllCreatedWindows(collections::List<IWindowsForm*>& windows, bool rootWindowOnly)
			{
				if (windowsController)
				{
					windowsController->GetAllCreatedWindows(windows, rootWindowOnly);
				}
			}

			void StopWindowsNativeController()
			{
				CHECK_ERROR(windowsController, L"vl::presentation::windows::StopWindowsNativeController()#The Windows native controller has been stopped.");
				delete windowsController;
				windowsController = nullptr;
			}

			void EnableCrossKernelCrashing()
			{
				/*
				"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options"
					DWORD DisableUserModeCallbackFilter = 1

				"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\TestCppCodegen.exe"
					DWORD DisableUserModeCallbackFilter = 1
				*/
				typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags); 
				typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags); 
				const DWORD EXCEPTION_SWALLOWING = 0x1;
 
				HMODULE kernel32 = LoadLibrary(L"kernel32.dll"); 
				tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy"); 
				tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy"); 
				if (pGetPolicy && pSetPolicy) 
				{ 
					DWORD dwFlags; 
					if (pGetPolicy(&dwFlags)) 
					{ 
						// Turn off the filter 
						pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
					} 
				} 
			}

/***********************************************************************
WindowsAutomationServiceBase
***********************************************************************/

			template<typename TBase>
			WString WindowsAutomationServiceBase<TBase>::RunIOCommandInternal(Nullable<WString> windowId, const WString& ioCommand)
			{
				WindowsForm* windowsForm = dynamic_cast<WindowsForm*>(this->GetNativeWindow(windowId));
				if (!windowsForm)
				{
					return L"!Invalid window.";
				}

				return RunIOCommandOnNativeWindow(&this->ioCommandState, GetWindowsNativeController(), windowsForm, windowsForm->listeners, ioCommand);
			}

			template<typename TBase>
			void WindowsAutomationServiceBase<TBase>::Stop()
			{
				TBase::Stop();
				StopWindowsHttpAutomationService();
			}

			template<typename TBase>
			INativeAutomationService::IOCommandAvailability WindowsAutomationServiceBase<TBase>::CanRunIOCommands()
			{
				return INativeAutomationService::IOCommandAvailability::Enabled;
			}

/***********************************************************************
WindowsAutomationService
***********************************************************************/

			Nullable<WString> WindowsAutomationService::GetNativeWindowId(INativeWindow* window)
			{
#define ERROR_MESSAGE_PREFIX L"vl::presentation::windows::WindowsAutomationService::GetNativeWindowId(INativeWindow*)#"
				auto controller = dynamic_cast<WindowsController*>(GetWindowsNativeController());
				CHECK_ERROR(controller->windows.Values().Contains(dynamic_cast<WindowsForm*>(window)), ERROR_MESSAGE_PREFIX L"The specified INativeWindow instance should be native.");
				return utow(static_cast<vuint>(reinterpret_cast<intptr_t>(window)));
#undef ERROR_MESSAGE_PREFIX
			}

			INativeWindow* WindowsAutomationService::GetNativeWindow(Nullable<WString> windowId)
			{
				auto controller = dynamic_cast<WindowsController*>(GetWindowsNativeController());
				if (windowId)
				{
					WindowsForm* windowsForm = reinterpret_cast<WindowsForm*>(static_cast<intptr_t>(wtou(windowId.Value())));
					if (!controller->windows.Values().Contains(windowsForm))
					{
						return nullptr;
					}
					return windowsForm;
				}
				else
				{
					return controller->mainWindow;
				}
			}

			WindowsAutomationService::WindowsAutomationService()
			{
			}

			WindowsAutomationService::~WindowsAutomationService()
			{
			}

/***********************************************************************
WindowsAutomationServiceHosted
***********************************************************************/

			WindowsAutomationServiceHosted::WindowsAutomationServiceHosted()
			{
			}

			WindowsAutomationServiceHosted::~WindowsAutomationServiceHosted()
			{
			}

/***********************************************************************
WindowsAutomationServiceRenderer
***********************************************************************/

			WindowsAutomationServiceRenderer::WindowsAutomationServiceRenderer(remote_renderer::GuiRemoteRendererSingle* _renderer)
				: WindowsAutomationServiceBase<AutomationServiceRenderer>(_renderer)
			{
			}

			WindowsAutomationServiceRenderer::~WindowsAutomationServiceRenderer()
			{
			}

			INativeAutomationService::IOCommandAvailability WindowsAutomationServiceRenderer::CanRunIOCommands()
			{
				return AutomationServiceRenderer::CanRunIOCommands();
			}

/***********************************************************************
HttpAutomationService
***********************************************************************/

			class HttpAutomationService : public inter_process::windows_http::HttpServerApi
			{
			protected:
				WString			urlControls;
				WString			urlDom;
				WString			urlIO;

				void OnHttpRequestReceived(PHTTP_REQUEST pRequest)
				{
					auto mainWindow = GetCurrentController()->WindowService()->GetMainWindow();
					auto asyncService = GetCurrentController()->AsyncService();
					auto automationService = GetCurrentController()->AutomationService();

					try
					{
						Nullable<WString> respondString;
						if (pRequest->Verb == HttpVerbGET)
						{
							if (pRequest->CookedUrl.pAbsPath == urlControls)
							{
								if (automationService->CanDumpControlTree())
								{
									asyncService->InvokeInMainThreadAndWait(mainWindow, [&]()
									{
										respondString = automationService->DumpControlTree();
									});
								}
							}
							else if (pRequest->CookedUrl.pAbsPath == urlDom)
							{
								if (automationService->CanDumpDomTree())
								{
									asyncService->InvokeInMainThreadAndWait(mainWindow, [&]()
									{
										respondString = automationService->DumpDomTree();
									});
								}
							}
						}
						else if (pRequest->Verb == HttpVerbPOST)
						{
							if (wcsncmp(pRequest->CookedUrl.pAbsPath, urlIO.Buffer(), (size_t)urlIO.Length()) == 0)
							{
								Nullable<WString> windowId;
								auto pId = pRequest->CookedUrl.pAbsPath + urlIO.Length();
								if (*pId == L'/')
								{
									windowId = ++pId;
								}
								else if (*pId)
								{
									SendResponse(GetHttpRequestQueue(), pRequest->RequestId, { 404, L"URL not supported." });
									return;
								}

								if (automationService->CanRunIOCommands() != INativeAutomationService::IOCommandAvailability::Disabled)
								{
									WString body = GetUtf8Body(pRequest).Value();
									respondString = automationService->RunIOCommand(windowId, body);
								}
							}
						}

						if (respondString)
						{
							return SendResponseUtf8(GetHttpRequestQueue(), pRequest->RequestId, respondString.Value());
						}
					}
					catch (const Error& error)
					{
						return (void)SendResponse(GetHttpRequestQueue(), pRequest->RequestId, { 404, WString::Unmanaged(error.Description()) });
					}
					catch (const Exception& ex)
					{
						return (void)SendResponse(GetHttpRequestQueue(), pRequest->RequestId, { 404, ex.Message() });
					}
					SendResponse(GetHttpRequestQueue(), pRequest->RequestId, { 404, L"URL not supported." });
				}

			public:
				HttpAutomationService(const WString& applicationName, vint port)
					: HttpServerApi(WString::Unmanaged(L"http://localhost:") + itow(port) + WString::Unmanaged(L"/") + applicationName + WString::Unmanaged(L"/"), false)
					, urlControls			(WString::Unmanaged(L"/") + applicationName + WString::Unmanaged(L"/Controls"))
					, urlDom				(WString::Unmanaged(L"/") + applicationName + WString::Unmanaged(L"/Dom"))
					, urlIO					(WString::Unmanaged(L"/") + applicationName + WString::Unmanaged(L"/IO"))
				{
				}
			};

			HttpAutomationService* httpAutomationService = nullptr;

			void StartWindowsHttpAutomationService(const WString& applicationName, vint port)
			{
				if (!GetCurrentController()->AutomationService()->Available())
				{
					return;
				}
				if (!httpAutomationService)
				{
					httpAutomationService = new HttpAutomationService(applicationName, port);
					httpAutomationService->Start();
				}
			}

			void StopWindowsHttpAutomationService()
			{
				if (httpAutomationService)
				{
					httpAutomationService->Stop();
					delete httpAutomationService;
					httpAutomationService = nullptr;
				}
			}
		}
	}
}


/***********************************************************************
.\DIRECT2D\WINDIRECT2DAPPLICATION.CPP
***********************************************************************/

#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dwrite.lib")
#pragma comment(lib, "d3d11.lib")

namespace vl
{
	namespace presentation
	{
		using namespace elements;

		namespace windows
		{
			using namespace vl::collections;

/***********************************************************************
WindowListener
***********************************************************************/

			class Direct2DWindowsNativeWindowListener : public Object, public INativeWindowListener
			{
			protected:
				ID2D1Factory*					d2dFactory;
				INativeWindow*					window;
				bool							rendering = false;
				bool							movedWhileRendering = false;

				virtual void					RebuildCanvas(NativeSize size) = 0;
			public:
				Direct2DWindowsNativeWindowListener(INativeWindow* _window, ID2D1Factory* _d2dFactory)
					:window(_window)
					,d2dFactory(_d2dFactory)
				{
				}

				void Moved()override
				{
					if (rendering)
					{
						movedWhileRendering = true;
					}
					else
					{
						ResizeRenderTarget();
					}
				}

				void ResizeRenderTarget()
				{
					RebuildCanvas(window->GetClientSize());
				}

				void StartRendering()
				{
					rendering = true;
				}

				void StopRendering()
				{
					rendering = false;
				}

				bool RetrieveAndResetMovedWhileRendering()
				{
					bool result = movedWhileRendering;
					movedWhileRendering = false;
					return result;
				}

				virtual ID2D1RenderTarget*		GetDirect2DRenderTarget() = 0;
				virtual void					RecreateRenderTarget() = 0;
				virtual bool					PresentRenderTarget() = 0;
			};

/***********************************************************************
WindowListener 1.0
***********************************************************************/

			class Direct2DWindowsNativeWindowListener_1_0 : public Direct2DWindowsNativeWindowListener
			{
			protected:
				ComPtr<ID2D1HwndRenderTarget>	d2dRenderTarget;
				NativeSize						previousSize;

				void RebuildCanvas(NativeSize size)override
				{
					if (size.x <= 1) size.x = 1;
					if (size.y <= 1) size.y = 1;
					if (!d2dRenderTarget)
					{
						ID2D1HwndRenderTarget* renderTarget = 0;
						IWindowsForm* form = GetWindowsForm(window);
						D2D1_RENDER_TARGET_PROPERTIES tp = D2D1::RenderTargetProperties();
						{
							UINT dpiX = 0;
							UINT dpiY = 0;
							DpiAwared_GetDpiForWindow(form->GetWindowHandle(), &dpiX, &dpiY);
							tp.dpiX = (FLOAT)dpiX;
							tp.dpiY = (FLOAT)dpiY;
						}
						HRESULT hr = d2dFactory->CreateHwndRenderTarget(
							tp,
							D2D1::HwndRenderTargetProperties(
								form->GetWindowHandle(),
								D2D1::SizeU((int)size.x.value, (int)size.y.value)
							),
							&renderTarget
						);
						if (!FAILED(hr))
						{
							d2dRenderTarget = renderTarget;
							d2dRenderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
						}
					}
					else if (previousSize != size)
					{
						d2dRenderTarget->Resize(D2D1::SizeU((int)size.x.value, (int)size.y.value));
					}
					previousSize = size;
				}
			public:
				Direct2DWindowsNativeWindowListener_1_0(INativeWindow* _window, ID2D1Factory* _d2dFactory)
					:Direct2DWindowsNativeWindowListener(_window, _d2dFactory)
				{
				}

				ID2D1RenderTarget* GetDirect2DRenderTarget()override
				{
					if(!d2dRenderTarget) Moved();
					return d2dRenderTarget.Obj();
				}

				void RecreateRenderTarget()override
				{
					if (d2dRenderTarget)
					{
						d2dRenderTarget = nullptr;
					}
				}

				bool PresentRenderTarget()override
				{
					return true;
				}
			};

/***********************************************************************
WindowListener 1.1
***********************************************************************/

			class Direct2DWindowsNativeWindowListener_1_1 : public Direct2DWindowsNativeWindowListener
			{
			protected:
				ComPtr<ID2D1Factory1>			d2dFactory1;
				ID3D11Device*					d3d11Device;
				ComPtr<IDXGIDevice>				dxgiDevice;
				ComPtr<IDXGISwapChain1>			dxgiSwapChain;
				ComPtr<ID2D1DeviceContext>		d2dDeviceContext;
				NativeSize						previousSize;

				ComPtr<IDXGIDevice> GetDXGIDevice()
				{
					IDXGIDevice* device = nullptr;
					HRESULT hr = d3d11Device->QueryInterface(&device);
					if (!SUCCEEDED(hr)) return nullptr;
					return device;
				}

				ComPtr<IDXGISwapChain1> CreateSwapChain(IDXGIDevice* dxgiDevice)
				{
					ComPtr<IDXGIAdapter> dxgiAdapter;
					{
						IDXGIAdapter* adapter = nullptr;
						HRESULT hr = dxgiDevice->GetAdapter(&adapter);
						if (!SUCCEEDED(hr)) return nullptr;
						dxgiAdapter = adapter;
					}

					ComPtr<IDXGIFactory2> dxgiFactory;
					{
						IDXGIFactory2* factory = nullptr;
						HRESULT hr = dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), (void**)&factory);
						if (!SUCCEEDED(hr)) return nullptr;
						dxgiFactory = factory;
					}

					IWindowsForm* form = GetWindowsForm(window);
					ComPtr<IDXGISwapChain1> dxgiSwapChain;
					{
						DXGI_SWAP_CHAIN_DESC1 props = {};
						props.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
						props.SampleDesc.Count = 1;
						props.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
						props.BufferCount = 2;

						IDXGISwapChain1* swapChain = nullptr;
						HRESULT hr = dxgiFactory->CreateSwapChainForHwnd(d3d11Device, form->GetWindowHandle(), &props, nullptr, nullptr, &swapChain);
						if (!SUCCEEDED(hr)) return nullptr;
						dxgiSwapChain = swapChain;
					}

					return dxgiSwapChain;
				}

				ComPtr<ID2D1DeviceContext> CreateDeviceContext(IDXGIDevice* dxgiDevice)
				{
					ComPtr<ID2D1Device> d2d1Device;
					{
						ID2D1Device* device = nullptr;
						HRESULT hr = d2dFactory1->CreateDevice(dxgiDevice, &device);
						if (!SUCCEEDED(hr)) return nullptr;
						d2d1Device = device;
					}

					ComPtr<ID2D1DeviceContext> d2dDeviceContext;
					{
						ID2D1DeviceContext* deviceContext = nullptr;
						HRESULT hr = d2d1Device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &deviceContext);
						if (!SUCCEEDED(hr)) return nullptr;
						d2dDeviceContext = deviceContext;
					}

					return d2dDeviceContext;
				}

				ComPtr<ID2D1Bitmap1> CreateBitmap(IDXGISwapChain1* swapChain, ID2D1DeviceContext* deviceContext)
				{
					ComPtr<IDXGISurface> dxgiSurface;
					{
						IDXGISurface* surface = nullptr;
						HRESULT hr = swapChain->GetBuffer(0, __uuidof(IDXGISurface), (void**)&surface);
						if (!SUCCEEDED(hr))return nullptr;
						dxgiSurface = surface;
					}

					ComPtr<ID2D1Bitmap1> d2dBitmap;
					{
						auto props = D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE));

						ID2D1Bitmap1* bitmap = nullptr;
						HRESULT hr = deviceContext->CreateBitmapFromDxgiSurface(dxgiSurface.Obj(), props, &bitmap);
						if (!SUCCEEDED(hr)) return nullptr;
						d2dBitmap = bitmap;
					}

					return d2dBitmap;
				}

				void RebuildCanvas(NativeSize size)override
				{
					if (size.x <= 1) size.x = 1;
					if (size.y <= 1) size.y = 1;

					if (!d2dDeviceContext)
					{
						if (!dxgiDevice)
						{
							dxgiDevice = GetDXGIDevice();
						}

						if (!dxgiSwapChain)
						{
							dxgiSwapChain = CreateSwapChain(dxgiDevice.Obj());
						}

						d2dDeviceContext = CreateDeviceContext(dxgiDevice.Obj());
						IWindowsForm* form = GetWindowsForm(window);
						{
							UINT dpiX = 0;
							UINT dpiY = 0;
							DpiAwared_GetDpiForWindow(form->GetWindowHandle(), &dpiX, &dpiY);
							d2dDeviceContext->SetDpi((FLOAT)dpiX, (FLOAT)dpiY);
						}
						previousSize = { 0,0 };
					}

					if (previousSize != size)
					{
						previousSize = size;
						d2dDeviceContext->SetTarget(nullptr);
						HRESULT hr = dxgiSwapChain->ResizeBuffers(
							0,
							(UINT)size.x.value,
							(UINT)size.y.value,
							DXGI_FORMAT_UNKNOWN,
							0
						);
						if (SUCCEEDED(hr))
						{
							auto d2dBitmap = CreateBitmap(dxgiSwapChain.Obj(), d2dDeviceContext.Obj());
							d2dDeviceContext->SetTarget(d2dBitmap.Obj());
						}
					}
				}
			public:
				Direct2DWindowsNativeWindowListener_1_1(INativeWindow* _window, ComPtr<ID2D1Factory1> _d2dFactory1, ID3D11Device* _d3d11Device)
					:Direct2DWindowsNativeWindowListener(_window, _d2dFactory1.Obj())
					,d2dFactory1(_d2dFactory1)
					,d3d11Device(_d3d11Device)
				{
				}

				ID2D1RenderTarget* GetDirect2DRenderTarget()override
				{
					if(!d2dDeviceContext) Moved();
					return d2dDeviceContext.Obj();
				}

				void RecreateRenderTarget()override
				{
					if (d2dDeviceContext)
					{
						d2dDeviceContext = nullptr;
						dxgiSwapChain = nullptr;
					}
				}

				bool PresentRenderTarget()override
				{
					if (d2dDeviceContext)
					{
						if (dxgiSwapChain)
						{
							DXGI_PRESENT_PARAMETERS parameters = {0};
							HRESULT hr = dxgiSwapChain->Present1(1, 0, &parameters);
							return hr == S_OK || hr == DXGI_STATUS_OCCLUDED;
						}
					}
					return false;
				}
			};

/***********************************************************************
ControllerListener
***********************************************************************/

			class Direct2DWindowsNativeControllerListener : public Object, public INativeControllerListener
			{
			public:
				Dictionary<INativeWindow*, Ptr<Direct2DWindowsNativeWindowListener>>		nativeWindowListeners;
				ComPtr<ID2D1Factory>														d2dFactory;
				ComPtr<IDWriteFactory>														dwrite;
				ComPtr<ID3D11Device>														d3d11Device;

				Direct2DWindowsNativeControllerListener()
				{
					{
						D2D1_FACTORY_OPTIONS fo = {};
						#ifdef _DEBUG
						fo.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
						#endif

						ID2D1Factory* factory=0;
						HRESULT hr=D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, fo, &factory);
						if(!FAILED(hr))
						{
							d2dFactory=factory;
						}
					}
					{
						IDWriteFactory* factory=0;
						HRESULT hr=DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&factory));
						if(!FAILED(hr))
						{
							dwrite=factory;
						}
					}
				}

				~Direct2DWindowsNativeControllerListener()
				{
					// TODO: (enumerable) foreach on dictionary
					for (vint i = 0; i < nativeWindowListeners.Count(); i++)
					{
						auto window = nativeWindowListeners.Keys()[i];
						auto listener = nativeWindowListeners.Values()[i];
						window->UninstallListener(listener.Obj());
					}
				}

				ComPtr<ID3D11Device> CreateD3D11Device(D3D_DRIVER_TYPE driverType)
				{
					UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
					#ifdef _DEBUG
					flags |= D3D11_CREATE_DEVICE_DEBUG;
					#endif

					D3D_FEATURE_LEVEL featureLevels[] =
					{
						D3D_FEATURE_LEVEL_11_1,
						D3D_FEATURE_LEVEL_11_0,
						D3D_FEATURE_LEVEL_10_1,
						D3D_FEATURE_LEVEL_10_0,
						D3D_FEATURE_LEVEL_9_3,
						D3D_FEATURE_LEVEL_9_2,
						D3D_FEATURE_LEVEL_9_1
					};


					ID3D11Device* device = nullptr;
					HRESULT hr = D3D11CreateDevice(
						nullptr,
						driverType,
						nullptr,
						flags,
						featureLevels,
						sizeof(featureLevels) / sizeof(*featureLevels),
						D3D11_SDK_VERSION,
						&device,
						nullptr,
						nullptr);
					if (SUCCEEDED(hr))
					{
						return device;
					}
					else if (device)
					{
						device->Release();
					}
					return nullptr;
				}

				void NativeWindowCreated(INativeWindow* window)
				{
					ComPtr<ID2D1Factory1> d2dfactory1;
					{
						ID2D1Factory1* factory = nullptr;
						HRESULT hr = windows::GetDirect2DFactory()->QueryInterface(&factory);
						if (SUCCEEDED(hr))
						{
							d2dfactory1 = factory;
						}
					}

					Ptr<Direct2DWindowsNativeWindowListener> listener;
					if (d2dfactory1)
					{
						if (!d3d11Device)
						{
							d3d11Device = CreateD3D11Device(D3D_DRIVER_TYPE_HARDWARE);
							if (!d3d11Device)
							{
								d3d11Device = CreateD3D11Device(D3D_DRIVER_TYPE_WARP);
							}
						}
#ifdef _DEBUG
						CHECK_ERROR(d3d11Device,
							L"Direct2DWindowsNativeControllerListener::NativeWindowCreated(INativeWindow*)#"
							L"Failed to create Direct3D 11 Device. "
							L"If you are running in Debug mode on Windows 10, please ensure the optional feature [Graphics Tools] is correctly installed. "
							L"This error will be skipped in Release mode and GacUI will fallback to use Direct2D 1.0, but you still need to check your Windows SDK Installation."
							);
#endif
					}

					if (d2dfactory1 && d3d11Device)
					{
						listener = Ptr(new Direct2DWindowsNativeWindowListener_1_1(window, d2dfactory1, d3d11Device.Obj()));
					}
					else
					{
						listener = Ptr(new Direct2DWindowsNativeWindowListener_1_0(window, d2dFactory.Obj()));
					}
					window->InstallListener(listener.Obj());
					nativeWindowListeners.Add(window, listener);
				}

				void NativeWindowDestroying(INativeWindow* window)
				{
					Ptr<Direct2DWindowsNativeWindowListener> listener=nativeWindowListeners[window];
					nativeWindowListeners.Remove(window);
					window->UninstallListener(listener.Obj());
				}
			};

			Direct2DWindowsNativeControllerListener* direct2DListener = nullptr;

			ID2D1Factory* GetDirect2DFactory()
			{
				return direct2DListener->d2dFactory.Obj();
			}

			IDWriteFactory* GetDirectWriteFactory()
			{
				return direct2DListener->dwrite.Obj();
			}

			ID3D11Device* GetD3D11Device()
			{
				return direct2DListener->d3d11Device.Obj();
			}
		}

		namespace elements_windows_d2d
		{
/***********************************************************************
OS Supporting
***********************************************************************/

			class WinDirect2DApplicationDirect2DObjectProvider : public IWindowsDirect2DObjectProvider
			{
			protected:

				windows::Direct2DWindowsNativeWindowListener* GetNativeWindowListener(INativeWindow* window)
				{
					vint index = windows::direct2DListener->nativeWindowListeners.Keys().IndexOf(window);
					return index == -1
						? nullptr
						: windows::direct2DListener->nativeWindowListeners.Values().Get(index).Obj();
				}

			public:
				void RecreateRenderTarget(INativeWindow* window)override
				{
					if (auto listener = GetNativeWindowListener(window))
					{
						return listener->RecreateRenderTarget();
					}
				}

				void ResizeRenderTarget(INativeWindow* window)override
				{
					if (auto listener = GetNativeWindowListener(window))
					{
						return listener->ResizeRenderTarget();
					}
				}

				ID2D1RenderTarget* GetNativeWindowDirect2DRenderTarget(INativeWindow* window)override
				{
					if (auto listener = GetNativeWindowListener(window))
					{
						return listener->GetDirect2DRenderTarget();
					}
					return nullptr;
				}

				void StartRendering(INativeWindow* window)override
				{
					if (auto listener = GetNativeWindowListener(window))
					{
						listener->StartRendering();
						if (auto renderTarget = listener->GetDirect2DRenderTarget())
						{
							renderTarget->BeginDraw();
							renderTarget->Clear(D2D1::ColorF(D2D1::ColorF::Black));
						}
					}
				}

				RenderTargetFailure StopRenderingAndPresent(INativeWindow* window)override
				{
					if (auto listener = GetNativeWindowListener(window))
					{
						listener->StopRendering();
						bool moved = listener->RetrieveAndResetMovedWhileRendering();

						if (auto renderTarget = listener->GetDirect2DRenderTarget())
						{
							HRESULT hr = renderTarget->EndDraw();
							if (hr == S_OK)
							{
								if (moved)
								{
									return RenderTargetFailure::ResizeWhileRendering;
								}
								else if (listener->PresentRenderTarget())
								{
									return RenderTargetFailure::None;
								}
							}
						}
					}
					return RenderTargetFailure::LostDevice;
				}

				ID2D1Factory* GetDirect2DFactory()override
				{
					return vl::presentation::windows::GetDirect2DFactory();
				}

				IDWriteFactory* GetDirectWriteFactory()override
				{
					return vl::presentation::windows::GetDirectWriteFactory();
				}

				IWindowsDirect2DRenderTarget* GetBindedRenderTarget(INativeWindow* window)override
				{
					auto windowsForm = vl::presentation::windows::GetWindowsForm(window);
					auto graphicsHandler = windowsForm->GetGraphicsHandler();
					return dynamic_cast<IWindowsDirect2DRenderTarget*>(graphicsHandler);
				}

				void SetBindedRenderTarget(INativeWindow* window, IWindowsDirect2DRenderTarget* renderTarget)override
				{
					vl::presentation::windows::GetWindowsForm(window)->SetGraphicsHandler(renderTarget);
				}

				IWICImagingFactory* GetWICImagingFactory()override
				{
					return vl::presentation::windows::GetWICImagingFactory();
				}

				IWICBitmap* GetWICBitmap(INativeImageFrame* frame)override
				{
					return vl::presentation::windows::GetWICBitmap(frame);
				}
			};
		}
	}
}

using namespace vl;
using namespace vl::presentation;
using namespace vl::presentation::windows;
using namespace vl::presentation::elements_windows_d2d;

int SetupWindowsDirect2DRendererInternal(bool hosted, bool raw)
{
	InitDpiAwareness(true);
	CoInitializeEx(NULL, COINIT_MULTITHREADED);
	HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
	WinDirect2DApplicationDirect2DObjectProvider objectProvider;
	SetWindowsDirect2DObjectProvider(&objectProvider);
	EnableCrossKernelCrashing();

	// create controller
	GuiHostedController* hostedController = nullptr;
	StartWindowsNativeController(hInstance);
	auto nativeController = GetWindowsNativeController();
	if (hosted)
	{
		hostedController = new GuiHostedController(nativeController);
		SetNativeController(hostedController);
		SetHostedApplication(hostedController->GetHostedApplication());
	}
	else
	{
		SetNativeController(nativeController);
	}

	Ptr<INativeAutomationService> automationService;
	if (hosted)
	{
		automationService = Ptr(new WindowsAutomationServiceHosted);
	}
	else
	{
		automationService = Ptr(new WindowsAutomationService);
	}
	GetNativeServiceSubstitution()->Substitute(automationService.Obj(), false);

	{
		// install listener
		Direct2DWindowsNativeControllerListener listener;
		nativeController->CallbackService()->InstallListener(&listener);
		direct2DListener = &listener;
		// main
		RendererMainDirect2D(hostedController, raw);
		// uninstall listener
		direct2DListener = nullptr;
		nativeController->CallbackService()->UninstallListener(&listener);
	}

	GetNativeServiceSubstitution()->Unsubstitute(automationService.Obj());
	automationService = nullptr;

	// destroy controller
	SetNativeController(nullptr);
	if (hostedController)
	{
		SetHostedApplication(nullptr);
		delete hostedController;
	}
	StopWindowsNativeController();
	return 0;
}

int SetupWindowsDirect2DRenderer()
{
	return SetupWindowsDirect2DRendererInternal(false, false);
}

int SetupHostedWindowsDirect2DRenderer()
{
	return SetupWindowsDirect2DRendererInternal(true, false);
}

int SetupRawWindowsDirect2DRenderer()
{
	return SetupWindowsDirect2DRendererInternal(false, true);
}

/***********************************************************************
.\DIRECT2D\RENDERERS\GUIGRAPHICSLAYOUTPROVIDERWINDOWSDIRECT2D.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace elements;
		using namespace collections;

		namespace elements_windows_d2d
		{

/***********************************************************************
WindowsDirect2DElementInlineObject
***********************************************************************/

			class WindowsDirect2DElementInlineObject : public IDWriteInlineObject
			{
			public:
				class IRendererCallback : public Interface
				{
				public:
					virtual Color									GetBackgroundColor(vint textPosition) = 0;
					virtual IWindowsDirect2DRenderTarget*			GetDirect2DRenderTarget() = 0;
					virtual Point									GetParagraphOffset() = 0;
					virtual IGuiGraphicsParagraphCallback*			GetParagraphCallback() = 0;
				};

			protected:
				vint												counter;
				IGuiGraphicsParagraph::InlineObjectProperties		properties;
				IRendererCallback*									rendererCallback;
				vint												start;
				vint												length;

			public:
				WindowsDirect2DElementInlineObject(
					IRendererCallback* _rendererCallback,
					vint _start,
					vint _length
					)
					:counter(1)
					,rendererCallback(_rendererCallback)
					,start(_start)
					,length(_length)
				{
				}

				~WindowsDirect2DElementInlineObject()
				{
					if (properties.backgroundImage)
					{
						IGuiGraphicsRenderer* graphicsRenderer=properties.backgroundImage->GetRenderer();
						if(graphicsRenderer)
						{
							graphicsRenderer->SetRenderTarget(0);
						}
					}
				}

				vint GetStart()
				{
					return start;
				}

				vint GetLength()
				{
					return length;
				}

				const IGuiGraphicsParagraph::InlineObjectProperties& GetProperties()
				{
					return properties;
				}

				void SetProperties(const IGuiGraphicsParagraph::InlineObjectProperties& value)
				{
					properties=value;
				}

				Ptr<IGuiGraphicsElement> GetElement()
				{
					return properties.backgroundImage;
				}

				HRESULT STDMETHODCALLTYPE QueryInterface( 
					REFIID riid,
					void __RPC_FAR *__RPC_FAR *ppvObject
					)
				{
					if(ppvObject)
					{
						*ppvObject=NULL;
					}
					return E_NOINTERFACE;
				}

				ULONG STDMETHODCALLTYPE AddRef(void)
				{
					++counter;
					return S_OK;
				}

				ULONG STDMETHODCALLTYPE Release(void)
				{
					if(--counter==0)
					{
						delete this;
					}
					return S_OK;
				}

				STDMETHOD(Draw)(
					void* clientDrawingContext,
					IDWriteTextRenderer* renderer,
					FLOAT originX,
					FLOAT originY,
					BOOL isSideways,
					BOOL isRightToLeft,
					IUnknown* clientDrawingEffect
					)override
				{
					Rect bounds(Point((vint)originX, (vint)originY), properties.size);
					if (properties.backgroundImage)
					{
						IGuiGraphicsRenderer* graphicsRenderer=properties.backgroundImage->GetRenderer();
						if(graphicsRenderer)
						{
							graphicsRenderer->Render(bounds);
						}
					}

					Color color = properties.backgroundColor;
					if(color.a!=0)
					{
						color.a/=2;
						if(IWindowsDirect2DRenderTarget* renderTarget=rendererCallback->GetDirect2DRenderTarget())
						{
							ID2D1SolidColorBrush* brush=renderTarget->CreateDirect2DBrush(color);

							renderTarget->GetDirect2DRenderTarget()->FillRectangle(
								D2D1::RectF(bounds.x1-0.5f, bounds.y1-0.5f, bounds.x2+0.5f, bounds.y2+0.5f),
								brush
								);

							renderTarget->DestroyDirect2DBrush(color);
						}
					}

					if (properties.callbackId != -1)
					{
						if (auto callback = rendererCallback->GetParagraphCallback())
						{
							auto offset = rendererCallback->GetParagraphOffset();
							auto size = callback->OnRenderInlineObject(properties.callbackId, Rect(Point(bounds.x1 - offset.x, bounds.y1 - offset.y), bounds.GetSize()));
							properties.size = size;
						}
					}
					return S_OK;
				}

				STDMETHOD(GetMetrics)(
					DWRITE_INLINE_OBJECT_METRICS* metrics
					)override
				{
					metrics->width=(FLOAT)properties.size.x;
					metrics->height=(FLOAT)properties.size.y;
					metrics->baseline=(FLOAT)(properties.baseline==-1?properties.size.y:properties.baseline);
					metrics->supportsSideways=TRUE;
					return S_OK;
				}

				STDMETHOD(GetOverhangMetrics)(
					DWRITE_OVERHANG_METRICS* overhangs
					)override
				{
					overhangs->left=0;
					overhangs->right=0;
					overhangs->top=0;
					overhangs->bottom=0;
					return S_OK;
				}

				STDMETHOD(GetBreakConditions)(
					DWRITE_BREAK_CONDITION* breakConditionBefore,
					DWRITE_BREAK_CONDITION* breakConditionAfter
					)override
				{
					switch(properties.breakCondition)
					{
					case IGuiGraphicsParagraph::StickToPreviousRun:
						*breakConditionBefore=DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
						*breakConditionAfter=DWRITE_BREAK_CONDITION_CAN_BREAK;
						break;
					case IGuiGraphicsParagraph::StickToNextRun:
						*breakConditionBefore=DWRITE_BREAK_CONDITION_CAN_BREAK;
						*breakConditionAfter=DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
						break;
					default:
						*breakConditionBefore=DWRITE_BREAK_CONDITION_CAN_BREAK;
						*breakConditionAfter=DWRITE_BREAK_CONDITION_CAN_BREAK;
					}
					return S_OK;
				}
			};

/***********************************************************************
WindowsDirect2DParagraph
***********************************************************************/

			class WindowsDirect2DParagraph : public Object, public IGuiGraphicsParagraph, public WindowsDirect2DElementInlineObject::IRendererCallback
			{
			protected:
				struct TextRange
				{
					vint								start;
					vint								end;

					TextRange(){}
					TextRange(vint _start, vint _end):start(_start),end(_end){}

					GUI_DEFINE_COMPARE_OPERATORS(TextRange)
				};

				typedef Dictionary<IGuiGraphicsElement*, ComPtr<WindowsDirect2DElementInlineObject>>	InlineElementMap;
				typedef Dictionary<TextRange, Color>													ColorMap;
				typedef Dictionary<TextRange, IGuiGraphicsElement*>										GraphicsElementMap;
			protected:
				IGuiGraphicsLayoutProvider*				provider;
				ID2D1SolidColorBrush*					defaultTextColor;
				IDWriteFactory*							dwriteFactory;
				IWindowsDirect2DRenderTarget*			renderTarget;
				WString									paragraphText;
				ComPtr<IDWriteTextLayout>				textLayout;
				bool									wrapLine;
				vint									maxWidth;
				List<Color>								usedColors;

				InlineElementMap						inlineElements;
				GraphicsElementMap						graphicsElements;
				ColorMap								backgroundColors;

				vint									caret;
				Color									caretColor;
				bool									caretFrontSide;
				ID2D1SolidColorBrush*					caretBrush;

				bool									formatDataAvailable;
				Array<DWRITE_LINE_METRICS>				lineMetrics;
				Array<vint>								lineStarts;
				Array<FLOAT>							lineTops;
				Array<DWRITE_CLUSTER_METRICS>			clusterMetrics;
				Array<DWRITE_HIT_TEST_METRICS>			hitTestMetrics;
				Array<vint>								charHitTestMap;

				Point									paragraphOffset;
				IGuiGraphicsParagraphCallback*			paragraphCallback;

/***********************************************************************
WindowsDirect2DParagraph (Ranges)
***********************************************************************/

				template<typename T>
				void CutMap(Dictionary<TextRange, T>& map, vint start, vint length)
				{
					vint end=start+length;
					// TODO: (enumerable) foreach:alterable(reversed) on group
					for(vint i=map.Count()-1;i>=0;i--)
					{
						TextRange key=map.Keys()[i];
						if(key.start<end && start<key.end)
						{
							T value=map.Values()[i];

							vint s1=key.start;
							vint s2=key.start>start?key.start:start;
							vint s3=key.end<end?key.end:end;
							vint s4=key.end;

							map.Remove(key);
							if(s1<s2)
							{
								map.Add(TextRange(s1, s2), value);
							}
							if(s2<s3)
							{
								map.Add(TextRange(s2, s3), value);
							}
							if(s3<s4)
							{
								map.Add(TextRange(s3, s4), value);
							}
						}
					}
				}
				
				template<typename T>
				void UpdateOverlappedMap(Dictionary<TextRange, T>& map, vint start, vint length, const T& value)
				{
					vint end=start+length;
					// TODO: (enumerable) foreach:alterable(reversed)) on dictionary
					for(vint i=map.Count()-1;i>=0;i--)
					{
						TextRange key=map.Keys()[i];
						if(key.start<end && start<key.end)
						{
							map.Set(key, value);
						}
					}
				}
				
				template<typename T>
				void DefragmentMap(Dictionary<TextRange, T>& map)
				{
					vint lastIndex=map.Count()-1;
					T lastValue=map.Values()[lastIndex];
					// TODO: (enumerable) foreach:indexed(alterable(reversed)) on dictionary
					for(vint i=map.Count()-2;i>=-1;i--)
					{
						if(i==-1 || map.Values()[i]!=lastValue)
						{
							if(lastIndex-i>0)
							{
								vint start=map.Keys()[i+1].start;
								vint end=map.Keys()[lastIndex].end;
								TextRange key(start, end);

								for(vint j=lastIndex;j>i;j--)
								{
									map.Remove(map.Keys()[j]);
								}
								map.Add(key, lastValue);
							}
							lastIndex=i;
							if(i!=-1)
							{
								lastValue=map.Values()[i];
							}
						}
					}
				}
				
				template<typename T>
				void SetMap(Dictionary<TextRange, T>& map, vint start, vint length, const T& value)
				{
					CutMap(map, start, length);
					UpdateOverlappedMap(map, start, length, value);
					DefragmentMap(map);
				}

				template<typename T>
				bool GetMap(Dictionary<TextRange, T>& map, vint textPosition, T& value)
				{
					vint start=0;
					vint end=map.Count()-1;
					while(start<=end)
					{
						vint middle=(start+end)/2;
						TextRange key=map.Keys()[middle];
						if(textPosition<key.start)
						{
							end=middle-1;
						}
						else if(textPosition>=key.end)
						{
							start=middle+1;
						}
						else
						{
							value=map.Values()[middle];
							return true;
						}
					}
					return false;
				}

/***********************************************************************
WindowsDirect2DParagraph (Layout Retriving)
***********************************************************************/

				void PrepareFormatData()
				{
					if(!formatDataAvailable)
					{
						formatDataAvailable=true;
						{
							UINT32 lineCount=0;
							textLayout->GetLineMetrics(NULL, 0, &lineCount);
							lineMetrics.Resize(lineCount);
							if(lineCount>0)
							{
								textLayout->GetLineMetrics(&lineMetrics[0], lineCount, &lineCount);
							}

							lineStarts.Resize(lineCount);
							lineTops.Resize(lineCount);
							vint start=0;
							FLOAT top=0;
							// TODO: (enumerable) foreach
							for(vint i=0;i<lineMetrics.Count();i++)
							{
								DWRITE_LINE_METRICS& metrics=lineMetrics[i];

								lineStarts[i]=start;
								start+=metrics.length;

								lineTops[i]=top;
								top+=metrics.height;
							}
						}
						{
							UINT32 clusterCount=0;
							textLayout->GetClusterMetrics(NULL, 0, &clusterCount);
							clusterMetrics.Resize(clusterCount);
							if(clusterCount>0)
							{
								textLayout->GetClusterMetrics(&clusterMetrics[0], clusterCount, &clusterCount);
							}
						}
						{
							vint textPos=0;
							hitTestMetrics.Resize(clusterMetrics.Count());
							// TODO: (enumerable) foreach
							for(vint i=0;i<hitTestMetrics.Count();i++)
							{
								FLOAT x=0;
								FLOAT y=0;
								DWRITE_HIT_TEST_METRICS& metrics=hitTestMetrics[i];
								textLayout->HitTestTextPosition((UINT32)textPos, FALSE, &x, &y, &metrics);
								textPos+=metrics.length;
							}
						}
						{
							charHitTestMap.Resize(paragraphText.Length());
							// TODO: (enumerable) foreach
							for(vint i=0;i<hitTestMetrics.Count();i++)
							{
								DWRITE_HIT_TEST_METRICS& metrics=hitTestMetrics[i];
								for(UINT32 j=0;j<metrics.length;j++)
								{
									charHitTestMap[metrics.textPosition+j]=i;
								}
							}
						}
					}
				}
			public:

/***********************************************************************
WindowsDirect2DParagraph (Initialization)
***********************************************************************/

				WindowsDirect2DParagraph(IGuiGraphicsLayoutProvider* _provider, const WString& _text, IGuiGraphicsRenderTarget* _renderTarget, IGuiGraphicsParagraphCallback* _paragraphCallback)
					:provider(_provider)
					,dwriteFactory(GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory())
					,renderTarget(dynamic_cast<IWindowsDirect2DRenderTarget*>(_renderTarget))
					,paragraphText(_text)
					,textLayout(nullptr)
					,wrapLine(true)
					,maxWidth(-1)
					,caret(-1)
					,caretFrontSide(false)
					,caretBrush(0)
					,formatDataAvailable(false)
					,paragraphCallback(_paragraphCallback)
				{
					FontProperties defaultFont= windows::GetWindowsNativeController()->ResourceService()->GetDefaultFont();
					Direct2DTextFormatPackage* package=GetWindowsDirect2DResourceManager()->CreateDirect2DTextFormat(defaultFont);
					defaultTextColor=renderTarget->CreateDirect2DBrush(Color(0, 0, 0));
					usedColors.Add(Color(0, 0, 0));

					IDWriteTextLayout* rawTextLayout=0;
					HRESULT hr=dwriteFactory->CreateTextLayout(
						_text.Buffer(),
						(int)_text.Length(),
						package->textFormat.Obj(),
						0,
						0,
						&rawTextLayout);
					if(!FAILED(hr))
					{
						textLayout=rawTextLayout;
						textLayout->SetMaxWidth(65536);
						textLayout->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);
					}
					graphicsElements.Add(TextRange(0, _text.Length()), nullptr);
					backgroundColors.Add(TextRange(0, _text.Length()), Color(0, 0, 0, 0));

					GetWindowsDirect2DResourceManager()->DestroyDirect2DTextFormat(defaultFont);
				}

				~WindowsDirect2DParagraph()
				{
					DisableCaret();
					for (auto color : usedColors)
					{
						renderTarget->DestroyDirect2DBrush(color);
					}
				}

				IGuiGraphicsLayoutProvider* GetProvider()override
				{
					return provider;
				}

				IGuiGraphicsRenderTarget* GetRenderTarget()override
				{
					return renderTarget;
				}

				Point GetParagraphOffset()override
				{
					return paragraphOffset;
				}

				IGuiGraphicsParagraphCallback* GetParagraphCallback()override
				{
					return paragraphCallback;
				}

/***********************************************************************
WindowsDirect2DParagraph (Formatting)
***********************************************************************/

				bool GetWrapLine()override
				{
					return wrapLine;
				}

				void SetWrapLine(bool value)override
				{
					if (wrapLine != value)
					{
						wrapLine = value;
						textLayout->SetWordWrapping(value ? DWRITE_WORD_WRAPPING_WRAP : DWRITE_WORD_WRAPPING_NO_WRAP);
						formatDataAvailable = false;
					}
				}

				vint GetMaxWidth()override
				{
					return maxWidth;
				}

				void SetMaxWidth(vint value)override
				{
					if (maxWidth != value)
					{
						maxWidth = value;
						textLayout->SetMaxWidth(value == -1 ? 65536 : (FLOAT)value);
						formatDataAvailable = false;
					}
				}

				Alignment GetParagraphAlignment()override
				{
					switch(textLayout->GetTextAlignment())
					{
					case DWRITE_TEXT_ALIGNMENT_LEADING:
						return Alignment::Left;
					case DWRITE_TEXT_ALIGNMENT_CENTER:
						return Alignment::Center;
					case DWRITE_TEXT_ALIGNMENT_TRAILING:
						return Alignment::Right;
					default:
						return Alignment::Left;
					}
				}

				void SetParagraphAlignment(Alignment value)override
				{
					if (GetParagraphAlignment() != value)
					{
						formatDataAvailable = false;
						switch (value)
						{
						case Alignment::Left:
							textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
							break;
						case Alignment::Center:
							textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
							break;
						case Alignment::Right:
							textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING);
							break;
						}
					}
				}

				bool SetFont(vint start, vint length, const WString& value)override
				{
					if(length==0) return true;
					formatDataAvailable=false;

					DWRITE_TEXT_RANGE range;
					range.startPosition=(int)start;
					range.length=(int)length;
					HRESULT hr=textLayout->SetFontFamilyName(value.Buffer(), range);
					return !FAILED(hr);
				}

				bool SetSize(vint start, vint length, vint value)override
				{
					if(length==0) return true;
					formatDataAvailable=false;

					DWRITE_TEXT_RANGE range;
					range.startPosition=(int)start;
					range.length=(int)length;
					HRESULT hr=textLayout->SetFontSize((FLOAT)value, range);
					return !FAILED(hr);
				}

				bool SetStyle(vint start, vint length, TextStyle value)override
				{
					if(length==0) return true;
					formatDataAvailable=false;

					DWRITE_TEXT_RANGE range;
					range.startPosition=(int)start;
					range.length=(int)length;
					HRESULT hr=S_OK;

					hr=textLayout->SetFontStyle(value&Italic?DWRITE_FONT_STYLE_ITALIC:DWRITE_FONT_STYLE_NORMAL, range);
					if(FAILED(hr)) return false;
					hr=textLayout->SetFontWeight(value&Bold?DWRITE_FONT_WEIGHT_BOLD:DWRITE_FONT_WEIGHT_NORMAL, range);
					if(FAILED(hr)) return false;
					hr=textLayout->SetUnderline(value&Underline?TRUE:FALSE, range);
					if(FAILED(hr)) return false;
					hr=textLayout->SetStrikethrough(value&Strikeline?TRUE:FALSE, range);
					if(FAILED(hr)) return false;

					return true;
				}

				bool SetColor(vint start, vint length, Color value)override
				{
					if(length==0) return true;
					formatDataAvailable=false;

					ID2D1SolidColorBrush* brush=renderTarget->CreateDirect2DBrush(value);
					usedColors.Add(value);

					DWRITE_TEXT_RANGE range;
					range.startPosition=(int)start;
					range.length=(int)length;
					HRESULT hr=textLayout->SetDrawingEffect(brush, range);
					return !FAILED(hr);
				}

				bool SetBackgroundColor(vint start, vint length, Color value)override
				{
					SetMap(backgroundColors, start, length, value);
					return true;
				}

				bool SetInlineObject(vint start, vint length, const InlineObjectProperties& properties)override
				{
					vint reuseIndex = -1;
					// TODO: (enumerable) foreach
					for (vint i = 0; i < inlineElements.Count(); i++)
					{
						auto inlineObject = inlineElements.Values().Get(i);
						if (start == inlineObject->GetStart() && length == inlineObject->GetLength())
						{
							auto&& inlineProps = inlineObject->GetProperties();
							if (inlineProps.callbackId != properties.callbackId) return false;
							if (inlineProps.backgroundImage != properties.backgroundImage) return false;
							reuseIndex = i;
						}
						else
						{
							if (inlineElements.Keys().Contains(properties.backgroundImage.Obj()))
							{
								return false;
							}
							if (start < inlineObject->GetStart() + inlineObject->GetLength() && inlineObject->GetStart() < start + length)
							{
								return false;
							}
						}
					}
					formatDataAvailable = false;

					auto inlineObject = reuseIndex != -1 ? inlineElements.Values().Get(reuseIndex) : ComPtr(new WindowsDirect2DElementInlineObject(this, start, length));
					inlineObject->SetProperties(properties);
					DWRITE_TEXT_RANGE range;
					range.startPosition = (int)start;
					range.length = (int)length;
					HRESULT hr = textLayout->SetInlineObject(inlineObject.Obj(), range);
					if (!FAILED(hr))
					{
						if (properties.backgroundImage && reuseIndex == -1)
						{
							IGuiGraphicsRenderer* renderer = properties.backgroundImage->GetRenderer();
							if (renderer)
							{
								renderer->SetRenderTarget(renderTarget);
							}
							inlineElements.Add(properties.backgroundImage.Obj(), inlineObject);
							SetMap(graphicsElements, start, length, properties.backgroundImage.Obj());
						}
						return true;
					}
					else
					{
						return false;
					}
				}

				bool ResetInlineObject(vint start, vint length)override
				{
					IGuiGraphicsElement* element=0;
					if(GetMap(graphicsElements, start, element) && element)
					{
						ComPtr<WindowsDirect2DElementInlineObject> inlineObject=inlineElements[element];
						DWRITE_TEXT_RANGE range;
						range.startPosition=(int)inlineObject->GetStart();
						range.length=(int)inlineObject->GetLength();
						HRESULT hr=textLayout->SetInlineObject(NULL, range);
						if(!FAILED(hr))
						{
							formatDataAvailable=false;
							inlineElements.Remove(element);
							SetMap(graphicsElements, inlineObject->GetStart(), inlineObject->GetLength(), (IGuiGraphicsElement*)0);
							return true;
						}
						else
						{
							return false;
						}
					}
					return false;
				}

				Size GetSize()override
				{
					DWRITE_TEXT_METRICS metrics;
					textLayout->GetMetrics(&metrics);
					return Size(
						(wrapLine ? 0 : (vint)ceil(metrics.widthIncludingTrailingWhitespace)),
						(vint)ceil(metrics.height));
				}

/***********************************************************************
WindowsDirect2DParagraph (IRenderingCallback)
***********************************************************************/

				Color GetBackgroundColor(vint textPosition)override
				{
					Color color;
					if(GetMap(backgroundColors, textPosition, color))
					{
						return color;
					}
					else
					{
						return Color(0, 0, 0, 0);
					}
				}

				IWindowsDirect2DRenderTarget* GetDirect2DRenderTarget()override
				{
					return renderTarget;
				}

/***********************************************************************
WindowsDirect2DParagraph (Rendering)
***********************************************************************/

				bool EnableCaret(vint _caret, Color _color, bool _frontSide)override
				{
					if (!IsValidCaret(_caret)) return false;
					if (caret != -1) DisableCaret();
					caret = _caret;
					caretColor = _color;
					caretFrontSide = _frontSide;
					caretBrush = renderTarget->CreateDirect2DBrush(caretColor);
					return true;
				}

				void DisableCaret()override
				{
					if (caret == -1) return;
					caret = -1;
					renderTarget->DestroyDirect2DBrush(caretColor);
					caretBrush = nullptr;
				}

				bool BlinkCaret()override
				{
					if (caret == -1) return false;
					if (caretBrush)
					{
						renderTarget->DestroyDirect2DBrush(caretColor);
						caretBrush = nullptr;
					}
					else
					{
						caretBrush = renderTarget->CreateDirect2DBrush(caretColor);
					}
					return true;
				}

				struct BackgroundRenderer
				{
					IWindowsDirect2DRenderTarget* renderTarget = nullptr;
					ID2D1SolidColorBrush* lastBackgroundBrush = nullptr;
					FLOAT lbx1, lby1, lbx2, lby2;

					void RenderBackground()
					{
						if (lastBackgroundBrush)
						{
							renderTarget->GetDirect2DRenderTarget()->FillRectangle(
								D2D1::RectF(lbx1, lby1, lbx2, lby2),
								lastBackgroundBrush
							);
							lastBackgroundBrush = nullptr;
						}
					}

					void SubmitBackground(ID2D1SolidColorBrush* brush, FLOAT x1, FLOAT y1, FLOAT x2, FLOAT y2)
					{
						if (lastBackgroundBrush)
						{
							if (lastBackgroundBrush != brush)
							{
								RenderBackground();
							}
							else
							{
								FLOAT yc = (y1 + y2) / 2;
								if (yc<lby1 || yc>lby2)
								{
									RenderBackground();
								}
							}
						}

						if (lastBackgroundBrush)
						{
							if (lbx1 > x1) lbx1 = x1;
							if (lby1 > y1) lby1 = y1;
							if (lbx2 < x2) lbx2 = x2;
							if (lby2 < y2) lby2 = y2;
						}
						else
						{
							lastBackgroundBrush = brush;
							lbx1 = x1;
							lby1 = y1;
							lbx2 = x2;
							lby2 = y2;
						}
					}
				};

				void Render(Rect bounds)override
				{
					paragraphOffset = bounds.LeftTop();
					PrepareFormatData();
					{
						BackgroundRenderer backgroundRenderer;
						backgroundRenderer.renderTarget = renderTarget;

						// TODO: (enumerable) foreach on dictionary
						for (vint i = 0; i < backgroundColors.Count(); i++)
						{
							TextRange key = backgroundColors.Keys()[i];
							Color color = backgroundColors.Values()[i];
							if (color.a > 0)
							{
								ID2D1SolidColorBrush* brush = renderTarget->CreateDirect2DBrush(color);

								vint start = key.start;
								if (start < 0)
								{
									start = 0;
								}

								while (start < charHitTestMap.Count() && start < key.end)
								{
									vint index = charHitTestMap[start];
									DWRITE_HIT_TEST_METRICS& hitTest = hitTestMetrics[index];

									FLOAT x1 = hitTest.left + (FLOAT)bounds.x1;
									FLOAT y1 = hitTest.top + (FLOAT)bounds.y1;
									FLOAT x2 = x1 + hitTest.width;
									FLOAT y2 = y1 + hitTest.height;

									x1 -= 0.5f;
									y1 -= 0.0f;
									x2 += 0.5f;
									y2 += 0.5f;

									backgroundRenderer.SubmitBackground(brush, x1, y1, x2, y2);
									start = hitTest.textPosition + hitTest.length;
								}

								renderTarget->DestroyDirect2DBrush(color);
							}
						}

						backgroundRenderer.RenderBackground();
					}

					renderTarget->GetDirect2DRenderTarget()->DrawTextLayout(
						D2D1::Point2F((FLOAT)bounds.Left(), (FLOAT)bounds.Top()),
						textLayout.Obj(),
						defaultTextColor,
						D2D1_DRAW_TEXT_OPTIONS_NO_SNAP);

					if (caret != -1 && caretBrush)
					{
						Rect caretBounds = GetCaretBounds(caret, caretFrontSide);
						vint x = caretBounds.x1 + bounds.x1;
						vint y1 = caretBounds.y1 + bounds.y1;
						vint y2 = y1 + caretBounds.Height();

						renderTarget->GetDirect2DRenderTarget()->DrawLine(
							D2D1::Point2F((FLOAT)x - 0.5f, (FLOAT)y1 + 0.5f),
							D2D1::Point2F((FLOAT)x - 0.5f, (FLOAT)y2 + 0.5f),
							caretBrush
						);
						renderTarget->GetDirect2DRenderTarget()->DrawLine(
							D2D1::Point2F((FLOAT)x + 0.5f, (FLOAT)y1 + 0.5f),
							D2D1::Point2F((FLOAT)x + 0.5f, (FLOAT)y2 + 0.5f),
							caretBrush
						);
					}
				}

/***********************************************************************
WindowsDirect2DParagraph (Caret Helper)
***********************************************************************/

				void GetLineIndexFromTextPos(vint textPos, vint& frontLineIndex, vint& backLineIndex)
				{
					frontLineIndex=-1;
					backLineIndex=-1;
					vint start=0;
					vint end=lineMetrics.Count()-1;
					while(start<=end)
					{
						vint middle=(start+end)/2;
						DWRITE_LINE_METRICS& metrics=lineMetrics[middle];
						vint lineStart=lineStarts[middle];
						vint lineEnd=lineStart+metrics.length-metrics.newlineLength;

						if(textPos<lineStart)
						{
							end=middle-1;
						}
						else if(textPos>lineEnd)
						{
							start=middle+1;
						}
						else if(textPos==lineStart && middle!=0)
						{
							DWRITE_LINE_METRICS& anotherLine=lineMetrics[middle-1];
							frontLineIndex=anotherLine.newlineLength==0?middle-1:middle;
							backLineIndex=middle;
							return;
						}
						else if(textPos==lineEnd && middle!=lineMetrics.Count()-1)
						{
							frontLineIndex=middle;
							backLineIndex=metrics.newlineLength==0?middle+1:middle;
							return;
						}
						else
						{
							frontLineIndex=middle;
							backLineIndex=middle;
							return;
						}
					}
				}

				Pair<FLOAT, FLOAT> GetLineYRange(vint lineIndex)
				{
					DWRITE_LINE_METRICS& line=lineMetrics[lineIndex];
					FLOAT top=lineTops[lineIndex];
					return Pair<FLOAT, FLOAT>(top, top+line.height);
				}

				vint GetLineIndexFromY(vint y)
				{
					if(paragraphText.Length()==0) return 0;
					FLOAT minY=0;
					FLOAT maxY=0;
					{
						minY=hitTestMetrics[0].top;
						DWRITE_HIT_TEST_METRICS& hitTest=hitTestMetrics[hitTestMetrics.Count()-1];
						maxY=hitTest.top+hitTest.height;
					}

					if(y<minY)
					{
						return 0;
					}
					else if(y>=maxY)
					{
						return lineMetrics.Count()-1;
					}

					vint start=0;
					vint end=lineMetrics.Count()-1;
					while(start<=end)
					{
						vint middle=(start+end)/2;
						Pair<FLOAT, FLOAT> yRange=GetLineYRange(middle);
						minY=yRange.key;
						maxY=yRange.value;

						if(y<minY)
						{
							end=middle-1;
						}
						else if(y>=maxY)
						{
							start=middle+1;
						}
						else
						{
							return middle;
						}
					}
					return -1;
				}

				vint GetCaretFromXWithLine(vint x, vint lineIndex)
				{
					DWRITE_LINE_METRICS& line=lineMetrics[lineIndex];
					vint lineStart=lineStarts[lineIndex];
					vint lineEnd=lineStart+line.length-line.newlineLength;

					FLOAT minLineX=0;
					FLOAT maxLineX=0;

					for(vint i=lineStart;i<lineEnd;)
					{
						vint index=charHitTestMap[i];
						DWRITE_HIT_TEST_METRICS& hitTest=hitTestMetrics[index];
						FLOAT minX=hitTest.left;
						FLOAT maxX=minX+hitTest.width;

						if(minLineX>minX) minLineX=minX;
						if(maxLineX<maxX) maxLineX=maxX;

						if(minX<=x && x<maxX)
						{
							DWRITE_CLUSTER_METRICS& cluster=clusterMetrics[index];
							FLOAT d1=x-minX;
							FLOAT d2=maxX-x;
							if(d1<=d2)
							{
								return cluster.isRightToLeft?i+hitTest.length:i;
							}
							else
							{
								return cluster.isRightToLeft?i:i+hitTest.length;
							}
						}
						i+=hitTest.length;
					}
					
					if(x<minLineX) return lineStart;
					if(x>=maxLineX) return lineEnd;
					return lineStart;
				}

/***********************************************************************
WindowsDirect2DParagraph (Caret)
***********************************************************************/

				vint GetCaret(vint comparingCaret, CaretRelativePosition position, bool& preferFrontSide)override
				{
					PrepareFormatData();
					if(position==CaretFirst) return 0;
					if(position==CaretLast) return paragraphText.Length();
					if(!IsValidCaret(comparingCaret)) return -1;

					vint frontLineIndex=-1;
					vint backLineIndex=-1;
					GetLineIndexFromTextPos(comparingCaret, frontLineIndex, backLineIndex);
					vint lineIndex=preferFrontSide?frontLineIndex:backLineIndex;
					DWRITE_LINE_METRICS& line=lineMetrics[lineIndex];
					vint lineStart=lineStarts[lineIndex];
					vint lineEnd=lineStart+line.length-line.newlineLength;

					switch(position)
					{
					case CaretLineFirst:
						return lineStarts[lineIndex];
					case CaretLineLast:
						return lineStarts[lineIndex]+line.length-line.newlineLength;
					case CaretMoveLeft:
						{
							if(comparingCaret==0)
							{
								return 0;
							}
							else if(comparingCaret==lineStart)
							{
								vint offset=lineMetrics[lineIndex-1].newlineLength;
								if(offset>0)
								{
									return lineStart-offset;
								}
							}
							return hitTestMetrics[charHitTestMap[comparingCaret-1]].textPosition;
						}
					case CaretMoveRight:
						{
							if(comparingCaret==paragraphText.Length())
							{
								return paragraphText.Length();
							}
							else if(comparingCaret==lineEnd && line.newlineLength!=0)
							{
								return lineEnd+line.newlineLength;
							}
							else
							{
								vint index=charHitTestMap[comparingCaret];
								if(index==hitTestMetrics.Count()-1) return paragraphText.Length();
								return hitTestMetrics[index+1].textPosition;
							}
						}
					case CaretMoveUp:
						{
							if(lineIndex==0)
							{
								return comparingCaret;
							}
							else
							{
								Rect bounds=GetCaretBounds(comparingCaret, preferFrontSide);
								preferFrontSide=true;
								return GetCaretFromXWithLine(bounds.x1, lineIndex-1);
							}
						}
					case CaretMoveDown:
						{
							if(lineIndex==lineMetrics.Count()-1)
							{
								return comparingCaret;
							}
							else
							{
								Rect bounds=GetCaretBounds(comparingCaret, preferFrontSide);
								preferFrontSide=false;
								return GetCaretFromXWithLine(bounds.x1, lineIndex+1);
							}
						}
					}
					return -1;
				}

				Rect GetCaretBounds(vint caret, bool frontSide)override
				{
					PrepareFormatData();
					if(!IsValidCaret(caret)) return Rect();
					if(paragraphText.Length()==0) return Rect(Point(0, 0), Size(0, GetSize().y));

					vint frontLineIndex=-1;
					vint backLineIndex=-1;
					GetLineIndexFromTextPos(caret, frontLineIndex, backLineIndex);
					vint lineIndex=frontSide?frontLineIndex:backLineIndex;

					Pair<FLOAT, FLOAT> lineYRange=GetLineYRange(lineIndex);
					DWRITE_LINE_METRICS& line=lineMetrics[lineIndex];
					if(line.length-line.newlineLength==0)
					{
						return Rect(0, (vint)lineYRange.key, 0, (vint)lineYRange.value);
					}
					else
					{
						vint lineStart=lineStarts[lineIndex];
						vint lineEnd=lineStart+line.length-line.newlineLength;
						if(caret==lineStart)
						{
							frontSide=false;
						}
						else if(caret==lineEnd)
						{
							frontSide=true;
						}
						if(frontSide)
						{
							caret--;
						}

						vint index=charHitTestMap[caret];
						DWRITE_HIT_TEST_METRICS& hitTest=hitTestMetrics[index];
						DWRITE_CLUSTER_METRICS& cluster=clusterMetrics[index];
						if(cluster.isRightToLeft)
						{
							frontSide=!frontSide;
						}

						if(frontSide)
						{
							return Rect(
								Point((vint)(hitTest.left+hitTest.width), (vint)hitTest.top),
								Size(0, (vint)hitTest.height)
								);
						}
						else
						{
							return Rect(
								Point((vint)hitTest.left, (vint)hitTest.top),
								Size(0, (vint)hitTest.height)
								);
						}
					}
				}

				vint GetCaretFromPoint(Point point)override
				{
					PrepareFormatData();
					vint lineIndex=GetLineIndexFromY(point.y);
					vint caret=GetCaretFromXWithLine(point.x, lineIndex);
					return caret;
				}

				Nullable<InlineObjectProperties> GetInlineObjectFromPoint(Point point, vint& start, vint& length)override
				{
					DWRITE_HIT_TEST_METRICS metrics={0};
					BOOL trailingHit=FALSE;
					BOOL inside=FALSE;
					start=-1;
					length=0;
					HRESULT hr=textLayout->HitTestPoint((FLOAT)point.x, (FLOAT)point.y, &trailingHit, &inside, &metrics);
					if(hr==S_OK)
					{
						IGuiGraphicsElement* element=0;
						if(GetMap(graphicsElements, metrics.textPosition, element) && element)
						{
							ComPtr<WindowsDirect2DElementInlineObject> inlineObject=inlineElements[element];
							start=inlineObject->GetStart();
							length=inlineObject->GetLength();
							return inlineObject->GetProperties();
						}
					}
					return Nullable<InlineObjectProperties>();
				}

				vint GetNearestCaretFromTextPos(vint textPos, bool frontSide)override
				{
					PrepareFormatData();
					if(!IsValidTextPos(textPos)) return -1;
					if(textPos==0 || textPos==paragraphText.Length()) return textPos;
				
					vint index=charHitTestMap[textPos];
					DWRITE_HIT_TEST_METRICS& hitTest=hitTestMetrics[index];
					if(hitTest.textPosition==textPos)
					{
						return textPos;
					}
					else if(frontSide)
					{
						return hitTest.textPosition;
					}
					else
					{
						return hitTest.textPosition+hitTest.length;
					}
				}

				bool IsValidCaret(vint caret)override
				{
					PrepareFormatData();
					if(!IsValidTextPos(caret)) return false;
					if(caret==0 || caret==paragraphText.Length()) return true;
					if(hitTestMetrics[charHitTestMap[caret]].textPosition==caret) return true;

					vint frontLineIndex=-1;
					vint backLineIndex=-1;
					GetLineIndexFromTextPos(caret, frontLineIndex, backLineIndex);
					if(frontLineIndex==-1 && backLineIndex==-1) return false;
					return false;
				}

				bool IsValidTextPos(vint textPos)
				{
					return 0<=textPos && textPos<=paragraphText.Length();
				}
			};

/***********************************************************************
WindowsDirect2DLayoutProvider
***********************************************************************/

			Ptr<IGuiGraphicsParagraph> WindowsDirect2DLayoutProvider::CreateParagraph(const WString& text, IGuiGraphicsRenderTarget* renderTarget, elements::IGuiGraphicsParagraphCallback* callback)
			{
				return Ptr(new WindowsDirect2DParagraph(this, text, renderTarget, callback));
			}
		}
	}
}

/***********************************************************************
.\DIRECT2D\RENDERERS\GUIGRAPHICSRENDERERSWINDOWSDIRECT2D.CPP
***********************************************************************/
#include <math.h>

namespace vl
{
	namespace presentation
	{
		namespace elements_windows_d2d
		{
			using namespace collections;

/***********************************************************************
GuiDirect2DElementRendererBase
***********************************************************************/

			template<typename TElement, typename TRenderer>
			void GuiDirect2DElementRendererBase<TElement, TRenderer>::InitializeInternal()
			{
			}

			template<typename TElement, typename TRenderer>
			void GuiDirect2DElementRendererBase<TElement, TRenderer>::FinalizeInternal()
			{
				static_cast<TRenderer*>(this)->DestroyBrush(this->renderTarget);
			}

			template<typename TElement, typename TRenderer>
			void GuiDirect2DElementRendererBase<TElement, TRenderer>::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
			{
				static_cast<TRenderer*>(this)->DestroyBrush(oldRenderTarget);
				static_cast<TRenderer*>(this)->CreateBrush(newRenderTarget);
			}

/***********************************************************************
GuiSolidBrushElementRendererBase
***********************************************************************/

			template<typename TElement, typename TRenderer, typename TBrush>
			void GuiSolidBrushElementRendererBase<TElement, TRenderer, TBrush>::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)
			{
				if (_renderTarget)
				{
					oldColor = this->element->GetColor(); 
					brush = _renderTarget->CreateDirect2DBrush(oldColor); 
				}
			}

			template<typename TElement, typename TRenderer, typename TBrush>
			void GuiSolidBrushElementRendererBase<TElement, TRenderer, TBrush>::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)
			{
				if (_renderTarget && brush)
				{
					_renderTarget->DestroyDirect2DBrush(oldColor);
					brush = 0;
				}
			}

			template<typename TElement, typename TRenderer, typename TBrush>
			void GuiSolidBrushElementRendererBase<TElement, TRenderer, TBrush>::OnElementStateChanged()
			{
				if (this->renderTarget)
				{
					Color color = this->element->GetColor();
					if (oldColor != color)
					{
						DestroyBrush(this->renderTarget);
						CreateBrush(this->renderTarget);
					}
				}
			}

/***********************************************************************
GuiGradientBrushElementRendererBase
***********************************************************************/

			template<typename TElement, typename TRenderer, typename TBrush>
			void GuiGradientBrushElementRendererBase<TElement, TRenderer, TBrush>::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)
			{
				if (_renderTarget)
				{
					oldColor = Pair<Color, Color>(this->element->GetColor1(), this->element->GetColor2());
					brush = _renderTarget->CreateDirect2DLinearBrush(oldColor.key, oldColor.value);
				}
			}

			template<typename TElement, typename TRenderer, typename TBrush>
			void GuiGradientBrushElementRendererBase<TElement, TRenderer, TBrush>::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)
			{
				if (_renderTarget && brush)
				{
					_renderTarget->DestroyDirect2DLinearBrush(oldColor.key, oldColor.value);
					brush = 0;
				}
			}

			template<typename TElement, typename TRenderer, typename TBrush>
			void GuiGradientBrushElementRendererBase<TElement, TRenderer, TBrush>::OnElementStateChanged()
			{
				if (this->renderTarget)
				{
					auto color = Pair(this->element->GetColor1(), this->element->GetColor2());
					if (oldColor != color)
					{
						DestroyBrush(this->renderTarget);
						CreateBrush(this->renderTarget);
					}
				}
			}

/***********************************************************************
GuiSolidBorderElementRenderer
***********************************************************************/

			void GuiFocusRectangleElementRenderer::InitializeInternal()
			{
			}

			void GuiFocusRectangleElementRenderer::FinalizeInternal()
			{
				focusRectangleEffect = nullptr;
			}

			void GuiFocusRectangleElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
			{
				focusRectangleEffect = nullptr;
				if (newRenderTarget)
				{
					focusRectangleEffect = newRenderTarget->GetFocusRectangleEffect();
				}
			}

			void GuiFocusRectangleElementRenderer::Render(Rect bounds)
			{
				if (focusRectangleEffect)
				{
					ID2D1RenderTarget* d2dRenderTarget = renderTarget->GetDirect2DRenderTarget();
					ID2D1DeviceContext* d2dDeviceContext = nullptr;

					HRESULT hr = d2dRenderTarget->QueryInterface(&d2dDeviceContext);
					if (SUCCEEDED(hr))
					{
						FLOAT x = (FLOAT)bounds.Left();
						FLOAT y = (FLOAT)bounds.Top();
						FLOAT x2 = (FLOAT)bounds.Right() - 1;
						FLOAT y2 = (FLOAT)bounds.Bottom() - 1;
						FLOAT w = (FLOAT)bounds.Width();
						FLOAT h = (FLOAT)bounds.Height();

						d2dDeviceContext->DrawImage(focusRectangleEffect, D2D1::Point2F(x, y), D2D1::RectF(0, 0, w, 1), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_MASK_INVERT);
						d2dDeviceContext->DrawImage(focusRectangleEffect, D2D1::Point2F(x, y2), D2D1::RectF(0, y2 - y, w, h), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_MASK_INVERT);
						d2dDeviceContext->DrawImage(focusRectangleEffect, D2D1::Point2F(x, y + 1), D2D1::RectF(0, 1, 1, h - 1), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_MASK_INVERT);
						d2dDeviceContext->DrawImage(focusRectangleEffect, D2D1::Point2F(x2, y + 1), D2D1::RectF(x2 - x, 1, w, h - 1), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_MASK_INVERT);
					}

					if (d2dDeviceContext)
					{
						d2dDeviceContext->Release();
					}
				}
			}

			void GuiFocusRectangleElementRenderer::OnElementStateChanged()
			{
			}

/***********************************************************************
GuiSolidBorderElementRenderer
***********************************************************************/

			GuiSolidBorderElementRenderer::GuiSolidBorderElementRenderer()
			{
			}

			void GuiSolidBorderElementRenderer::Render(Rect bounds)
			{
				ID2D1RenderTarget* d2dRenderTarget = renderTarget->GetDirect2DRenderTarget();
				auto shape = element->GetShape();

				switch (shape.shapeType)
				{
				case ElementShapeType::Rectangle:
					d2dRenderTarget->DrawRectangle(
						D2D1::RectF((FLOAT)bounds.x1 + 0.5f, (FLOAT)bounds.y1 + 0.5f, (FLOAT)bounds.x2 - 0.5f, (FLOAT)bounds.y2 - 0.5f),
						brush
						);
					break;
				case ElementShapeType::Ellipse:
					d2dRenderTarget->DrawEllipse(
						D2D1::Ellipse(D2D1::Point2F((bounds.x1 + bounds.x2) / 2.0f, (bounds.y1 + bounds.y2) / 2.0f), bounds.Width() / 2.0f, bounds.Height() / 2.0f),
						brush
						);
					break;
				case ElementShapeType::RoundRect:
					d2dRenderTarget->DrawRoundedRectangle(
						D2D1::RoundedRect(
							D2D1::RectF((FLOAT)bounds.x1 + 0.5f, (FLOAT)bounds.y1 + 0.5f, (FLOAT)bounds.x2 - 0.5f, (FLOAT)bounds.y2 - 0.5f),
							(FLOAT)shape.radiusX,
							(FLOAT)shape.radiusY
							),
						brush
						);
					break;
				}
			}

/***********************************************************************
Gui3DBorderElementRenderer
***********************************************************************/

			void Gui3DBorderElementRenderer::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)
			{
				if(_renderTarget)
				{
					oldColor1=element->GetColor1();
					oldColor2=element->GetColor2();
					brush1=_renderTarget->CreateDirect2DBrush(oldColor1);
					brush2=_renderTarget->CreateDirect2DBrush(oldColor2);
				}
			}

			void Gui3DBorderElementRenderer::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)
			{
				if(_renderTarget)
				{
					if(brush1)
					{
						_renderTarget->DestroyDirect2DBrush(oldColor1);
						brush1 = nullptr;
					}
					if(brush2)
					{
						_renderTarget->DestroyDirect2DBrush(oldColor2);
						brush2 = nullptr;
					}
				}
			}

			void Gui3DBorderElementRenderer::InitializeInternal()
			{
			}

			void Gui3DBorderElementRenderer::FinalizeInternal()
			{
				DestroyBrush(renderTarget);
			}

			void Gui3DBorderElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
			{
				DestroyBrush(oldRenderTarget);
				CreateBrush(newRenderTarget);
			}

			Gui3DBorderElementRenderer::Gui3DBorderElementRenderer()
			{
			}

			void Gui3DBorderElementRenderer::Render(Rect bounds)
			{
				D2D1_RECT_F rect=D2D1::RectF((FLOAT)bounds.x1+0.5f, (FLOAT)bounds.y1+0.5f, (FLOAT)bounds.x2-0.5f, (FLOAT)bounds.y2-0.5f);
				ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();

				d2dRenderTarget->DrawLine(D2D1::Point2F(rect.left, rect.top), D2D1::Point2F(rect.right, rect.top), brush1);
				d2dRenderTarget->DrawLine(D2D1::Point2F(rect.left, rect.top), D2D1::Point2F(rect.left, rect.bottom), brush1);
				d2dRenderTarget->DrawLine(D2D1::Point2F(rect.right, rect.bottom), D2D1::Point2F(rect.left, rect.bottom), brush2);
				d2dRenderTarget->DrawLine(D2D1::Point2F(rect.right, rect.bottom), D2D1::Point2F(rect.right, rect.top), brush2);
			}

			void Gui3DBorderElementRenderer::OnElementStateChanged()
			{
				if(renderTarget)
				{
					Color color1=element->GetColor1();
					Color color2=element->GetColor2();
					if(oldColor1!=color1 || oldColor2!=color2)
					{
						DestroyBrush(renderTarget);
						CreateBrush(renderTarget);
					}
				}
			}

/***********************************************************************
Gui3DSplitterElementRenderer
***********************************************************************/

			void Gui3DSplitterElementRenderer::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)
			{
				if(_renderTarget)
				{
					oldColor1=element->GetColor1();
					oldColor2=element->GetColor2();
					brush1=_renderTarget->CreateDirect2DBrush(oldColor1);
					brush2=_renderTarget->CreateDirect2DBrush(oldColor2);
				}
			}

			void Gui3DSplitterElementRenderer::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)
			{
				if(_renderTarget)
				{
					if(brush1)
					{
						_renderTarget->DestroyDirect2DBrush(oldColor1);
						brush1 = nullptr;
					}
					if(brush2)
					{
						_renderTarget->DestroyDirect2DBrush(oldColor2);
						brush2 = nullptr;
					}
				}
			}

			void Gui3DSplitterElementRenderer::InitializeInternal()
			{
			}

			void Gui3DSplitterElementRenderer::FinalizeInternal()
			{
				DestroyBrush(renderTarget);
			}

			void Gui3DSplitterElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
			{
				DestroyBrush(oldRenderTarget);
				CreateBrush(newRenderTarget);
			}

			Gui3DSplitterElementRenderer::Gui3DSplitterElementRenderer()
			{
			}

			void Gui3DSplitterElementRenderer::Render(Rect bounds)
			{
				D2D1_POINT_2F p11, p12, p21, p22;
				switch(element->GetDirection())
				{
				case Gui3DSplitterElement::Horizontal:
					{
						vint y=bounds.y1+bounds.Height()/2-1;
						p11=D2D1::Point2F((FLOAT)bounds.x1, (FLOAT)y+0.5f);
						p12=D2D1::Point2F((FLOAT)bounds.x2, (FLOAT)y+0.5f);
						p21=D2D1::Point2F((FLOAT)bounds.x1, (FLOAT)y+1.5f);
						p22=D2D1::Point2F((FLOAT)bounds.x2, (FLOAT)y+1.5f);
					}
					break;
				case Gui3DSplitterElement::Vertical:
					{
						vint x=bounds.x1+bounds.Width()/2-1;
						p11=D2D1::Point2F((FLOAT)x+0.5f, (FLOAT)bounds.y1-0.0f);
						p12=D2D1::Point2F((FLOAT)x+0.5f, (FLOAT)bounds.y2+0.0f);
						p21=D2D1::Point2F((FLOAT)x+1.5f, (FLOAT)bounds.y1-0.0f);
						p22=D2D1::Point2F((FLOAT)x+1.5f, (FLOAT)bounds.y2+0.0f);
					}
					break;
				}
				ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();

				d2dRenderTarget->DrawLine(p11, p12, brush1);
				d2dRenderTarget->DrawLine(p21, p22, brush2);
			}

			void Gui3DSplitterElementRenderer::OnElementStateChanged()
			{
				if(renderTarget)
				{
					Color color1=element->GetColor1();
					Color color2=element->GetColor2();
					if(oldColor1!=color1 || oldColor2!=color2)
					{
						DestroyBrush(renderTarget);
						CreateBrush(renderTarget);
					}
				}
			}

/***********************************************************************
GuiSolidBackgroundElementRenderer
***********************************************************************/

			GuiSolidBackgroundElementRenderer::GuiSolidBackgroundElementRenderer()
			{
			}

			void GuiSolidBackgroundElementRenderer::Render(Rect bounds)
			{
				ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
				auto shape = element->GetShape();

				switch(shape.shapeType)
				{
				case ElementShapeType::Rectangle:
					d2dRenderTarget->FillRectangle(
						D2D1::RectF((FLOAT)bounds.x1, (FLOAT)bounds.y1, (FLOAT)bounds.x2, (FLOAT)bounds.y2),
						brush
						);
					break;
				case ElementShapeType::Ellipse:
					d2dRenderTarget->FillEllipse(
						D2D1::Ellipse(D2D1::Point2F((bounds.x1+bounds.x2)/2.0f, (bounds.y1+bounds.y2)/2.0f), bounds.Width()/2.0f, bounds.Height()/2.0f),
						brush
						);
					break;
				case ElementShapeType::RoundRect:
					d2dRenderTarget->FillRoundedRectangle(
						D2D1::RoundedRect(
							D2D1::RectF((FLOAT)bounds.x1 + 0.5f, (FLOAT)bounds.y1 + 0.5f, (FLOAT)bounds.x2 - 0.5f, (FLOAT)bounds.y2 - 0.5f),
							(FLOAT)shape.radiusX,
							(FLOAT)shape.radiusY
							),
						brush
						);
					break;
				}
			}

/***********************************************************************
GuiGradientBackgroundElementRenderer
***********************************************************************/

			GuiGradientBackgroundElementRenderer::GuiGradientBackgroundElementRenderer()
			{
			}

			void GuiGradientBackgroundElementRenderer::Render(Rect bounds)
			{
				D2D1_POINT_2F points[2];
				switch(element->GetDirection())
				{
				case GuiGradientBackgroundElement::Horizontal:
					{
						points[0].x=(FLOAT)bounds.x1;
						points[0].y=(FLOAT)bounds.y1;
						points[1].x=(FLOAT)bounds.x2;
						points[1].y=(FLOAT)bounds.y1;
					}
					break;
				case GuiGradientBackgroundElement::Vertical:
					{
						points[0].x=(FLOAT)bounds.x1;
						points[0].y=(FLOAT)bounds.y1;
						points[1].x=(FLOAT)bounds.x1;
						points[1].y=(FLOAT)bounds.y2;
					}
					break;
				case GuiGradientBackgroundElement::Slash:
					{
						points[0].x=(FLOAT)bounds.x2;
						points[0].y=(FLOAT)bounds.y1;
						points[1].x=(FLOAT)bounds.x1;
						points[1].y=(FLOAT)bounds.y2;
					}
					break;
				case GuiGradientBackgroundElement::Backslash:
					{
						points[0].x=(FLOAT)bounds.x1;
						points[0].y=(FLOAT)bounds.y1;
						points[1].x=(FLOAT)bounds.x2;
						points[1].y=(FLOAT)bounds.y2;
					}
					break;
				}

				brush->SetStartPoint(points[0]);
				brush->SetEndPoint(points[1]);
				
				ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
				auto shape = element->GetShape();

				switch(shape.shapeType)
				{
				case ElementShapeType::Rectangle:
					d2dRenderTarget->FillRectangle(
						D2D1::RectF((FLOAT)bounds.x1, (FLOAT)bounds.y1, (FLOAT)bounds.x2, (FLOAT)bounds.y2),
						brush
						);
					break;
				case ElementShapeType::Ellipse:
					d2dRenderTarget->FillEllipse(
						D2D1::Ellipse(D2D1::Point2F((bounds.x1+bounds.x2)/2.0f, (bounds.y1+bounds.y2)/2.0f), bounds.Width()/2.0f, bounds.Height()/2.0f),
						brush
						);
					break;
				case ElementShapeType::RoundRect:
					d2dRenderTarget->FillRoundedRectangle(
						D2D1::RoundedRect(
							D2D1::RectF((FLOAT)bounds.x1 + 0.5f, (FLOAT)bounds.y1 + 0.5f, (FLOAT)bounds.x2 - 0.5f, (FLOAT)bounds.y2 - 0.5f),
							(FLOAT)shape.radiusX,
							(FLOAT)shape.radiusY
							),
						brush
						);
					break;
				}
			}

/***********************************************************************
GuiInnerShadowElementRenderer
***********************************************************************/

			void GuiInnerShadowElementRenderer::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)
			{
				if (_renderTarget)
				{
					oldColor = element->GetColor();
					transparentColor = Color(oldColor.r, oldColor.g, oldColor.b, 0);
					linearBrush = _renderTarget->CreateDirect2DLinearBrush(transparentColor, oldColor);
					radialBrush = _renderTarget->CreateDirect2DRadialBrush(transparentColor, oldColor);
				}
			}

			void GuiInnerShadowElementRenderer::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)
			{
				if (_renderTarget)
				{
					_renderTarget->DestroyDirect2DLinearBrush(transparentColor, oldColor);
					_renderTarget->DestroyDirect2DRadialBrush(transparentColor, oldColor);

					linearBrush = nullptr;
					radialBrush = nullptr;
				}
			}

			void GuiInnerShadowElementRenderer::InitializeInternal()
			{
			}

			void GuiInnerShadowElementRenderer::FinalizeInternal()
			{
				DestroyBrush(renderTarget);
			}

			void GuiInnerShadowElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
			{
				DestroyBrush(oldRenderTarget);
				CreateBrush(newRenderTarget);
			}
			
			GuiInnerShadowElementRenderer::GuiInnerShadowElementRenderer()
			{
			}
			
			void GuiInnerShadowElementRenderer::Render(Rect bounds)
			{
				vint w = bounds.Width();
				vint h = bounds.Height();
				vint t = element->GetThickness();
				vint bW = w - 2 * t;
				vint bH = h - 2 * t;
				if (bW > 0 && bH > 0)
				{
					vint x1 = bounds.Left();
					vint x4 = bounds.Right();
					vint x2 = x1 + t;
					vint x3 = x4 - t;

					vint y1 = bounds.Top();
					vint y4 = bounds.Bottom();
					vint y2 = y1 + t;
					vint y3 = y4 - t;

					auto d2dRenderTarget = renderTarget->GetDirect2DRenderTarget();
					{
						// top
						linearBrush->SetStartPoint(D2D1::Point2F((FLOAT)x2, (FLOAT)y2));
						linearBrush->SetEndPoint(D2D1::Point2F((FLOAT)x2, (FLOAT)y1));
						d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x2, (FLOAT)y1, (FLOAT)x3, (FLOAT)y2), linearBrush);
					}
					{
						// bottom
						linearBrush->SetStartPoint(D2D1::Point2F((FLOAT)x2, (FLOAT)y3));
						linearBrush->SetEndPoint(D2D1::Point2F((FLOAT)x2, (FLOAT)y4));
						d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x2, (FLOAT)y3, (FLOAT)x3, (FLOAT)y4), linearBrush);
					}
					{
						// left
						linearBrush->SetStartPoint(D2D1::Point2F((FLOAT)x2, (FLOAT)y2));
						linearBrush->SetEndPoint(D2D1::Point2F((FLOAT)x1, (FLOAT)y2));
						d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x1, (FLOAT)y2, (FLOAT)x2, (FLOAT)y3), linearBrush);
					}
					{
						// right
						linearBrush->SetStartPoint(D2D1::Point2F((FLOAT)x3, (FLOAT)y2));
						linearBrush->SetEndPoint(D2D1::Point2F((FLOAT)x4, (FLOAT)y2));
						d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x3, (FLOAT)y2, (FLOAT)x4, (FLOAT)y3), linearBrush);
					}

					radialBrush->SetRadiusX((FLOAT)t);
					radialBrush->SetRadiusY((FLOAT)t);
					{
						// left-top
						radialBrush->SetCenter(D2D1::Point2F((FLOAT)x2, (FLOAT)y2));
						d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x1, (FLOAT)y1, (FLOAT)x2, (FLOAT)y2), radialBrush);
					}
					{
						// left-bottom
						radialBrush->SetCenter(D2D1::Point2F((FLOAT)x3, (FLOAT)y2));
						d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x3, (FLOAT)y1, (FLOAT)x4, (FLOAT)y2), radialBrush);
					}
					{
						// right-top
						radialBrush->SetCenter(D2D1::Point2F((FLOAT)x2, (FLOAT)y3));
						d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x1, (FLOAT)y3, (FLOAT)x2, (FLOAT)y4), radialBrush);
					}
					{
						// right-bottom
						radialBrush->SetCenter(D2D1::Point2F((FLOAT)x3, (FLOAT)y3));
						d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x3, (FLOAT)y3, (FLOAT)x4, (FLOAT)y4), radialBrush);
					}
				}
			}

			void GuiInnerShadowElementRenderer::OnElementStateChanged()
			{
				if (renderTarget)
				{
					if (oldColor != element->GetColor())
					{
						DestroyBrush(renderTarget);
						CreateBrush(renderTarget);
					}
				}
			}

/***********************************************************************
GuiSolidLabelElementRenderer
***********************************************************************/

			void GuiSolidLabelElementRenderer::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)
			{
				if(_renderTarget)
				{
					oldColor=element->GetColor();
					brush=_renderTarget->CreateDirect2DBrush(oldColor);
				}
			}

			void GuiSolidLabelElementRenderer::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)
			{
				if(_renderTarget && brush)
				{
					_renderTarget->DestroyDirect2DBrush(oldColor);
					brush = nullptr;
				}
			}

			void GuiSolidLabelElementRenderer::CreateTextFormat(IWindowsDirect2DRenderTarget* _renderTarget)
			{
				if(_renderTarget)
				{
					IWindowsDirect2DResourceManager* resourceManager=GetWindowsDirect2DResourceManager();
					oldFont=element->GetFont();
					if (oldFont.fontFamily == L"") oldFont.fontFamily = windows::GetWindowsNativeController()->ResourceService()->GetDefaultFont().fontFamily;
					if (oldFont.size == 0) oldFont.size = 12;
					textFormat=resourceManager->CreateDirect2DTextFormat(oldFont);
				}
			}

			void GuiSolidLabelElementRenderer::DestroyTextFormat(IWindowsDirect2DRenderTarget* _renderTarget)
			{
				if(_renderTarget && textFormat)
				{
					IWindowsDirect2DResourceManager* resourceManager=GetWindowsDirect2DResourceManager();
					resourceManager->DestroyDirect2DTextFormat(oldFont);
					textFormat = nullptr;
				}
			}

			void GuiSolidLabelElementRenderer::CreateTextLayout()
			{
				if (textFormat)
				{
					HRESULT hr = GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory()->CreateTextLayout(
						oldText.Buffer(),
						(int)oldText.Length(),
						textFormat->textFormat.Obj(),
						0,
						0,
						&textLayout);
					CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");

					if (oldFont.underline)
					{
						DWRITE_TEXT_RANGE textRange;
						textRange.startPosition = 0;
						textRange.length = (int)oldText.Length();
						textLayout->SetUnderline(TRUE, textRange);
					}
					if (oldFont.strikeline)
					{
						DWRITE_TEXT_RANGE textRange;
						textRange.startPosition = 0;
						textRange.length = (int)oldText.Length();
						textLayout->SetStrikethrough(TRUE, textRange);
					}
				}
			}

			void GuiSolidLabelElementRenderer::DestroyTextLayout()
			{
				if(textLayout)
				{
					textLayout->Release();
					textLayout = nullptr;
				}
			}

			void GuiSolidLabelElementRenderer::UpdateMinSize()
			{
				float maxWidth=0;
				DestroyTextLayout();
				bool calculateSizeFromTextLayout=false;
				if(renderTarget)
				{
					if(element->GetWrapLine())
					{
						if(element->GetWrapLineHeightCalculation())
						{
							CreateTextLayout();
							if(textLayout)
							{
								maxWidth=textLayout->GetMaxWidth();
								if(oldMaxWidth!=-1)
								{
									textLayout->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);
									textLayout->SetMaxWidth((float)oldMaxWidth);
								}
								calculateSizeFromTextLayout=true;
							}
						}
					}
					else
					{
						CreateTextLayout();
						if(textLayout)
						{
							maxWidth=textLayout->GetMaxWidth();
							calculateSizeFromTextLayout=true;
						}
					}
				}
				if(calculateSizeFromTextLayout)
				{
					DWRITE_TEXT_METRICS metrics;
					HRESULT hr=textLayout->GetMetrics(&metrics);
					if(!FAILED(hr))
					{
						vint width=0;
						if(!element->GetEllipse() && !element->GetWrapLine() && !element->GetMultiline())
						{
							width=(vint)ceil(metrics.widthIncludingTrailingWhitespace);
						}
						minSize=Size(width, (vint)ceil(metrics.height));
					}
					textLayout->SetMaxWidth(maxWidth);
				}
				else
				{
					minSize=Size();
				}
			}

			void GuiSolidLabelElementRenderer::InitializeInternal()
			{
			}

			void GuiSolidLabelElementRenderer::FinalizeInternal()
			{
				DestroyTextLayout();
				DestroyBrush(renderTarget);
				DestroyTextFormat(renderTarget);
			}

			void GuiSolidLabelElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
			{
				DestroyBrush(oldRenderTarget);
				DestroyTextFormat(oldRenderTarget);
				CreateBrush(newRenderTarget);
				CreateTextFormat(newRenderTarget);
				UpdateMinSize();
			}

			GuiSolidLabelElementRenderer::GuiSolidLabelElementRenderer()
			{
			}

			void GuiSolidLabelElementRenderer::Render(Rect bounds)
			{
				if(!textLayout)
				{
					CreateTextLayout();
				}

				vint x=0;
				vint y=0;
				switch(element->GetHorizontalAlignment())
				{
				case Alignment::Left:
					x=bounds.Left();
					break;
				case Alignment::Center:
					x=bounds.Left()+(bounds.Width()-minSize.x)/2;
					break;
				case Alignment::Right:
					x=bounds.Right()-minSize.x;
					break;
				}
				switch(element->GetVerticalAlignment())
				{
				case Alignment::Top:
					y=bounds.Top();
					break;
				case Alignment::Center:
					y=bounds.Top()+(bounds.Height()-minSize.y)/2;
					break;
				case Alignment::Bottom:
					y=bounds.Bottom()-minSize.y;
					break;
				}

				renderTarget->SetTextAntialias(oldFont.antialias, oldFont.verticalAntialias);

				if(!element->GetEllipse() && !element->GetMultiline() && !element->GetWrapLine())
				{
					ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
					d2dRenderTarget->DrawTextLayout(
						D2D1::Point2F((FLOAT)x, (FLOAT)y),
						textLayout,
						brush,
						D2D1_DRAW_TEXT_OPTIONS_NO_SNAP
						);
				}
				else
				{
					IDWriteFactory* dwriteFactory=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory();
					DWRITE_TRIMMING trimming;
					IDWriteInlineObject* inlineObject;
					{
						HRESULT hr = textLayout->GetTrimming(&trimming, &inlineObject);
						CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
					}

					textLayout->SetWordWrapping(element->GetWrapLine()?DWRITE_WORD_WRAPPING_WRAP:DWRITE_WORD_WRAPPING_NO_WRAP);
					if(element->GetEllipse())
					{
						textLayout->SetTrimming(&textFormat->trimming, textFormat->ellipseInlineObject.Obj());
					}
					switch(element->GetHorizontalAlignment())
					{
					case Alignment::Left:
						textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
						break;
					case Alignment::Center:
						textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
						break;
					case Alignment::Right:
						textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING);
						break;
					}
					if(!element->GetMultiline() && !element->GetWrapLine())
					{
						switch(element->GetVerticalAlignment())
						{
						case Alignment::Top:
							textLayout->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
							break;
						case Alignment::Center:
							textLayout->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
							break;
						case Alignment::Bottom:
							textLayout->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_FAR);
							break;
						}
					}

					Rect textBounds=bounds;
					if(element->GetEllipse() && !element->GetMultiline() && !element->GetWrapLine())
					{
						textBounds=Rect(Point(textBounds.x1, y), Size(bounds.Width(), minSize.y));
					}

					textLayout->SetMaxWidth((FLOAT)textBounds.Width());
					textLayout->SetMaxHeight((FLOAT)textBounds.Height());

					ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
					d2dRenderTarget->DrawTextLayout(
						D2D1::Point2F((FLOAT)textBounds.Left(), (FLOAT)textBounds.Top()),
						textLayout,
						brush,
						D2D1_DRAW_TEXT_OPTIONS_NO_SNAP
						);

					textLayout->SetTrimming(&trimming, inlineObject);
					if(oldMaxWidth!=textBounds.Width())
					{
						oldMaxWidth=textBounds.Width();
						UpdateMinSize();
					}
				}
			}

			void GuiSolidLabelElementRenderer::OnElementStateChanged()
			{
				if(renderTarget)
				{
					Color color=element->GetColor();
					if(oldColor!=color)
					{
						DestroyBrush(renderTarget);
						CreateBrush(renderTarget);
					}

					FontProperties font=element->GetFont();
					if(oldFont!=font)
					{
						DestroyTextFormat(renderTarget);
						CreateTextFormat(renderTarget);
					}
				}
				oldText=element->GetText();
				UpdateMinSize();
			}

/***********************************************************************
GuiImageFrameElementRenderer
***********************************************************************/

			void GuiImageFrameElementRenderer::UpdateBitmap(IWindowsDirect2DRenderTarget* renderTarget)
			{
				if (element->GetImage() && 0 <= element->GetFrameIndex() && element->GetFrameIndex() < element->GetImage()->GetFrameCount())
				{
					INativeImageFrame* frame = element->GetImage()->GetFrame(element->GetFrameIndex());
					if (renderTarget)
					{
						bitmap = renderTarget->GetBitmap(frame, element->GetEnabled());
					}

					if (element->GetStretch())
					{
						minSize = Size(0, 0);
					}
					else
					{
						minSize = frame->GetSize();
					}
				}
				else
				{
					bitmap = nullptr;
					minSize = Size(0, 0);
				}
			}

			void GuiImageFrameElementRenderer::InitializeInternal()
			{
			}

			void GuiImageFrameElementRenderer::FinalizeInternal()
			{
			}

			void GuiImageFrameElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
			{
				UpdateBitmap(newRenderTarget);
			}

			GuiImageFrameElementRenderer::GuiImageFrameElementRenderer()
			{
			}

			void GuiImageFrameElementRenderer::Render(Rect bounds)
			{
				if(bitmap)
				{
					ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
					D2D1_RECT_F source=D2D1::RectF(0, 0, (FLOAT)minSize.x, (FLOAT)minSize.y);
					D2D1_RECT_F destination;
					if(element->GetStretch())
					{
						INativeImageFrame* frame=element->GetImage()->GetFrame(element->GetFrameIndex());
						auto size = frame->GetSize();
						source = D2D1::RectF(0, 0, (FLOAT)size.x, (FLOAT)size.y);
						destination=D2D1::RectF((FLOAT)bounds.x1, (FLOAT)bounds.y1, (FLOAT)bounds.x2, (FLOAT)bounds.y2);
					}
					else
					{
						vint x=0;
						vint y=0;
						switch(element->GetHorizontalAlignment())
						{
						case Alignment::Left:
							x=bounds.Left();
							break;
						case Alignment::Center:
							x=bounds.Left()+(bounds.Width()-minSize.x)/2;
							break;
						case Alignment::Right:
							x=bounds.Right()-minSize.x;
							break;
						}
						switch(element->GetVerticalAlignment())
						{
						case Alignment::Top:
							y=bounds.Top();
							break;
						case Alignment::Center:
							y=bounds.Top()+(bounds.Height()-minSize.y)/2;
							break;
						case Alignment::Bottom:
							y=bounds.Bottom()-minSize.y;
							break;
						}
						destination=D2D1::RectF((FLOAT)x, (FLOAT)y, (FLOAT)(x+minSize.x), (FLOAT)(y+minSize.y));
					}

					ID2D1DeviceContext* d2dDeviceContext = nullptr;
					{
						HRESULT hr = d2dRenderTarget->QueryInterface(&d2dDeviceContext);
						if (!SUCCEEDED(hr))
						{
							if (d2dDeviceContext)
							{
								d2dDeviceContext->Release();
							}
							d2dDeviceContext = nullptr;
						}
					}

					if(element->GetImage()->GetFormat()==INativeImage::Gif && element->GetFrameIndex()>0)
					{
						vint max = element->GetFrameIndex();
						for (vint i = 0; i <= max; i++)
						{
							ComPtr<ID2D1Bitmap> frameBitmap=renderTarget->GetBitmap(element->GetImage()->GetFrame(i), element->GetEnabled());
							if (d2dDeviceContext)
							{
								d2dDeviceContext->DrawBitmap(frameBitmap.Obj(), destination, 1.0f, D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC, source);
							}
							else
							{
								d2dRenderTarget->DrawBitmap(frameBitmap.Obj(), destination, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, source);
							}
						}
					}
					else
					{
						if (d2dDeviceContext)
						{
							d2dDeviceContext->DrawBitmap(bitmap.Obj(), destination, 1.0f, D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC, source);
						}
						else
						{
							d2dRenderTarget->DrawBitmap(bitmap.Obj(), destination, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, source);
						}
					}

					if (d2dDeviceContext)
					{
						d2dDeviceContext->Release();
					}
				}
			}

			void GuiImageFrameElementRenderer::OnElementStateChanged()
			{
				UpdateBitmap(renderTarget);
			}

/***********************************************************************
GuiPolygonElementRenderer
***********************************************************************/

			void GuiPolygonElementRenderer::CreateGeometry()
			{
				oldPoints.Resize(element->GetPointCount());
				if (oldPoints.Count() > 0)
				{
					memcpy(&oldPoints[0], &element->GetPoint(0), sizeof(Point)*element->GetPointCount());
				}
				if (oldPoints.Count() >= 3)
				{
					ID2D1PathGeometry* pg = 0;
					HRESULT hr = GetWindowsDirect2DObjectProvider()->GetDirect2DFactory()->CreatePathGeometry(&pg);
					CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");

					geometry = pg;
					FillGeometry(Point(0, 0));
				}
			}

			void GuiPolygonElementRenderer::DestroyGeometry()
			{
				if(geometry)
				{
					geometry = nullptr;
				}
			}

			void GuiPolygonElementRenderer::FillGeometry(Point offset)
			{
				if (geometry)
				{
					ID2D1GeometrySink* pgs = 0;
					HRESULT hr = geometry->Open(&pgs);
					CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");

					D2D1_POINT_2F p;
					p.x = (FLOAT)(oldPoints[0].x + offset.x) + 0.5f;
					p.y = (FLOAT)(oldPoints[0].y + offset.y) + 0.5f;
					pgs->BeginFigure(p, D2D1_FIGURE_BEGIN_FILLED);
					// TODO: (enumerable) foreach
					for (vint i = 1; i < oldPoints.Count(); i++)
					{
						p.x = (FLOAT)(oldPoints[i].x + offset.x) + 0.5f;
						p.y = (FLOAT)(oldPoints[i].y + offset.y) + 0.5f;
						pgs->AddLine(p);
					}
					pgs->EndFigure(D2D1_FIGURE_END_CLOSED);
					pgs->Close();
					pgs->Release();
				}
			}

			void GuiPolygonElementRenderer::RecreateResource(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
			{
				if(oldRenderTarget==newRenderTarget)
				{
					if(oldRenderTarget)
					{
						if(oldBorderColor!=element->GetBorderColor())
						{
							oldRenderTarget->DestroyDirect2DBrush(oldBorderColor);
							oldBorderColor=element->GetBorderColor();
							borderBrush=newRenderTarget->CreateDirect2DBrush(oldBorderColor);
						}
						if(oldBackgroundColor!=element->GetBackgroundColor())
						{
							oldRenderTarget->DestroyDirect2DBrush(oldBackgroundColor);
							oldBackgroundColor=element->GetBackgroundColor();
							backgroundBrush=newRenderTarget->CreateDirect2DBrush(oldBackgroundColor);
						}
					}
				}
				else
				{
					if(oldRenderTarget)
					{
						oldRenderTarget->DestroyDirect2DBrush(oldBorderColor);
						oldRenderTarget->DestroyDirect2DBrush(oldBackgroundColor);
					}
					if(newRenderTarget)
					{
						oldBorderColor=element->GetBorderColor();
						oldBackgroundColor=element->GetBackgroundColor();
						borderBrush=newRenderTarget->CreateDirect2DBrush(oldBorderColor);
						backgroundBrush=newRenderTarget->CreateDirect2DBrush(oldBackgroundColor);
					}
				}
			}

			void GuiPolygonElementRenderer::InitializeInternal()
			{
				oldBorderColor=element->GetBorderColor();
				oldBackgroundColor=element->GetBackgroundColor();
				RecreateResource(0, renderTarget);
				CreateGeometry();
			}

			void GuiPolygonElementRenderer::FinalizeInternal()
			{
				DestroyGeometry();
				RecreateResource(renderTarget, 0);
			}

			void GuiPolygonElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
			{
				RecreateResource(oldRenderTarget, newRenderTarget);
			}

			GuiPolygonElementRenderer::GuiPolygonElementRenderer()
			{
			}

			void GuiPolygonElementRenderer::Render(Rect bounds)
			{
				if(renderTarget && geometry)
				{
					ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
					vint offsetX=(bounds.Width()-minSize.x)/2+bounds.x1;
					vint offsetY=(bounds.Height()-minSize.y)/2+bounds.y1;

					D2D1_MATRIX_3X2_F oldT, newT;
					d2dRenderTarget->GetTransform(&oldT);
					newT=D2D1::Matrix3x2F::Translation((FLOAT)offsetX, (FLOAT)offsetY);
					d2dRenderTarget->SetTransform(&newT);

					d2dRenderTarget->FillGeometry(geometry.Obj(), backgroundBrush);
					d2dRenderTarget->DrawGeometry(geometry.Obj(), borderBrush);
					d2dRenderTarget->SetTransform(&oldT);
				}
			}

			void GuiPolygonElementRenderer::OnElementStateChanged()
			{
				minSize=element->GetSize();
				RecreateResource(renderTarget, renderTarget);

				bool polygonModified=false;
				if(oldPoints.Count()!=element->GetPointCount())
				{
					polygonModified=true;
				}
				else
				{
					// TODO: (enumerable) foreach
					for(vint i=0;i<oldPoints.Count();i++)
					{
						if(oldPoints[i]!=element->GetPoint(i))
						{
							polygonModified=true;
							break;
						}
					}
				}
				if(polygonModified)
				{
					DestroyGeometry();
					CreateGeometry();
				}
			}

/***********************************************************************
GuiDirect2DElementRenderer
***********************************************************************/

			void GuiDirect2DElementRenderer::InitializeInternal()
			{
			}

			void GuiDirect2DElementRenderer::FinalizeInternal()
			{
			}

			void GuiDirect2DElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
			{
				IDWriteFactory* fdw=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory();
				ID2D1Factory* fd2d=GetWindowsDirect2DObjectProvider()->GetDirect2DFactory();
				if(oldRenderTarget)
				{
					GuiDirect2DElementEventArgs arguments(element, oldRenderTarget->GetDirect2DRenderTarget(), fdw, fd2d, Rect());
					element->BeforeRenderTargetChanged.Execute(arguments);
				}
				if(newRenderTarget)
				{
					GuiDirect2DElementEventArgs arguments(element, newRenderTarget->GetDirect2DRenderTarget(), fdw, fd2d, Rect());
					element->AfterRenderTargetChanged.Execute(arguments);
				}
			}

			GuiDirect2DElementRenderer::GuiDirect2DElementRenderer()
			{
			}

			GuiDirect2DElementRenderer::~GuiDirect2DElementRenderer()
			{
			}
			
			void GuiDirect2DElementRenderer::Render(Rect bounds)
			{
				if(renderTarget)
				{
					IDWriteFactory* fdw=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory();
					ID2D1Factory* fd2d=GetWindowsDirect2DObjectProvider()->GetDirect2DFactory();
					renderTarget->PushClipper(bounds, element);
					if(!renderTarget->IsClipperCoverWholeTarget())
					{
						ID2D1RenderTarget* rt=renderTarget->GetDirect2DRenderTarget();
						GuiDirect2DElementEventArgs arguments(element, rt, fdw, fd2d, bounds);
						element->Rendering.Execute(arguments);
					}
					renderTarget->PopClipper(element);
				}
			}

			void GuiDirect2DElementRenderer::OnElementStateChanged()
			{
			}
		}
	}
}

/***********************************************************************
.\DIRECT2D\RENDERERS\GUIGRAPHICSWINDOWSDIRECT2D.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace elements
		{

/***********************************************************************
GuiDirect2DElement
***********************************************************************/

			GuiDirect2DElement::GuiDirect2DElement()
			{
			}
		}

		namespace elements_windows_d2d
		{
			using namespace elements;
			using namespace collections;
			using namespace windows;

			D2D1::ColorF GetD2DColor(Color color)
			{
				return D2D1::ColorF(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f);
			}

/***********************************************************************
CachedResourceAllocator
***********************************************************************/

			typedef Pair<Color, Color> ColorPair;

			class CachedSolidBrushAllocator : public GuiCachedResourceAllocatorBase<CachedSolidBrushAllocator, Color, ComPtr<ID2D1SolidColorBrush>>
			{
				IWindowsDirect2DRenderTarget* guiRenderTarget = nullptr;
			public:
				CachedSolidBrushAllocator()
				{
				}

				void SetRenderTarget(IWindowsDirect2DRenderTarget* _guiRenderTarget)
				{
					guiRenderTarget = _guiRenderTarget;
				}

				ComPtr<ID2D1SolidColorBrush> CreateInternal(Color color)
				{
					ID2D1SolidColorBrush* brush = 0;
					auto renderTarget = guiRenderTarget->GetDirect2DRenderTarget();
					HRESULT hr = renderTarget->CreateSolidColorBrush(GetD2DColor(color), &brush);
					CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
					return brush;
				}
			};

			class CachedLinearBrushAllocator : public GuiCachedResourceAllocatorBase<CachedLinearBrushAllocator, ColorPair, ComPtr<ID2D1LinearGradientBrush>>
			{
				IWindowsDirect2DRenderTarget*	guiRenderTarget = nullptr;
			public:
				CachedLinearBrushAllocator()
				{
				}

				void SetRenderTarget(IWindowsDirect2DRenderTarget* _guiRenderTarget)
				{
					guiRenderTarget = _guiRenderTarget;
				}

				ComPtr<ID2D1LinearGradientBrush> CreateInternal(ColorPair colors)
				{
					ID2D1RenderTarget* renderTarget = guiRenderTarget->GetDirect2DRenderTarget();
					ID2D1GradientStopCollection* stopCollection = 0;
					{
						D2D1_GRADIENT_STOP stops[2];
						stops[0].color = GetD2DColor(colors.key);
						stops[0].position = 0.0f;
						stops[1].color = GetD2DColor(colors.value);
						stops[1].position = 1.0f;

						HRESULT hr = renderTarget->CreateGradientStopCollection(
							stops,
							2,
							D2D1_GAMMA_2_2,
							D2D1_EXTEND_MODE_CLAMP,
							&stopCollection);
						CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
					}

					ID2D1LinearGradientBrush* brush = 0;
					{
						D2D1_POINT_2F points[2] = { {0, 0}, {0, 0} };
						HRESULT hr = renderTarget->CreateLinearGradientBrush(
							D2D1::LinearGradientBrushProperties(points[0], points[1]),
							stopCollection,
							&brush);
						stopCollection->Release();
						CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
					}
					return brush;
				}
			};

			class CachedRadialBrushAllocator : public GuiCachedResourceAllocatorBase<CachedRadialBrushAllocator, ColorPair, ComPtr<ID2D1RadialGradientBrush>>
			{
				IWindowsDirect2DRenderTarget*	guiRenderTarget = nullptr;
			public:
				CachedRadialBrushAllocator()
				{
				}

				void SetRenderTarget(IWindowsDirect2DRenderTarget* _guiRenderTarget)
				{
					guiRenderTarget = _guiRenderTarget;
				}

				ComPtr<ID2D1RadialGradientBrush> CreateInternal(ColorPair colors)
				{
					ID2D1RenderTarget* renderTarget = guiRenderTarget->GetDirect2DRenderTarget();
					ID2D1GradientStopCollection* stopCollection = 0;
					{
						D2D1_GRADIENT_STOP stops[2];
						stops[0].color = GetD2DColor(colors.key);
						stops[0].position = 0.0f;
						stops[1].color = GetD2DColor(colors.value);
						stops[1].position = 1.0f;

						HRESULT hr = renderTarget->CreateGradientStopCollection(
							stops,
							2,
							D2D1_GAMMA_2_2,
							D2D1_EXTEND_MODE_CLAMP,
							&stopCollection);
						CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
					}

					ID2D1RadialGradientBrush* brush = 0;
					{
						D2D1_POINT_2F points[2] = { {0, 0}, {0, 0} };
						HRESULT hr = renderTarget->CreateRadialGradientBrush(
							D2D1::RadialGradientBrushProperties(points[0], points[1], 1, 1),
							stopCollection,
							&brush);
						stopCollection->Release();
						CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
					}
					return brush;
				}
			};

			class CachedTextFormatAllocator : public GuiCachedResourceAllocatorBase<CachedTextFormatAllocator, FontProperties, Ptr<Direct2DTextFormatPackage>>
			{
			public:

				static ComPtr<IDWriteTextFormat> CreateDirect2DFont(const FontProperties& fontProperties)
				{
					IDWriteFactory* dwriteFactory = GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory();
					IDWriteTextFormat* format = 0;
					HRESULT hr = dwriteFactory->CreateTextFormat(
						fontProperties.fontFamily.Buffer(),
						NULL,
						(fontProperties.bold ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL),
						(fontProperties.italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL),
						DWRITE_FONT_STRETCH_NORMAL,
						(FLOAT)fontProperties.size,
						L"",
						&format);
					CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
					format->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
					return format;
				}

				Ptr<Direct2DTextFormatPackage> CreateInternal(const FontProperties& fontProperties)
				{
					auto textFormat = Ptr(new Direct2DTextFormatPackage);
					textFormat->textFormat = CreateDirect2DFont(fontProperties);
					textFormat->trimming.granularity = DWRITE_TRIMMING_GRANULARITY_CHARACTER;
					textFormat->trimming.delimiter = 0;
					textFormat->trimming.delimiterCount = 0;

					IDWriteInlineObject* ellipseInlineObject = 0;
					GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory()->CreateEllipsisTrimmingSign(textFormat->textFormat.Obj(), &ellipseInlineObject);
					textFormat->ellipseInlineObject = ellipseInlineObject;
					return textFormat;
				}
			};

/***********************************************************************
WindowsDirect2DRenderTarget
***********************************************************************/

			class WindowsDirect2DImageFrameCache : public Object, public INativeImageFrameCache
			{
			protected:
				IWindowsDirect2DRenderTarget*	renderTarget = nullptr;
				INativeImageFrame*				cachedFrame = nullptr;
				ComPtr<ID2D1Bitmap>				bitmap;
				ComPtr<ID2D1Bitmap>				disabledBitmap;
			public:
				WindowsDirect2DImageFrameCache(IWindowsDirect2DRenderTarget* _renderTarget)
					:renderTarget(_renderTarget)
				{
				}

				~WindowsDirect2DImageFrameCache()
				{
				}

				void OnAttach(INativeImageFrame* frame)override
				{
					cachedFrame = frame;
					ID2D1Bitmap* d2dBitmap = 0;
					HRESULT hr = renderTarget->GetDirect2DRenderTarget()->CreateBitmapFromWicBitmap(
						GetWindowsDirect2DObjectProvider()->GetWICBitmap(frame),
						&d2dBitmap
					);
					CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
					bitmap = d2dBitmap;
				}
				
				void OnDetach(INativeImageFrame* frame)override
				{
					renderTarget->DestroyBitmapCache(cachedFrame);
				}

				INativeImageFrame* GetFrame()
				{
					return cachedFrame;
				}

				ComPtr<ID2D1Bitmap> GetBitmap(bool enabled)
				{
					if (enabled)
					{
						return bitmap;
					}
					else
					{
						if (!disabledBitmap)
						{
							IWICBitmap* frameBitmap = GetWindowsDirect2DObjectProvider()->GetWICBitmap(cachedFrame);
							ID2D1Bitmap* d2dBitmap = 0;
							HRESULT hr = renderTarget->GetDirect2DRenderTarget()->CreateBitmapFromWicBitmap(
								frameBitmap,
								&d2dBitmap
							);
							CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");

							disabledBitmap = d2dBitmap;
							WICRect rect;
							rect.X = 0;
							rect.Y = 0;
							rect.Width = bitmap->GetPixelSize().width;
							rect.Height = bitmap->GetPixelSize().height;
							BYTE* buffer = new BYTE[rect.Width * rect.Height * 4];
							hr = frameBitmap->CopyPixels(&rect, rect.Width * 4, rect.Width * rect.Height * 4, buffer);
							if (SUCCEEDED(hr))
							{
								vint count = rect.Width * rect.Height;
								BYTE* read = buffer;
								for (vint i = 0; i < count; i++)
								{
									BYTE g = (read[0] + read[1] + read[2]) / 6 + read[3] / 2;
									read[0] = g;
									read[1] = g;
									read[2] = g;
									read += 4;
								}
								D2D1_RECT_U d2dRect;
								d2dRect.left = 0;
								d2dRect.top = 0;
								d2dRect.right = rect.Width;
								d2dRect.bottom = rect.Height;
								d2dBitmap->CopyFromMemory(&d2dRect, buffer, rect.Width * 4);
							}
							delete[] buffer;
						}
						return disabledBitmap;
					}
				}
			};

			class WindowsDirect2DRenderTarget : public IWindowsDirect2DRenderTarget
			{
				typedef SortedList<Ptr<WindowsDirect2DImageFrameCache>> ImageCacheList;
			protected:
				INativeWindow*					window;
				ID2D1RenderTarget*				d2dRenderTarget = nullptr;
				ID2D1DeviceContext*				d2dDeviceContext = nullptr;
				List<Rect>						clippers;
				vint							clipperCoverWholeTargetCounter = 0;
				bool							hostedRendering = false;
				bool							rendering = false;

				CachedSolidBrushAllocator		solidBrushes;
				CachedLinearBrushAllocator		linearBrushes;
				CachedRadialBrushAllocator		radialBrushes;
				ImageCacheList					imageCaches;
				ComPtr<ID2D1Effect>				focusRectangleEffect;

				ComPtr<IDWriteRenderingParams>	noAntialiasParams;
				ComPtr<IDWriteRenderingParams>	horizontalAntialiasParams;
				ComPtr<IDWriteRenderingParams>	bidirectionalAntialiasParams;

				ComPtr<IDWriteRenderingParams> CreateRenderingParams(DWRITE_RENDERING_MODE renderingMode, IDWriteRenderingParams* defaultParams, IDWriteFactory* dwriteFactory)
				{
					IDWriteRenderingParams* renderingParams = 0;
					FLOAT gamma = defaultParams->GetGamma();
					FLOAT enhancedContrast = defaultParams->GetEnhancedContrast();
					FLOAT clearTypeLevel = defaultParams->GetClearTypeLevel();
					DWRITE_PIXEL_GEOMETRY pixelGeometry = defaultParams->GetPixelGeometry();
					HRESULT hr = dwriteFactory->CreateCustomRenderingParams(
						gamma,
						enhancedContrast,
						clearTypeLevel,
						pixelGeometry,
						renderingMode,
						&renderingParams);
					if (!FAILED(hr))
					{
						return renderingParams;
					}
					else
					{
						return nullptr;
					}
				}

				void StartRenderingOnNativeWindow() override
				{
					d2dRenderTarget = GetWindowsDirect2DObjectProvider()->GetNativeWindowDirect2DRenderTarget(window);
					CHECK_ERROR(d2dRenderTarget, L"vl::presentation::elements_windows_d2d::WindowsDirect2DRenderTarget::StartRenderingOnNativeWindow()#Invalid render target.");
					GetWindowsDirect2DObjectProvider()->StartRendering(window);
				}

				RenderTargetFailure StopRenderingOnNativeWindow() override
				{
					CHECK_ERROR(d2dRenderTarget, L"vl::presentation::elements_windows_d2d::WindowsDirect2DRenderTarget::StopRenderingOnNativeWindow()#Invalid render target.");
					auto result = GetWindowsDirect2DObjectProvider()->StopRenderingAndPresent(window);
					d2dRenderTarget = nullptr;
					return result;
				}

				Size GetCanvasSize() override
				{
					return window->Convert(window->GetClientSize());
				}

				void AfterPushedClipper(Rect clipper, Rect validArea, reflection::DescriptableObject* generator) override
				{
					d2dRenderTarget->PushAxisAlignedClip(
						D2D1::RectF((FLOAT)validArea.x1, (FLOAT)validArea.y1, (FLOAT)validArea.x2, (FLOAT)validArea.y2),
						D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
						);
				}

				void AfterPushedClipperAndBecameInvalid(Rect clipper, reflection::DescriptableObject* generator) override
				{
				}

				void AfterPoppedClipperAndBecameValid(Rect validArea, bool clipperExists, reflection::DescriptableObject* generator) override
				{
				}

				void AfterPoppedClipper(Rect validArea, bool clipperExists, reflection::DescriptableObject* generator) override
				{
					d2dRenderTarget->PopAxisAlignedClip();
				}
			public:
				WindowsDirect2DRenderTarget(INativeWindow* _window)
					: window(_window)
				{
					solidBrushes.SetRenderTarget(this);
					linearBrushes.SetRenderTarget(this);
					radialBrushes.SetRenderTarget(this);

					IDWriteFactory* dwriteFactory = GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory();
					IDWriteRenderingParams* defaultParams = 0;
					HRESULT hr = dwriteFactory->CreateRenderingParams(&defaultParams);
					if (!FAILED(hr))
					{
						noAntialiasParams = CreateRenderingParams(DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL, defaultParams, dwriteFactory);
						horizontalAntialiasParams = CreateRenderingParams(DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL, defaultParams, dwriteFactory);
						bidirectionalAntialiasParams = CreateRenderingParams(DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, defaultParams, dwriteFactory);
						defaultParams->Release();
					}
				}

				~WindowsDirect2DRenderTarget()
				{
					while (imageCaches.Count())
					{
						Ptr<WindowsDirect2DImageFrameCache> cache = imageCaches[imageCaches.Count() - 1];
						cache->GetFrame()->RemoveCache(this);
					}
				}

				ID2D1RenderTarget* GetDirect2DRenderTarget()override
				{
					return d2dRenderTarget ? d2dRenderTarget : GetWindowsDirect2DObjectProvider()->GetNativeWindowDirect2DRenderTarget(window);
				}

				ComPtr<ID2D1Bitmap> GetBitmap(INativeImageFrame* frame, bool enabled)override
				{
					Ptr<INativeImageFrameCache> cache = frame->GetCache(this);
					if (cache)
					{
						return cache.Cast<WindowsDirect2DImageFrameCache>()->GetBitmap(enabled);
					}
					else
					{
						auto d2dCache = Ptr(new WindowsDirect2DImageFrameCache(this));
						if (frame->SetCache(this, d2dCache))
						{
							imageCaches.Add(d2dCache);
							return d2dCache->GetBitmap(enabled);
						}
						else
						{
							return nullptr;
						}
					}
				}

				void DestroyBitmapCache(INativeImageFrame* frame)override
				{
					WindowsDirect2DImageFrameCache* cache = frame->GetCache(this).Cast<WindowsDirect2DImageFrameCache>().Obj();
					imageCaches.Remove(cache);
				}

				void SetTextAntialias(bool antialias, bool verticalAntialias)override
				{
					ComPtr<IDWriteRenderingParams> params;
					if (!antialias)
					{
						params = noAntialiasParams;
					}
					else if (!verticalAntialias)
					{
						params = horizontalAntialiasParams;
					}
					else
					{
						params = bidirectionalAntialiasParams;
					}
					if (params && d2dRenderTarget)
					{
						d2dRenderTarget->SetTextRenderingParams(params.Obj());
					}
				}

				ID2D1Effect* GetFocusRectangleEffect()override
				{
					if (!focusRectangleEffect)
					{
						ID2D1RenderTarget* d2dRenderTarget = GetWindowsDirect2DObjectProvider()->GetNativeWindowDirect2DRenderTarget(window);
						ID2D1DeviceContext* d2dDeviceContext = nullptr;
						HRESULT hr = d2dRenderTarget->QueryInterface(&d2dDeviceContext);
						if (d2dDeviceContext)
						{
							if (auto wicFactory = GetWICImagingFactory())
							{
								// this is the content of EffectBuffer.png in the same folder where this cpp file is in
								static const char EffectBuffer[] =
									"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52\x00\x00\x00\x08\x00\x00\x00\x08\x08\x06\x00\x00"
									"\x00\xC4\x0F\xBE\x8B\x00\x00\x00\x01\x73\x52\x47\x42\x00\xAE\xCE\x1C\xE9\x00\x00\x00\x04\x67\x41\x4D\x41\x00\x00"
									"\xB1\x8F\x0B\xFC\x61\x05\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0E\xC3\x00\x00\x0E\xC3\x01\xC7\x6F\xA8\x64\x00"
									"\x00\x00\x2F\x49\x44\x41\x54\x28\x53\x75\xC8\xB1\x0D\x00\x30\x0C\x02\x41\xEF\xBF\x34\xB1\x68\xA2\x47\xF8\x9B\x13"
									"\x8C\xB6\xD9\x2E\x5D\x9E\x10\xA3\xE8\xF2\x84\x18\x45\x97\x27\xC4\x28\xBA\x3C\xBF\xD2\x03\xC2\xD7\x7F\x81\x23\x94"
									"\x3E\x1F\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82";
								auto effectImage = GetWindowsNativeController()->ImageService()->CreateImageFromMemory((void*)EffectBuffer, (vint)(sizeof(EffectBuffer) - 1));
								IWICBitmap* wicEffectBitmap = effectImage ? GetWICBitmap(effectImage->GetFrame(0)) : nullptr;
								if (wicEffectBitmap)
								{
									ID2D1Bitmap* d2dEffectBitmap = nullptr;
									hr = d2dRenderTarget->CreateBitmapFromWicBitmap(wicEffectBitmap, &d2dEffectBitmap);
									if (d2dEffectBitmap)
									{
										ID2D1Effect* d2dEffect = nullptr;
										hr = d2dDeviceContext->CreateEffect(CLSID_D2D1Tile, &d2dEffect);
										if (d2dEffect)
										{
											d2dEffect->SetInput(0, d2dEffectBitmap);
											d2dEffect->SetValue(D2D1_TILE_PROP_RECT, D2D1::RectF(0, 0, 2, 2));
											focusRectangleEffect = d2dEffect;
										}
										d2dEffectBitmap->Release();
									}
								}
							}
							d2dDeviceContext->Release();
						}
					}
					return focusRectangleEffect.Obj();
				}

				ID2D1SolidColorBrush* CreateDirect2DBrush(Color color)override
				{
					return solidBrushes.Create(color).Obj();
				}

				void DestroyDirect2DBrush(Color color)override
				{
					solidBrushes.Destroy(color);
				}

				ID2D1LinearGradientBrush* CreateDirect2DLinearBrush(Color c1, Color c2)override
				{
					return linearBrushes.Create(Pair<Color, Color>(c1, c2)).Obj();
				}

				void DestroyDirect2DLinearBrush(Color c1, Color c2)override
				{
					linearBrushes.Destroy(Pair<Color, Color>(c1, c2));
				}

				ID2D1RadialGradientBrush* CreateDirect2DRadialBrush(Color c1, Color c2)override
				{
					return radialBrushes.Create(Pair<Color, Color>(c1, c2)).Obj();
				}

				void DestroyDirect2DRadialBrush(Color c1, Color c2)override
				{
					radialBrushes.Destroy(Pair<Color, Color>(c1, c2));
				}
			};

/***********************************************************************
WindowsGDIResourceManager
***********************************************************************/

			class WindowsDirect2DResourceManager : public GuiGraphicsResourceManager, public IWindowsDirect2DResourceManager, public INativeControllerListener
			{
			protected:
				SortedList<Ptr<WindowsDirect2DRenderTarget>>		renderTargets;
				Ptr<WindowsDirect2DLayoutProvider>					layoutProvider;

				CachedTextFormatAllocator							textFormats;
			public:
				WindowsDirect2DResourceManager()
				{
					layoutProvider = Ptr(new WindowsDirect2DLayoutProvider);
				}

				IGuiGraphicsRenderTarget* GetRenderTarget(INativeWindow* window)override
				{
					return GetWindowsDirect2DObjectProvider()->GetBindedRenderTarget(window);
				}

				void RecreateRenderTarget(INativeWindow* window)override
				{
					NativeWindowDestroying(window);
					GetWindowsDirect2DObjectProvider()->RecreateRenderTarget(window);
					NativeWindowCreated(window);
				}

				void ResizeRenderTarget(INativeWindow* window)
				{
					GetWindowsDirect2DObjectProvider()->ResizeRenderTarget(window);
				}

				IGuiGraphicsLayoutProvider* GetLayoutProvider()override
				{
					return layoutProvider.Obj();
				}

				Ptr<IGuiGraphicsElement> CreateRawElement() override
				{
					return Ptr(GuiDirect2DElement::Create());
				}

				void NativeWindowCreated(INativeWindow* window)override
				{
					auto renderTarget = Ptr(new WindowsDirect2DRenderTarget(window));
					renderTargets.Add(renderTarget);
					GetWindowsDirect2DObjectProvider()->SetBindedRenderTarget(window, renderTarget.Obj());
				}

				void NativeWindowDestroying(INativeWindow* window)override
				{
					WindowsDirect2DRenderTarget* renderTarget = dynamic_cast<WindowsDirect2DRenderTarget*>(GetWindowsDirect2DObjectProvider()->GetBindedRenderTarget(window));
					GetWindowsDirect2DObjectProvider()->SetBindedRenderTarget(window, 0);
					renderTargets.Remove(renderTarget);
				}

				Direct2DTextFormatPackage* CreateDirect2DTextFormat(const FontProperties& fontProperties)override
				{
					return textFormats.Create(fontProperties).Obj();
				}

				void DestroyDirect2DTextFormat(const FontProperties& fontProperties)override
				{
					textFormats.Destroy(fontProperties);
				}
			};
		}

		namespace elements_windows_d2d
		{
			IWindowsDirect2DResourceManager* windowsDirect2DResourceManager = nullptr;

			IWindowsDirect2DResourceManager* GetWindowsDirect2DResourceManager()
			{
				return windowsDirect2DResourceManager;
			}

			void SetWindowsDirect2DResourceManager(IWindowsDirect2DResourceManager* resourceManager)
			{
				windowsDirect2DResourceManager = resourceManager;
			}

/***********************************************************************
OS Supporting
***********************************************************************/

			IWindowsDirect2DObjectProvider* windowsDirect2DObjectProvider = nullptr;

			IWindowsDirect2DObjectProvider* GetWindowsDirect2DObjectProvider()
			{
				return windowsDirect2DObjectProvider;
			}

			void SetWindowsDirect2DObjectProvider(IWindowsDirect2DObjectProvider* provider)
			{
				windowsDirect2DObjectProvider = provider;
			}
		}
	}
}

/***********************************************************************
NativeMain
***********************************************************************/

using namespace vl::presentation;
using namespace vl::presentation::elements;


extern void GuiRawMain();
extern void GuiApplicationMain();

void RendererMainDirect2D(GuiHostedController* hostedController, bool raw)
{
	elements_windows_d2d::WindowsDirect2DResourceManager resourceManager;
	elements_windows_d2d::SetWindowsDirect2DResourceManager(&resourceManager);
	windows::GetWindowsNativeController()->CallbackService()->InstallListener(&resourceManager);

	auto hostedResourceManager = hostedController ? new GuiHostedGraphicsResourceManager(hostedController, &resourceManager) : nullptr;
	SetGuiGraphicsResourceManager(
		hostedResourceManager
		? hostedResourceManager
		: static_cast<IGuiGraphicsResourceManager*>(&resourceManager)
		);

	{
		elements_windows_d2d::GuiFocusRectangleElementRenderer::Register();
		elements_windows_d2d::GuiSolidBorderElementRenderer::Register();
		elements_windows_d2d::Gui3DBorderElementRenderer::Register();
		elements_windows_d2d::Gui3DSplitterElementRenderer::Register();
		elements_windows_d2d::GuiSolidBackgroundElementRenderer::Register();
		elements_windows_d2d::GuiGradientBackgroundElementRenderer::Register();
		elements_windows_d2d::GuiInnerShadowElementRenderer::Register();
		elements_windows_d2d::GuiSolidLabelElementRenderer::Register();
		elements_windows_d2d::GuiImageFrameElementRenderer::Register();
		elements_windows_d2d::GuiPolygonElementRenderer::Register();
		elements_windows_d2d::GuiDirect2DElementRenderer::Register();
		elements::GuiDocumentElementRenderer::Register();

		if (hostedController) hostedController->Initialize();
		if (raw)
		{
			GuiRawMain();
		}
		else
		{
			GuiApplicationMain();
		}
		if (hostedController) hostedController->Finalize();
	}

	SetGuiGraphicsResourceManager(nullptr);
	if (hostedResourceManager) delete hostedResourceManager;

	windows::GetWindowsNativeController()->CallbackService()->UninstallListener(&resourceManager);
	elements_windows_d2d::SetWindowsDirect2DResourceManager(nullptr);
}

/***********************************************************************
.\GDI\WINGDI.CPP
***********************************************************************/

#pragma comment(lib, "Msimg32.lib")

namespace vl
{
	namespace presentation
	{
		namespace windows
		{

/*********************************************************************************************************
WinRegion
*********************************************************************************************************/

		bool IsEqual(WinRegion::Ptr Region1, WinRegion::Ptr Region2)
		{
			return EqualRgn(Region1->GetHandle(), Region2->GetHandle())!=0;
		}

		WinRegion::WinRegion(vint Left, vint Top, vint Right, vint Bottom, bool Rectangle)
		{
			if(Rectangle)
			{
				FHandle=CreateRectRgn((int)Left, (int)Top, (int)Right, (int)Bottom);
			}
			else
			{
				FHandle=CreateEllipticRgn((int)Left, (int)Top, (int)Right, (int)Bottom);
			}
		}

		WinRegion::WinRegion(RECT Rect, bool Rectangle)
		{
			if(Rectangle)
			{
				FHandle=CreateRectRgnIndirect(&Rect);
			}
			else
			{
				FHandle=CreateEllipticRgnIndirect(&Rect);
			}
		}

		WinRegion::WinRegion(vint Left, vint Top, vint Right, vint Bottom, vint EllipseWidth, vint EllipseHeight)
		{
			FHandle=CreateRoundRectRgn((int)Left, (int)Top, (int)Right, (int)Bottom, (int)EllipseWidth, (int)EllipseHeight);
		}

		WinRegion::WinRegion(POINT* Points, vint Count, bool Alternate)
		{
			FHandle=CreatePolygonRgn(Points, (int)Count, Alternate?ALTERNATE:WINDING);
		}

		WinRegion::WinRegion(WinRegion::Ptr Region)
		{
			FHandle=CreateRectRgn(0, 0, 1, 1);
			CombineRgn(FHandle, Region->GetHandle(), Region->GetHandle(), RGN_COPY);
		}

		WinRegion::WinRegion(WinRegion::Ptr Region1, WinRegion::Ptr Region2, vint CombineMode)
		{
			FHandle=CreateRectRgn(0, 0, 1, 1);
			CombineRgn(FHandle, Region1->GetHandle(), Region2->GetHandle(), (int)CombineMode);
		}

		WinRegion::WinRegion(HRGN RegionHandle)
		{
			FHandle=RegionHandle;
		}

		WinRegion::~WinRegion()
		{
			DeleteObject(FHandle);
		}

		HRGN WinRegion::GetHandle()
		{
			return FHandle;
		}

		bool WinRegion::ContainPoint(POINT Point)
		{
			return PtInRegion(FHandle, Point.x, Point.y)!=0;
		}

		bool WinRegion::ContainRect(RECT Rect)
		{
			return RectInRegion(FHandle, &Rect)!=0;
		}

		RECT WinRegion::GetBoundRect()
		{
			RECT Rect={0, 0, 0, 0};
			GetRgnBox(FHandle, &Rect);
			return Rect;
		}

		void WinRegion::Move(vint OffsetX, vint OffsetY)
		{
			OffsetRgn(FHandle, (int)OffsetX, (int)OffsetY);
		}

/*********************************************************************************************************
WinTransform
*********************************************************************************************************/
	
		WinTransform::WinTransform(XFORM Transform)
		{
			FTransform=Transform;
		}

		WinTransform::WinTransform(const WinTransform& Transform)
		{
			FTransform=Transform.FTransform;
		}

		WinTransform& WinTransform::operator=(const WinTransform& Transform)
		{
			FTransform=Transform.FTransform;
			return *this;
		}

		WinTransform WinTransform::operator*(const WinTransform& Transform)
		{
			XFORM Result;
			CombineTransform(&Result, GetHandle(), Transform.GetHandle());
			return Result;
		}

		const XFORM* WinTransform::GetHandle()const
		{
			return &FTransform;
		}

		/*------------------------------------------------------------------------------*/

		WinTransform WinTransform::Translate(float OffsetX, float OffsetY)
		{
			XFORM Transform;
			Transform.eM11=1.0f;
			Transform.eM12=0.0f;
			Transform.eM21=0.0f;
			Transform.eM22=1.0f;
			Transform.eDx=OffsetX;
			Transform.eDy=OffsetY;
			return Transform;
		}

		WinTransform WinTransform::Scale(float ScaleX, float ScaleY)
		{
			XFORM Transform;
			Transform.eM11=ScaleX;
			Transform.eM12=0.0f;
			Transform.eM21=0.0f;
			Transform.eM22=ScaleY;
			Transform.eDx=0.0f;
			Transform.eDy=0.0f;
			return Transform;
		}

		WinTransform WinTransform::Rotate(float Angle)
		{
			XFORM Transform;
			Transform.eM11=(FLOAT)cos(Angle);
			Transform.eM12= (FLOAT)sin(Angle);
			Transform.eM21= (FLOAT)-sin(Angle);
			Transform.eM22= (FLOAT)cos(Angle);
			Transform.eDx=0.0f;
			Transform.eDy=0.0f;
			return Transform;
		}

		WinTransform WinTransform::Rotate(float Cos, float Sin)
		{
			XFORM Transform;
			Transform.eM11=Cos;
			Transform.eM12=Sin;
			Transform.eM21=-Sin;
			Transform.eM22=Cos;
			Transform.eDx=0.0f;
			Transform.eDy=0.0f;
			return Transform;
		}

		WinTransform WinTransform::ReflectX()
		{
			XFORM Transform;
			Transform.eM11=1.0f;
			Transform.eM12=0.0f;
			Transform.eM21=0.0f;
			Transform.eM22=-1.0f;
			Transform.eDx=0.0f;
			Transform.eDy=0.0f;
			return Transform;
		}

		WinTransform WinTransform::ReflectY()
		{
			XFORM Transform;
			Transform.eM11=-1.0f;
			Transform.eM12=0.0f;
			Transform.eM21=0.0f;
			Transform.eM22=1.0f;
			Transform.eDx=0.0f;
			Transform.eDy=0.0f;
			return Transform;
		}

		WinTransform WinTransform::Reflect(float VectorX, float VectorY)
		{
			float Len= (FLOAT)sqrt(VectorX*VectorX+VectorY*VectorY);
			float Cos=VectorX/Len;
			float Sin=VectorY/Len;

			return Rotate(Cos, -Sin)*ReflectX()*Rotate(Cos, Sin);
		}

		WinTransform WinTransform::Reflect(float OriginX, float OriginY, float VectorX, float VectorY)
		{
			float Len= (FLOAT)sqrt(VectorX*VectorX+VectorY*VectorY);
			float Cos=VectorX/Len;
			float Sin=VectorY/Len;

			return Translate(-OriginX, -OriginY)*Rotate(Cos, -Sin)*ReflectX()*Rotate(Cos, Sin)*Translate(OriginX, OriginY);
		}

		WinTransform WinTransform::AxisV(float Xx, float Xy, float Yx, float Yy)
		{
			XFORM Transform;
			Transform.eM11=Xx;
			Transform.eM12=Xy;
			Transform.eM21=Yx;
			Transform.eM22=Yy;
			Transform.eDx=0.0f;
			Transform.eDy=0.0f;
			return Transform;
		}

		WinTransform WinTransform::AxisA(float AngleX, float LenX, float AngleY, float LenY)
		{
			XFORM Transform;
			Transform.eM11=(FLOAT)cos(AngleX)*LenX;
			Transform.eM12=(FLOAT)sin(AngleX)*LenX;
			Transform.eM21=(FLOAT)cos(AngleY)*LenY;
			Transform.eM22=(FLOAT)sin(AngleY)*LenY;
			Transform.eDx=0.0f;
			Transform.eDy=0.0f;
			return Transform;
		}

/*********************************************************************************************************
WinMetaFileBuilder
*********************************************************************************************************/

		void WinMetaFileBuilder::Create(vint Width, vint Height)
		{
			HDC hdcRef=GetDC(NULL);
			vint iWidthMM = GetDeviceCaps(hdcRef, HORZSIZE); 
			vint iHeightMM = GetDeviceCaps(hdcRef, VERTSIZE); 
			vint iWidthPels = GetDeviceCaps(hdcRef, HORZRES); 
			vint iHeightPels = GetDeviceCaps(hdcRef, VERTRES); 
			ReleaseDC(NULL, hdcRef);

			RECT Rect;
			Rect.left=0;
			Rect.top=0;
			Rect.right = (int)((Width*iWidthMM*100)/iWidthPels);
			Rect.bottom = (int)((Height*iHeightMM*100)/iHeightPels);

			HDC Handle=CreateEnhMetaFile(NULL, NULL, &Rect, L"VczhLibrary++GDI\0Enhanced Meta File\0");
			FDC->Initialize(Handle);

			FWidth=Width;
			FHeight=Height;
		}

		void WinMetaFileBuilder::Draw(HENHMETAFILE Handle)
		{
			RECT Rect;
			Rect.left=0;
			Rect.top=0;
			Rect.right=(int)FWidth;
			Rect.bottom=(int)FHeight;
			PlayEnhMetaFile(FDC->GetHandle(), Handle, &Rect);
		}

		void WinMetaFileBuilder::Destroy()
		{
			DeleteEnhMetaFile(CloseEnhMetaFile(FDC->GetHandle()));
		}

		WinMetaFileBuilder::WinMetaFileBuilder(vint Width, vint Height)
		{
			FDC=new WinProxyDC();
			Create(Width, Height);
		}

		WinMetaFileBuilder::~WinMetaFileBuilder()
		{
			Destroy();
			delete FDC;
		}

		void WinMetaFileBuilder::LoadFrom(WinMetaFile* File)
		{
			Destroy();
			Create(File->GetWidth(), File->GetHeight());
			Draw(File->GetHandle());
		}

		void WinMetaFileBuilder::SaveTo(WinMetaFile* File)
		{
			HENHMETAFILE Handle=CloseEnhMetaFile(FDC->GetHandle());
			if(File->FHandle)
			{
				DeleteEnhMetaFile(File->FHandle);
			}
			File->FHandle=Handle;
			File->FWidth=FWidth;
			File->FHeight=FHeight;
			Create(FWidth, FHeight);
			Draw(Handle);
		}

		void WinMetaFileBuilder::LoadFrom(WString FileName)
		{
			WinMetaFile File(FileName);
			Destroy();
			Create(File.GetWidth(), File.GetHeight());
			Draw(File.GetHandle());
		}

		void WinMetaFileBuilder::SaveTo(WString FileName)
		{
			HENHMETAFILE Handle=CloseEnhMetaFile(FDC->GetHandle());
			HENHMETAFILE NewHandle=CopyEnhMetaFile(Handle, FileName.Buffer());
			DeleteEnhMetaFile(NewHandle);
			Create(FWidth, FHeight);
			Draw(Handle);
			DeleteEnhMetaFile(Handle);
		}

		WinDC* WinMetaFileBuilder::GetWinDC()
		{
			return FDC;
		}

		vint WinMetaFileBuilder::GetWidth()
		{
			return FWidth;
		}

		vint WinMetaFileBuilder::GetHeight()
		{
			return FHeight;
		}

/*********************************************************************************************************
WinMetaFile
*********************************************************************************************************/

		WinMetaFile::WinMetaFile(WString FileName)
		{
			FHandle=GetEnhMetaFile(FileName.Buffer());
			ENHMETAHEADER Header;
			GetEnhMetaFileHeader(FHandle, sizeof(Header), &Header);
			FWidth=(Header.rclFrame.right-Header.rclFrame.left)*Header.szlDevice.cx/(Header.szlMillimeters.cx*100);
			FHeight=(Header.rclFrame.bottom-Header.rclFrame.top)*Header.szlDevice.cy/(Header.szlMillimeters.cy*100);
		}

		WinMetaFile::WinMetaFile(WinMetaFileBuilder* Builder)
		{
			FHandle=NULL;
			Builder->SaveTo(this);
		}

		WinMetaFile::~WinMetaFile()
		{
			DeleteEnhMetaFile(FHandle);
		}

		HENHMETAFILE WinMetaFile::GetHandle()
		{
			return FHandle;
		}

		vint WinMetaFile::GetWidth()
		{
			return FWidth;
		}

		vint WinMetaFile::GetHeight()
		{
			return FHeight;
		}

/*********************************************************************************************************
WinBitmap
*********************************************************************************************************/

		vint WinBitmap::GetBitsFromBB(BitmapBits BB)
		{
			switch(BB)
			{
			case vbb32Bits:
				return 32;
			case vbb24Bits:
				return 24;
			default:
				return 1;
			}
		}

		vint WinBitmap::GetLineBytes(vint Width, BitmapBits BB)
		{
			vint Bits=GetBitsFromBB(BB);
			vint LineBits=Width*Bits;
			vint AlignBits=sizeof(DWORD)*8;
			LineBits+=(AlignBits-LineBits%AlignBits)%AlignBits;
			return LineBits/8;
		}

		void WinBitmap::FillBitmapInfoHeader(vint Width, vint Height, BitmapBits Bits, BITMAPINFOHEADER* Header)
		{
			Header->biSize=sizeof(BITMAPINFOHEADER);
			Header->biWidth=(int)Width;
			Header->biHeight=-(int)Height;
			Header->biPlanes=1;
			Header->biBitCount=(int)GetBitsFromBB(Bits);
			Header->biCompression=BI_RGB;
			Header->biSizeImage=0;
			Header->biXPelsPerMeter=0;
			Header->biYPelsPerMeter=0;
			Header->biClrUsed=(Bits==vbb2Bits?2:0);
			Header->biClrImportant=0;
		}

		HBITMAP WinBitmap::CreateDDB(vint Width, vint Height, BitmapBits Bits)
		{
			if(Bits==vbb2Bits)
			{
				return CreateBitmap((int)Width, (int)Height, 2, (int)GetBitsFromBB(Bits), NULL);
			}
			else
			{
				WinBitmap Bitmap(1, 1, Bits, true);
				return CreateCompatibleBitmap(Bitmap.GetWinDC()->GetHandle(), (int)Width, (int)Height);
			}
		}

		HBITMAP WinBitmap::CreateDIB(vint Width, vint Height, BitmapBits Bits, BYTE**& ScanLines)
		{
			BITMAPINFO* Info=(BITMAPINFO*)malloc(sizeof(BITMAPINFOHEADER)+2*sizeof(RGBQUAD));
			FillBitmapInfoHeader(Width, Height, Bits, &Info->bmiHeader);
			Info->bmiColors[0].rgbBlue=0;
			Info->bmiColors[0].rgbGreen=0;
			Info->bmiColors[0].rgbRed=0;
			Info->bmiColors[0].rgbReserved=0;
			Info->bmiColors[1].rgbBlue=255;
			Info->bmiColors[1].rgbGreen=255;
			Info->bmiColors[1].rgbRed=255;
			Info->bmiColors[1].rgbReserved=255;

			BYTE* FirstLine=0;
			HBITMAP Handle=CreateDIBSection(FDC->GetHandle(), Info, DIB_RGB_COLORS, (void**)&FirstLine, NULL, 0);
			ScanLines=new BYTE*[Height];
			vint LineBytes=GetLineBytes(Width, Bits);
			for(vint i=0;i<Height;i++)
			{
				ScanLines[i]=FirstLine+LineBytes*i;
			}
			free(Info);
			return Handle;
		}

		void WinBitmap::Constructor(vint Width, vint Height, BitmapBits Bits, bool DIBSections)
		{
			FDC=new WinImageDC();
			if(DIBSections)
			{
				FHandle=CreateDIB(Width, Height, Bits, FScanLines);
			}
			else
			{
				FHandle=CreateDDB(Width, Height, Bits);
				FScanLines=0;
			}
			FWidth=Width;
			FHeight=Height;
			FBits=Bits;
			FAlphaChannelBuilt=false;
			HGDIOBJ Object=SelectObject(FDC->GetHandle(), FHandle);
			if(Object)
			{
				DeleteObject(Object);
			}
		}
	
		WinBitmap::WinBitmap(vint Width, vint Height, BitmapBits Bits, bool DIBSections)
		{
			Constructor(Width, Height, Bits, DIBSections);
		}

		WinBitmap::WinBitmap(WString FileName, bool Use32Bits, bool DIBSections)
		{
			FBits=Use32Bits?vbb32Bits:vbb24Bits;
		
			HBITMAP	TempBmp=(HBITMAP)LoadImage(NULL, FileName.Buffer(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
			HDC		TempDC=CreateCompatibleDC(NULL);
			BITMAP	TempRec;

			GetObject(TempBmp, sizeof(BITMAP), &TempRec);
			DeleteObject(SelectObject(TempDC, TempBmp));

			Constructor(TempRec.bmWidth, TempRec.bmHeight, FBits, DIBSections);
			BitBlt(FDC->GetHandle(), 0, 0, TempRec.bmWidth, TempRec.bmHeight, TempDC, 0, 0, SRCCOPY);

			DeleteObject(TempDC);
			DeleteObject(TempBmp);
		}

		WinBitmap::~WinBitmap()
		{
			if(FScanLines)
			{
				delete[] FScanLines;
			}
			if(FHandle)
			{
				DeleteObject(FHandle);
			}
			delete FDC;
		}

		void WinBitmap::SaveToStream(stream::IStream& Output, bool DIBV5ClipboardFormat)
		{
			if (FScanLines)
			{
				BITMAPFILEHEADER Header1;
				BITMAPV5HEADER Header2;

				if (!DIBV5ClipboardFormat)
				{
					Header1.bfType = 'M' * 256 + 'B';
					Header1.bfSize = (int)(sizeof(Header1) + sizeof(Header2) + GetLineBytes()*FHeight);
					Header1.bfReserved1 = 0;
					Header1.bfReserved2 = 0;
					Header1.bfOffBits = sizeof(Header2) + sizeof(Header1);
					Output.Write(&Header1, sizeof(Header1));
				}

				{
					memset(&Header2, 0, sizeof(Header2));
					Header2.bV5Size = sizeof(Header2);
					Header2.bV5Width = (int)FWidth;
					Header2.bV5Height = -(int)FHeight;
					Header2.bV5Planes = 1;
					Header2.bV5BitCount = (int)GetBitsFromBB(FBits);
					Header2.bV5Compression = BI_RGB;
					Header2.bV5CSType = LCS_sRGB;
					Header2.bV5Intent = LCS_GM_GRAPHICS;
					Output.Write(&Header2, sizeof(Header2));
				}

				for (vint i = 0; i<FHeight; i++)
				{
					Output.Write(FScanLines[i], GetLineBytes());
				}
			}
			else
			{
				WinBitmap Temp(FWidth, FHeight, FBits, true);
				Temp.GetWinDC()->Copy(0, 0, FWidth, FHeight, FDC, 0, 0);
				Temp.SaveToStream(Output, false);
			}
		}

		void WinBitmap::SaveToFile(WString FileName)
		{
			stream::FileStream Output(FileName, stream::FileStream::WriteOnly);
			SaveToStream(Output, false);
		}

		WinDC* WinBitmap::GetWinDC()
		{
			return FDC;
		}

		vint WinBitmap::GetWidth()
		{
			return FWidth;
		}

		vint WinBitmap::GetHeight()
		{
			return FHeight;
		}

		vint WinBitmap::GetLineBytes()
		{
			return GetLineBytes(FWidth, FBits);
		}

		BYTE** WinBitmap::GetScanLines()
		{
			return FScanLines;
		}

		HBITMAP WinBitmap::GetBitmap()
		{
			return FHandle;
		}

		WinBitmap::BitmapBits WinBitmap::GetBitmapBits()
		{
			return FBits;
		}
	
		void WinBitmap::FillCompatibleHeader(BITMAPINFOHEADER* Header)
		{
			FillBitmapInfoHeader(FWidth, FHeight, FBits, Header);
		}

		bool WinBitmap::CanBuildAlphaChannel()
		{
			return FScanLines!=0 && FBits==vbb32Bits;
		}

		bool WinBitmap::IsAlphaChannelBuilt()
		{
			return FAlphaChannelBuilt;
		}

		void WinBitmap::BuildAlphaChannel(bool autoPremultiply)
		{
			if(CanBuildAlphaChannel() && !FAlphaChannelBuilt)
			{
				FAlphaChannelBuilt=true;
				if(autoPremultiply)
				{
					for(vint i=0;i<FHeight;i++)
					{
						BYTE* Colors=FScanLines[i];
						vint j=FWidth;
						while(j--)
						{
							BYTE Alpha=Colors[3];
							Colors[0]=Colors[0]*Alpha/255;
							Colors[1]=Colors[1]*Alpha/255;
							Colors[2]=Colors[2]*Alpha/255;
							Colors+=4;
						}
					}
				}
			}
		}

		void WinBitmap::GenerateTrans(COLORREF Color)
		{
			if(CanBuildAlphaChannel() && !FAlphaChannelBuilt)
			{
				for(vint i=0;i<FHeight;i++)
				{
					COLORREF* Colors=(COLORREF*)FScanLines[i];
					vint j=FWidth;
					while(j--)
					{
						COLORREF Dest=*Colors & 0x00FFFFFF;
						*Colors = Dest | (0xFF000000 * (Dest!=Color));
						Colors++;
					}
				}
			}
		}

		void WinBitmap::GenerateAlpha(BYTE Alpha)
		{
			if(CanBuildAlphaChannel() && !FAlphaChannelBuilt)
			{
				for(vint i=0;i<FHeight;i++)
				{
					BYTE* Colors=FScanLines[i];
					vint j=FWidth;
					while(j--)
					{
						Colors[3]=Alpha;
						Colors+=4;
					}
				}
			}
		}

		void WinBitmap::GenerateTransAlpha(COLORREF Color, BYTE Alpha)
		{
			if(CanBuildAlphaChannel() && !FAlphaChannelBuilt)
			{
				COLORREF A=Alpha<<24;
				for(vint i=0;i<FHeight;i++)
				{
					COLORREF* Colors=(COLORREF*)FScanLines[i];
					vint j=FWidth;
					while(j--)
					{
						COLORREF Dest=*Colors & 0x00FFFFFF;
						*Colors = Dest | (A * (Dest!=Color));
						Colors++;
					}
				}
			}
		}

		void WinBitmap::GenerateLuminance()
		{
			if(CanBuildAlphaChannel() && !FAlphaChannelBuilt)
			{
				for(vint i=0;i<FHeight;i++)
				{
					COLORREF* Colors=(COLORREF*)FScanLines[i];
					vint j=FWidth;
					while(j--)
					{
						COLORREF Dest=*Colors & 0x00FFFFFF;
						*Colors = Dest | ((GetRValue(Dest)*77 + GetGValue(Dest)*151 + GetBValue(Dest)*28) & 0x0000FF00)<<16;
						Colors++;
					}
				}
			}
		}

		void WinBitmap::GenerateGrayLevel()
		{
			if(CanBuildAlphaChannel() && !FAlphaChannelBuilt)
			{
				for(vint i=0;i<FHeight;i++)
				{
					COLORREF* Colors=(COLORREF*)FScanLines[i];
					vint j=FWidth;
					while(j--)
					{
						COLORREF Dest=*Colors & 0x00FFFFFF;
						*Colors = Dest | ((GetRValue(Dest)+GetGValue(Dest)+GetBValue(Dest))/3)<<24;
						Colors++;
					}
				}
			}
		}

		void WinBitmap::Generate(BYTE(*Function)(COLORREF))
		{
			if(CanBuildAlphaChannel() && !FAlphaChannelBuilt)
			{
				for(vint i=0;i<FHeight;i++)
				{
					COLORREF* Colors=(COLORREF*)FScanLines[i];
					vint j=FWidth;
					while(j--)
					{
						COLORREF Dest= *Colors & 0x00FFFFFF;
						*Colors = Dest | Function(Dest)<<24;
						Colors++;
					}
				}
			}
		}
			
/*********************************************************************************************************
WinBrush
*********************************************************************************************************/

		WinBrush::WinBrush()
		{
			FDIBMemory=0;
			LOGBRUSH lb;
			lb.lbColor=0;
			lb.lbStyle=BS_NULL;
			lb.lbHatch=0;
			FHandle=CreateBrushIndirect(&lb);
		}

		WinBrush::WinBrush(COLORREF Color)
		{
			FDIBMemory=0;
			FHandle=CreateSolidBrush(Color);
		}

		WinBrush::WinBrush(vint Hatch, COLORREF Color)
		{
			FDIBMemory=0;
			FHandle=CreateHatchBrush((int)Hatch, Color);
		}

		WinBrush::WinBrush(WinBitmap::Ptr DIB)
		{
			WinBitmap Temp(DIB->GetWidth(), DIB->GetHeight(), WinBitmap::vbb24Bits, true);
			Temp.GetWinDC()->Draw(0, 0, DIB);
			vint HeaderSize=sizeof(BITMAPINFOHEADER);
			FDIBMemory=new unsigned char[HeaderSize+Temp.GetHeight()*Temp.GetLineBytes()];
			Temp.FillCompatibleHeader((BITMAPINFOHEADER*)FDIBMemory);
			memcpy(FDIBMemory+HeaderSize, Temp.GetScanLines()[0], Temp.GetHeight()*Temp.GetLineBytes());

			FHandle=CreateDIBPatternBrushPt(FDIBMemory, DIB_RGB_COLORS);
			DWORD Error=GetLastError();
		}

		WinBrush::~WinBrush()
		{
			DeleteObject(FHandle);
			if(FDIBMemory)
			{
				delete[] FDIBMemory;
			}
		}

		HBRUSH WinBrush::GetHandle()
		{
			return FHandle;
		}

/*********************************************************************************************************
WinPen
*********************************************************************************************************/

		WinPen::WinPen(vint Style, vint Width, COLORREF Color)
		{
			FDIBMemory=0;
			FHandle=CreatePen((int)Style, (int)Width, (int)Color);
		}

		WinPen::WinPen(vint Style, vint EndCap, vint Join, vint Width, COLORREF Color, DWORD styleCount, const DWORD* styleArray)
		{
			FDIBMemory=0;
			LOGBRUSH Brush;
			Brush.lbColor=Color;
			Brush.lbStyle=BS_SOLID;
			Brush.lbHatch=0;
			FHandle=ExtCreatePen((int)(PS_GEOMETRIC|Style|EndCap|Join), (int)Width, &Brush, styleCount, styleArray);
		}

		WinPen::WinPen(vint Style, vint EndCap, vint Join, vint Hatch, vint Width, COLORREF Color)
		{
			FDIBMemory=0;
			LOGBRUSH Brush;
			Brush.lbColor=Color;
			Brush.lbStyle=BS_HATCHED;
			Brush.lbHatch=Hatch;
			FHandle=ExtCreatePen((int)(PS_GEOMETRIC|Style|EndCap|Join), (int)Width, &Brush, NULL, NULL);
		}

		WinPen::WinPen(WinBitmap::Ptr DIB, vint Style, vint EndCap, vint Join, vint Width)
		{
			WinBitmap Temp(DIB->GetWidth(), DIB->GetHeight(), WinBitmap::vbb24Bits, true);
			Temp.GetWinDC()->Draw(0, 0, DIB);
			vint HeaderSize=sizeof(BITMAPINFOHEADER);
			FDIBMemory=new unsigned char[HeaderSize+Temp.GetHeight()*Temp.GetLineBytes()];
			Temp.FillCompatibleHeader((BITMAPINFOHEADER*)FDIBMemory);
			memcpy(FDIBMemory+HeaderSize, Temp.GetScanLines()[0], Temp.GetHeight()*Temp.GetLineBytes());
		
			LOGBRUSH Brush;
			Brush.lbColor=RGB(0, 0, 0);
			Brush.lbStyle=BS_DIBPATTERNPT;
			Brush.lbHatch=(ULONG_PTR)FDIBMemory;
			FHandle=ExtCreatePen((int)(PS_GEOMETRIC|Style|EndCap|Join), (int)Width, &Brush, NULL, NULL);
		}

		WinPen::~WinPen()
		{
			DeleteObject(FHandle);
			if(FDIBMemory)
			{
				delete[] FDIBMemory;
			}
		}

		HPEN WinPen::GetHandle()
		{
			return FHandle;
		}

/*********************************************************************************************************
WinFont
*********************************************************************************************************/

		WinFont::WinFont(WString Name, vint Height, vint Width, vint Escapement, vint Orientation, vint Weight, bool Italic, bool Underline, bool StrikeOut, bool Antialise)
		{
			FFontInfo.lfHeight=(int)Height;
			FFontInfo.lfWidth=(int)Width;
			FFontInfo.lfEscapement=(int)Escapement;
			FFontInfo.lfOrientation=(int)Orientation;
			FFontInfo.lfWeight=(int)Weight;
			FFontInfo.lfItalic=Italic?TRUE:FALSE;
			FFontInfo.lfUnderline=Underline?TRUE:FALSE;
			FFontInfo.lfStrikeOut=StrikeOut?TRUE:FALSE;
			FFontInfo.lfCharSet=DEFAULT_CHARSET;
			FFontInfo.lfOutPrecision=OUT_DEFAULT_PRECIS;
			FFontInfo.lfClipPrecision=CLIP_DEFAULT_PRECIS;
			FFontInfo.lfQuality=Antialise?CLEARTYPE_QUALITY:NONANTIALIASED_QUALITY;
			FFontInfo.lfPitchAndFamily=DEFAULT_PITCH | FF_DONTCARE;
			wcsncpy_s(FFontInfo.lfFaceName, sizeof(FFontInfo.lfFaceName)/sizeof(*FFontInfo.lfFaceName), Name.Buffer(), LF_FACESIZE-1);
			FHandle=CreateFontIndirect(&FFontInfo);
		}

		WinFont::WinFont(LOGFONT* FontInfo)
		{
			FFontInfo=*FontInfo;
			FHandle=CreateFontIndirect(&FFontInfo);
		}

		WinFont::~WinFont()
		{
			DeleteObject(FHandle);
		}

		HFONT WinFont::GetHandle()
		{
			return FHandle;
		}

		LOGFONT* WinFont::GetInfo()
		{
			return &FFontInfo;
		}

/*********************************************************************************************************
IWinResourceService
*********************************************************************************************************/

		WinBrush::Ptr CreateDefaultBrush()
		{
			return Ptr(new WinBrush(RGB(255, 255, 255)));
		}

		WinPen::Ptr CreateDefaultPen()
		{
			return Ptr(new WinPen(PS_SOLID, 0, RGB(0, 0, 0)));
		}

		WinFont::Ptr CreateDefaultFont()
		{
			NONCLIENTMETRICS NonClientMetrics;
			NonClientMetrics.cbSize=sizeof(NONCLIENTMETRICS);
			SystemParametersInfo(SPI_GETNONCLIENTMETRICS, NonClientMetrics.cbSize, &NonClientMetrics, 0);
			if(!*NonClientMetrics.lfMessageFont.lfFaceName)
			{
				NonClientMetrics.cbSize=sizeof(NONCLIENTMETRICS)-sizeof(NonClientMetrics.iPaddedBorderWidth);
				SystemParametersInfo(SPI_GETNONCLIENTMETRICS, NonClientMetrics.cbSize, &NonClientMetrics, 0);
			}
			return Ptr(new WinFont(&NonClientMetrics.lfMessageFont));
		}

		class DefaultResourceService : public Object, public IWinResourceService
		{
		public:
			static IWinResourceService* _DefaultResourceService;

			WinPen::Ptr GetDefaultPen()
			{
				return CreateDefaultPen();
			}

			WinBrush::Ptr GetDefaultBrush()
			{
				return CreateDefaultBrush();
			}

			WinFont::Ptr GetDefaultFont()
			{
				return CreateDefaultFont();
			}
		} _DRS;
		IWinResourceService* DefaultResourceService::_DefaultResourceService=&_DRS;

		IWinResourceService* GetDefaultResourceService()
		{
			return DefaultResourceService::_DefaultResourceService;
		}

		void SetDefaultResourceService(IWinResourceService* Service)
		{
			DefaultResourceService::_DefaultResourceService=Service;
		}

/*********************************************************************************************************
WinDC
*********************************************************************************************************/

		void WinDC::Init()
		{
			FPen=GetDefaultResourceService()->GetDefaultPen();
			FOldPen=(HPEN)SelectObject(FHandle, FPen->GetHandle());

			FBrush=GetDefaultResourceService()->GetDefaultBrush();
			FOldBrush=(HBRUSH)SelectObject(FHandle, FBrush->GetHandle());

			FFont=GetDefaultResourceService()->GetDefaultFont();
			FOldFont=(HFONT)SelectObject(FHandle, FFont->GetHandle());

			SetGraphicsMode(FHandle, GM_ADVANCED);
		}

		WinDC::WinDC()
		{
			FHandle=0;
			FOldPen=0;
			FOldBrush=0;
			FOldFont=0;
		}

		WinDC::~WinDC()
		{
			SelectObject(FHandle, FOldFont);
			SelectObject(FHandle, FOldBrush);
			SelectObject(FHandle, FOldPen);
		}

		HDC WinDC::GetHandle()
		{
			return FHandle;
		}

		WinPen::Ptr WinDC::GetPen()
		{
			return FPen;
		}

		WinBrush::Ptr WinDC::GetBrush()
		{
			return FBrush;
		}

		WinFont::Ptr WinDC::GetFont()
		{
			return FFont;
		}

		void WinDC::SetPen(WinPen::Ptr Pen)
		{
			SelectObject(FHandle, Pen->GetHandle());
			FPen=Pen;
		}

		void WinDC::SetBrush(WinBrush::Ptr Brush)
		{
			SelectObject(FHandle, Brush->GetHandle());
			FBrush=Brush;
		}

		void WinDC::SetFont(WinFont::Ptr Font)
		{
			SelectObject(FHandle, Font->GetHandle());
			FFont=Font;
		}

		COLORREF WinDC::GetBackColor()
		{
			return GetBkColor(FHandle);
		}

		void WinDC::SetBackColor(COLORREF Color)
		{
			SetBkColor(FHandle, Color);
		}

		COLORREF WinDC::GetTextColor()
		{
			return ::GetTextColor(FHandle);
		}

		void WinDC::SetTextColor(COLORREF Color)
		{
			::SetTextColor(FHandle, Color);
		}

		bool WinDC::GetBackTransparent()
		{
			return GetBkMode(FHandle)==TRANSPARENT;
		}

		void WinDC::SetBackTransparent(bool Transparent)
		{
			SetBkMode(FHandle, Transparent?TRANSPARENT:OPAQUE);
		}

		POINT WinDC::GetBrushOrigin()
		{
			POINT Point;
			GetBrushOrgEx(FHandle, &Point);
			return Point;
		}

		void WinDC::SetBrushOrigin(POINT Point)
		{
			SetBrushOrgEx(FHandle, Point.x, Point.y, NULL);
		}

		int WinDC::SetRasterOperation(int rop2)
		{
			return SetROP2(FHandle, rop2);
		}

		/*------------------------------------------------------------------------------*/

		void WinDC::DrawBuffer(vint X, vint Y, const wchar_t* Text, vint CharCount)
		{
			TextOut(FHandle, (int)X, (int)Y, Text, (int)CharCount);
		}

		void WinDC::DrawBuffer(vint X, vint Y, const wchar_t* Text, vint CharCount, vint TabWidth, vint TabOriginX)
		{
			int realTabWidth=(int)TabWidth;
			TabbedTextOut(FHandle, (int)X, (int)Y, Text, (int)CharCount, 1, &realTabWidth, (int)TabOriginX);
		}

		void WinDC::DrawBuffer(RECT Rect, const wchar_t* Text, vint CharCount, UINT Format)
		{
			DrawText(FHandle, Text, (int)CharCount, &Rect, Format);
		}

		void WinDC::DrawString(vint X, vint Y, WString Text)
		{
			DrawBuffer(X, Y, Text.Buffer(), Text.Length());
		}

		void WinDC::DrawString(vint X, vint Y, WString Text, vint TabWidth, vint TabOriginX)
		{
			DrawBuffer(X, Y, Text.Buffer(), Text.Length(), TabWidth, TabOriginX);
		}

		void WinDC::DrawString(RECT Rect, WString Text, UINT Format)
		{
			DrawBuffer(Rect, Text.Buffer(), Text.Length(), Format);
		}

		SIZE WinDC::MeasureString(WString Text, vint TabSize)
		{
			return MeasureBuffer(Text.Buffer(), Text.Length(), TabSize);
		}

		SIZE WinDC::MeasureBuffer(const wchar_t* Text, vint CharCount, vint TabSize)
		{
			SIZE Size;
			if(TabSize==-1)
			{
				GetTextExtentPoint32(FHandle, Text, (int)CharCount, &Size);
			}
			else
			{
				int realTabSize=(int)TabSize;
				DWORD Result=GetTabbedTextExtent(FHandle, Text, (int)CharCount, 1, &realTabSize);
				Size.cx=LOWORD(Result);
				Size.cy=HIWORD(Result);
			}
			return Size;
		}

		SIZE WinDC::MeasureBuffer(const wchar_t* Text, vint TabSize)
		{
			return MeasureBuffer(Text, wcslen(Text), TabSize);
		}

		SIZE WinDC::MeasureWrapLineString(WString Text, vint MaxWidth)
		{
			return MeasureWrapLineBuffer(Text.Buffer(), Text.Length(), MaxWidth);
		}

		SIZE WinDC::MeasureWrapLineBuffer(const wchar_t* Text, vint CharCount, vint MaxWidth)
		{
			SIZE size = {0};
			vint lineCount=0;
			const wchar_t* reading=Text;
			INT* dx=new INT[CharCount];
			while(*reading)
			{
				INT fit=0;
				GetTextExtentExPoint(FHandle, reading, (int)(CharCount-(reading-Text)), (int)MaxWidth, &fit, dx, &size);
				reading+=fit;
				lineCount++;
			}
			delete dx;
			size.cx=0;
			size.cy*=(int)lineCount;
			return size;
		}

		SIZE WinDC::MeasureWrapLineBuffer(const wchar_t* Text, vint MaxWidth)
		{
			return MeasureWrapLineBuffer(Text, wcslen(Text), MaxWidth);
		}

		void WinDC::FillRegion(WinRegion::Ptr Region)
		{
			FillRgn(FHandle, Region->GetHandle(), FBrush->GetHandle());
		}

		void WinDC::FrameRegion(WinRegion::Ptr Region, vint BlockWidth, vint BlockHeight)
		{
			FrameRgn(FHandle, Region->GetHandle(), FBrush->GetHandle(), (int)BlockWidth, (int)BlockHeight);
		}

		void WinDC::MoveTo(vint X, vint Y)
		{
			::MoveToEx(FHandle, (int)X, (int)Y, NULL);
		}

		void WinDC::LineTo(vint X, vint Y)
		{
			::LineTo(FHandle, (int)X, (int)Y);
		}

		void WinDC::Rectangle(vint Left, vint Top, vint Right, vint Bottom)
		{
			::Rectangle(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom);
		}

		void WinDC::Rectangle(RECT Rect)
		{
			::Rectangle(FHandle, Rect.left, Rect.top, Rect.right, Rect.bottom);
		}

		void WinDC::FocusRectangle(vint Left, vint Top, vint Right, vint Bottom)
		{
			RECT Rect;
			Rect.left=(int)Left;
			Rect.top=(int)Top;
			Rect.right=(int)Right;
			Rect.bottom=(int)Bottom;
			::DrawFocusRect(FHandle, &Rect);
		}

		void WinDC::FocusRectangle(RECT Rect)
		{
			::DrawFocusRect(FHandle, &Rect);
		}

		void WinDC::FillRect(vint Left, vint Top, vint Right, vint Bottom)
		{
			RECT Rect;
			Rect.left=(int)Left;
			Rect.top=(int)Top;
			Rect.right=(int)Right;
			Rect.bottom=(int)Bottom;
			::FillRect(FHandle, &Rect, FBrush->GetHandle());
		}

		void WinDC::FillRect(RECT Rect)
		{
			::FillRect(FHandle, &Rect, FBrush->GetHandle());
		}

		void WinDC::Ellipse(vint Left, vint Top, vint Right, vint Bottom)
		{
			::Ellipse(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom);
		}

		void WinDC::Ellipse(RECT Rect)
		{
			::Ellipse(FHandle, Rect.left, Rect.top, Rect.right, Rect.bottom);
		}

		void WinDC::RoundRect(vint Left, vint Top, vint Right, vint Bottom, vint EllipseWidth, vint EllipseHeight)
		{
			::RoundRect(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)EllipseWidth, (int)EllipseHeight);
		}

		void WinDC::RoundRect(RECT Rect, vint EllipseWidth, vint EllipseHeight)
		{
			::RoundRect(FHandle, (int)Rect.left, (int)Rect.top, (int)Rect.right, (int)Rect.bottom, (int)EllipseWidth, (int)EllipseHeight);
		}

		void WinDC::PolyLine(const POINT* Points, vint Count)
		{
			::Polyline(FHandle, Points, (int)Count);
		}

		void WinDC::PolyLineTo(const POINT* Points, vint Count)
		{
			::PolylineTo(FHandle, Points, (int)Count);
		}

		void WinDC::PolyGon(const POINT* Points, vint Count)
		{
			::Polygon(FHandle, Points, (int)Count);
		}

		void WinDC::PolyBezier(const POINT* Points, vint Count)
		{
			::PolyBezier(FHandle, Points, (int)Count);
		}

		void WinDC::PolyBezierTo(const POINT* Points, vint Count)
		{
			::PolyBezierTo(FHandle, Points, (int)Count);
		}

		void WinDC::PolyDraw(const POINT* Points, const BYTE* Actions, vint PointCount)
		{
			::PolyDraw(FHandle, Points, Actions, (int)PointCount);
		}

		void WinDC::Arc(RECT Bound, POINT Start, POINT End)
		{
			::Arc(FHandle, Bound.left, Bound.top, Bound.right, Bound.bottom, Start.x, Start.y, End.x, End.y);
		}

		void WinDC::Arc(vint Left, vint Top, vint Right, vint Bottom, vint StartX, vint StartY, vint EndX, vint EndY)
		{
			::Arc(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)StartX, (int)StartY, (int)EndX, (int)EndY);
		}

		void WinDC::ArcTo(RECT Bound, POINT Start, POINT End)
		{
			::ArcTo(FHandle, (int)Bound.left, (int)Bound.top, (int)Bound.right, (int)Bound.bottom, (int)Start.x, (int)Start.y, (int)End.x, (int)End.y);
		}

		void WinDC::ArcTo(vint Left, vint Top, vint Right, vint Bottom, vint StartX, vint StartY, vint EndX, vint EndY)
		{
			::ArcTo(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)StartX, (int)StartY, (int)EndX, (int)EndY);
		}

		void WinDC::AngleArc(vint X, vint Y, vint Radius, float StartAngle, float SweepAngle)
		{
			::AngleArc(FHandle, (int)X, (int)Y, (int)Radius, StartAngle, SweepAngle);
		}

		void WinDC::AngleArc(vint X, vint Y, vint Radius, double StartAngle, double SweepAngle)
		{
			::AngleArc(FHandle, (int)X, (int)Y, (int)Radius, (float)StartAngle, (float)SweepAngle);
		}

		void WinDC::Chord(RECT Bound, POINT Start, POINT End)
		{
			::Chord(FHandle, (int)Bound.left, (int)Bound.top, (int)Bound.right, (int)Bound.bottom, (int)Start.x, (int)Start.y, (int)End.x, (int)End.y);
		}

		void WinDC::Chord(vint Left, vint Top, vint Right, vint Bottom, vint StartX, vint StartY, vint EndX, vint EndY)
		{
			::Chord(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)StartX, (int)StartY, (int)EndX, (int)EndY);
		}

		void WinDC::Pie(RECT Bound, POINT Start, POINT End)
		{
			::Pie(FHandle, (int)Bound.left, (int)Bound.top, (int)Bound.right, (int)Bound.bottom, (int)Start.x, (int)Start.y, (int)End.x, (int)End.y);
		}

		void WinDC::Pie(vint Left, vint Top, vint Right, vint Bottom, vint StartX, vint StartY, vint EndX, vint EndY)
		{
			::Pie(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)StartX, (int)StartY, (int)EndX, (int)EndY);
		}

		void WinDC::GradientRectH(TRIVERTEX* Vertices, vint VerticesCount, GRADIENT_RECT* Rectangles, vint RectangleCount)
		{
			GradientFill(FHandle, Vertices, (int)VerticesCount, Rectangles, (int)RectangleCount, GRADIENT_FILL_RECT_H);
		}

		void WinDC::GradientRectV(TRIVERTEX* Vertices, vint VerticesCount, GRADIENT_RECT* Rectangles, vint RectangleCount)
		{
			GradientFill(FHandle, Vertices, (int)VerticesCount, Rectangles, (int)RectangleCount, GRADIENT_FILL_RECT_V);
		}

		void WinDC::GradientTriangle(TRIVERTEX* Vertices, vint VerticesCount, GRADIENT_TRIANGLE* Triangles, vint TriangleCount)
		{
			GradientFill(FHandle, Vertices, (int)VerticesCount, Triangles, (int)TriangleCount, GRADIENT_FILL_TRIANGLE);
		}

		/*------------------------------------------------------------------------------*/

		void WinDC::BeginPath()
		{
			::BeginPath(FHandle);
		}

		void WinDC::EndPath()
		{
			::EndPath(FHandle);
		}

		void WinDC::ClosePath()
		{
			::CloseFigure(FHandle);
		}

		void WinDC::WidenPath()
		{
			::WidenPath(FHandle);
		}

		void WinDC::DiscardPath()
		{
			::AbortPath(FHandle);
		}

		void WinDC::DrawPath()
		{
			::StrokePath(FHandle);
		}

		void WinDC::FillPath()
		{
			::FillPath(FHandle);
		}

		void WinDC::DrawAndFillPath()
		{
			::StrokeAndFillPath(FHandle);
		}

		WinRegion::Ptr WinDC::RegionFromPath()
		{
			return Ptr(new WinRegion(::PathToRegion(FHandle)));
		}
	
		/*------------------------------------------------------------------------------*/

		bool WinDC::PointInClip(POINT Point)
		{
			return PtVisible(FHandle, Point.x, Point.y)==TRUE;
		}

		bool WinDC::RectInClip(RECT Rect)
		{
			return RectVisible(FHandle, &Rect)==TRUE;
		}

		void WinDC::ClipPath(vint CombineMode)
		{
			SelectClipPath(FHandle, (int)CombineMode);
		}

		void WinDC::ClipRegion(WinRegion::Ptr Region)
		{
			SelectClipRgn(FHandle, Region->GetHandle());
		}

		void WinDC::RemoveClip()
		{
			SelectClipRgn(FHandle, NULL);
		}

		void WinDC::MoveClip(vint OffsetX, vint OffsetY)
		{
			OffsetClipRgn(FHandle, (int)OffsetX, (int)OffsetY);
		}

		void WinDC::CombineClip(WinRegion::Ptr Region, vint CombineMode)
		{
			ExtSelectClipRgn(FHandle, Region->GetHandle(), (int)CombineMode);
		}

		void WinDC::IntersetClipRect(RECT Rect)
		{
			::IntersectClipRect(FHandle, Rect.left, Rect.top, Rect.right, Rect.bottom);
		}

		void WinDC::ExcludeClipRect(RECT Rect)
		{
			::ExcludeClipRect(FHandle, Rect.left, Rect.top, Rect.right, Rect.bottom);
		}

		WinRegion::Ptr WinDC::GetClipRegion()
		{
			HRGN Handle=CreateRectRgn(0, 0, 1, 1);
			GetClipRgn(FHandle, Handle);
			return Ptr(new WinRegion(Handle));
		}

		RECT WinDC::GetClipBoundRect()
		{
			RECT Rect;
			GetClipBox(FHandle, &Rect);
			return Rect;
		}

		/*------------------------------------------------------------------------------*/

		WinTransform WinDC::GetTransform()
		{
			XFORM Transform;
			GetWorldTransform(FHandle, &Transform);
			return Transform;
		}

		void WinDC::SetTransform(const WinTransform& Transform)
		{
			SetWorldTransform(FHandle, Transform.GetHandle());
		}

		/*------------------------------------------------------------------------------*/

		void WinDC::Copy(vint dstX, vint dstY, vint dstW, vint dstH, WinDC* Source, vint srcX, vint srcY, DWORD DrawROP)
		{
			HDC SourceHandle=Source?Source->GetHandle():0;
			BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, SourceHandle, (int)srcX, (int)srcY, (int)DrawROP);
		}

		void WinDC::Copy(RECT dstRect, WinDC* Source, POINT srcPos, DWORD DrawROP)
		{
			HDC SourceHandle=Source?Source->GetHandle():0;
			BitBlt(FHandle, dstRect.left, dstRect.top, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top, SourceHandle, srcPos.x, srcPos.y, DrawROP);
		}

		void WinDC::Copy(vint dstX, vint dstY, vint dstW, vint dstH, WinDC* Source, vint srcX, vint srcY , vint srcW, vint srcH, DWORD DrawROP)
		{
			HDC SourceHandle=Source?Source->GetHandle():0;
			StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, SourceHandle, (int)srcX, (int)srcY, (int)srcW, (int)srcH, (int)DrawROP);
		}

		void WinDC::Copy(RECT dstRect, WinDC* Source, RECT srcRect, DWORD DrawROP)
		{
			HDC SourceHandle=Source?Source->GetHandle():0;
			StretchBlt(	FHandle		, dstRect.left, dstRect.top, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top, 
						SourceHandle, srcRect.left, srcRect.top, srcRect.right-srcRect.left, srcRect.bottom-srcRect.top, 
						DrawROP);
		}

		void WinDC::Copy(POINT UpperLeft, POINT UpperRight, POINT LowerLeft, WinDC* Source, vint srcX, vint srcY, vint srcW, vint srcH)
		{
			POINT Pt[3];
			Pt[0]=UpperLeft;
			Pt[1]=UpperRight;
			Pt[2]=LowerLeft;
			PlgBlt(FHandle, Pt, Source->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, 0, 0, 0);
		}

		void WinDC::Copy(POINT UpperLeft, POINT UpperRight, POINT LowerLeft, WinDC*Source, RECT srcRect)
		{
			POINT Pt[3];
			Pt[0]=UpperLeft;
			Pt[1]=UpperRight;
			Pt[2]=LowerLeft;
			PlgBlt(FHandle, Pt, Source->GetHandle(), srcRect.left, srcRect.top, srcRect.right-srcRect.left, srcRect.bottom-srcRect.top, 0, 0, 0);
		}

		void WinDC::CopyTrans(vint dstX, vint dstY, vint dstW, vint dstH, WinDC* Source, vint srcX, vint srcY , vint srcW, vint srcH, COLORREF Color)
		{
			TransparentBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Source->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Color);
		}

		void WinDC::CopyTrans(RECT dstRect, WinDC* Source, RECT srcRect, COLORREF Color)
		{
			TransparentBlt(	FHandle				, dstRect.left, dstRect.top, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top, 
							Source->GetHandle()	, srcRect.left, srcRect.top, srcRect.right-srcRect.left, srcRect.bottom-srcRect.top, 
							Color);
		}

		/*------------------------------------------------------------------------------*/

		void WinDC::Draw(vint dstX, vint dstY, WinMetaFile* MetaFile)
		{
			Draw(dstX, dstY, MetaFile->GetWidth(), MetaFile->GetHeight(), MetaFile);
		}

		void WinDC::Draw(POINT Pos, WinMetaFile* MetaFile)
		{
			Draw(Pos.x, Pos.y, MetaFile->GetWidth(), MetaFile->GetHeight(), MetaFile);
		}

		void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinMetaFile* MetaFile)
		{
			RECT Rect;
			Rect.left=(int)dstX;
			Rect.top=(int)dstY;
			Rect.right=(int)(dstX+dstW);
			Rect.bottom=(int)(dstY+dstH);
			Draw(Rect, MetaFile);
		}

		void WinDC::Draw(RECT Rect, WinMetaFile* MetaFile)
		{
			PlayEnhMetaFile(FHandle, MetaFile->GetHandle(), &Rect);
		}

		/*------------------------------------------------------------------------------*/

		void WinDC::Draw(vint dstX, vint dstY, WinBitmap::Ptr Bitmap)
		{
			vint dstW=Bitmap->GetWidth();
			vint dstH=Bitmap->GetHeight();
			vint srcX=0;
			vint srcY=0;
			if(!Bitmap->IsAlphaChannelBuilt())
			{
				BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, SRCCOPY);
			}
			else
			{
				vint srcW=dstW;
				vint srcH=dstH;
				BLENDFUNCTION Blend;
				Blend.BlendOp=AC_SRC_OVER;
				Blend.BlendFlags=0;
				Blend.SourceConstantAlpha=255;
				Blend.AlphaFormat=AC_SRC_ALPHA;
				AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
			}
		}

		void WinDC::Draw(POINT Pos, WinBitmap::Ptr Bitmap)
		{
			vint dstX=Pos.x;
			vint dstY=Pos.y;
			vint dstW=Bitmap->GetWidth();
			vint dstH=Bitmap->GetHeight();
			vint srcX=0;
			vint srcY=0;
			if(!Bitmap->IsAlphaChannelBuilt())
			{
				BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, SRCCOPY);
			}
			else
			{
				vint srcW=dstW;
				vint srcH=dstH;
				BLENDFUNCTION Blend;
				Blend.BlendOp=AC_SRC_OVER;
				Blend.BlendFlags=0;
				Blend.SourceConstantAlpha=255;
				Blend.AlphaFormat=AC_SRC_ALPHA;
				AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
			}
		}

		void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap)
		{
			vint srcX=0;
			vint srcY=0;
			vint srcW=Bitmap->GetWidth();
			vint srcH=Bitmap->GetHeight();
			if(!Bitmap->IsAlphaChannelBuilt())
			{
				StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, SRCCOPY);
			}
			else
			{
				BLENDFUNCTION Blend;
				Blend.BlendOp=AC_SRC_OVER;
				Blend.BlendFlags=0;
				Blend.SourceConstantAlpha=255;
				Blend.AlphaFormat=AC_SRC_ALPHA;
				AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
			}
		}

		void WinDC::Draw(RECT Rect, WinBitmap::Ptr Bitmap)
		{
			vint dstX=Rect.left;
			vint dstY=Rect.top;
			vint dstW=Rect.right-Rect.left;
			vint dstH=Rect.bottom-Rect.top;
			vint srcX=0;
			vint srcY=0;
			vint srcW=Bitmap->GetWidth();
			vint srcH=Bitmap->GetHeight();
			if(!Bitmap->IsAlphaChannelBuilt())
			{
				StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, SRCCOPY);
			}
			else
			{
				BLENDFUNCTION Blend;
				Blend.BlendOp=AC_SRC_OVER;
				Blend.BlendFlags=0;
				Blend.SourceConstantAlpha=255;
				Blend.AlphaFormat=AC_SRC_ALPHA;
				AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
			}
		}

		void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, vint srcX, vint srcY)
		{
			if(!Bitmap->IsAlphaChannelBuilt())
			{
				BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, SRCCOPY);
			}
			else
			{
				vint srcW=dstW;
				vint srcH=dstH;
				BLENDFUNCTION Blend;
				Blend.BlendOp=AC_SRC_OVER;
				Blend.BlendFlags=0;
				Blend.SourceConstantAlpha=255;
				Blend.AlphaFormat=AC_SRC_ALPHA;
				AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
			}
		}

		void WinDC::Draw(RECT Rect, WinBitmap::Ptr Bitmap, POINT Pos)
		{
			vint dstX=Rect.left;
			vint dstY=Rect.top;
			vint dstW=Rect.right-Rect.left;
			vint dstH=Rect.bottom-Rect.top;
			vint srcX=Pos.x;
			vint srcY=Pos.y;
			if(!Bitmap->IsAlphaChannelBuilt())
			{
				BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, SRCCOPY);
			}
			else
			{
				vint srcW=dstW;
				vint srcH=dstH;
				BLENDFUNCTION Blend;
				Blend.BlendOp=AC_SRC_OVER;
				Blend.BlendFlags=0;
				Blend.SourceConstantAlpha=255;
				Blend.AlphaFormat=AC_SRC_ALPHA;
				AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
			}
		}

		void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, vint srcX, vint srcY, vint srcW, vint srcH)
		{
			if(!Bitmap->IsAlphaChannelBuilt())
			{
				StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, SRCCOPY);
			}
			else
			{
				BLENDFUNCTION Blend;
				Blend.BlendOp=AC_SRC_OVER;
				Blend.BlendFlags=0;
				Blend.SourceConstantAlpha=255;
				Blend.AlphaFormat=AC_SRC_ALPHA;
				AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
			}
		}

		void WinDC::Draw(RECT dstRect, WinBitmap::Ptr Bitmap, RECT srcRect)
		{
			vint dstX=dstRect.left;
			vint dstY=dstRect.top;
			vint dstW=dstRect.right-dstRect.left;
			vint dstH=dstRect.bottom-dstRect.top;
			vint srcX=srcRect.left;
			vint srcY=srcRect.top;
			vint srcW=srcRect.right-srcRect.left;
			vint srcH=srcRect.bottom-srcRect.top;
			if(!Bitmap->IsAlphaChannelBuilt())
			{
				StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, SRCCOPY);
			}
			else
			{
				BLENDFUNCTION Blend;
				Blend.BlendOp=AC_SRC_OVER;
				Blend.BlendFlags=0;
				Blend.SourceConstantAlpha=255;
				Blend.AlphaFormat=AC_SRC_ALPHA;
				AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
			}
		}

		/*------------------------------------------------------------------------------*/

		void WinDC::Draw(vint dstX, vint dstY, WinBitmap::Ptr Bitmap, unsigned char Alpha)
		{
			vint dstW=Bitmap->GetWidth();
			vint dstH=Bitmap->GetHeight();
			vint srcX=0;
			vint srcY=0;
			vint srcW=dstW;
			vint srcH=dstH;

			BLENDFUNCTION Blend;
			Blend.BlendOp=AC_SRC_OVER;
			Blend.BlendFlags=0;
			Blend.SourceConstantAlpha=Alpha;
			Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
			AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
		}

		void WinDC::Draw(POINT Pos, WinBitmap::Ptr Bitmap, unsigned char Alpha)
		{
			vint dstX=Pos.x;
			vint dstY=Pos.y;
			vint dstW=Bitmap->GetWidth();
			vint dstH=Bitmap->GetHeight();
			vint srcX=0;
			vint srcY=0;
			vint srcW=dstW;
			vint srcH=dstH;

			BLENDFUNCTION Blend;
			Blend.BlendOp=AC_SRC_OVER;
			Blend.BlendFlags=0;
			Blend.SourceConstantAlpha=Alpha;
			Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
			AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
		}

		void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, unsigned char Alpha)
		{
			vint srcX=0;
			vint srcY=0;
			vint srcW=Bitmap->GetWidth();
			vint srcH=Bitmap->GetHeight();

			BLENDFUNCTION Blend;
			Blend.BlendOp=AC_SRC_OVER;
			Blend.BlendFlags=0;
			Blend.SourceConstantAlpha=Alpha;
			Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
			AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
		}

		void WinDC::Draw(RECT Rect, WinBitmap::Ptr Bitmap, unsigned char Alpha)
		{
			vint dstX=Rect.left;
			vint dstY=Rect.top;
			vint dstW=Rect.right-Rect.left;
			vint dstH=Rect.bottom-Rect.top;
			vint srcX=0;
			vint srcY=0;
			vint srcW=Bitmap->GetWidth();
			vint srcH=Bitmap->GetHeight();

			BLENDFUNCTION Blend;
			Blend.BlendOp=AC_SRC_OVER;
			Blend.BlendFlags=0;
			Blend.SourceConstantAlpha=Alpha;
			Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
			AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
		}

		void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, vint srcX, vint srcY, unsigned char Alpha)
		{
			vint srcW=dstW;
			vint srcH=dstH;

			BLENDFUNCTION Blend;
			Blend.BlendOp=AC_SRC_OVER;
			Blend.BlendFlags=0;
			Blend.SourceConstantAlpha=Alpha;
			Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
			AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
		}

		void WinDC::Draw(RECT Rect, WinBitmap::Ptr Bitmap, POINT Pos, unsigned char Alpha)
		{
			vint dstX=Rect.left;
			vint dstY=Rect.top;
			vint dstW=Rect.right-Rect.left;
			vint dstH=Rect.bottom-Rect.top;
			vint srcX=Pos.x;
			vint srcY=Pos.y;
			vint srcW=dstW;
			vint srcH=dstH;

			BLENDFUNCTION Blend;
			Blend.BlendOp=AC_SRC_OVER;
			Blend.BlendFlags=0;
			Blend.SourceConstantAlpha=Alpha;
			Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
			AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
		}

		void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, vint srcX, vint srcY, vint srcW, vint srcH, unsigned char Alpha)
		{
			BLENDFUNCTION Blend;
			Blend.BlendOp=AC_SRC_OVER;
			Blend.BlendFlags=0;
			Blend.SourceConstantAlpha=Alpha;
			Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
			AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
		}

		void WinDC::Draw(RECT dstRect, WinBitmap::Ptr Bitmap, RECT srcRect, unsigned char Alpha)
		{
			vint dstX=dstRect.left;
			vint dstY=dstRect.top;
			vint dstW=dstRect.right-dstRect.left;
			vint dstH=dstRect.bottom-dstRect.top;
			vint srcX=srcRect.left;
			vint srcY=srcRect.top;
			vint srcW=srcRect.right-srcRect.left;
			vint srcH=srcRect.bottom-srcRect.top;

			BLENDFUNCTION Blend;
			Blend.BlendOp=AC_SRC_OVER;
			Blend.BlendFlags=0;
			Blend.SourceConstantAlpha=Alpha;
			Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
			AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
		}

/*********************************************************************************************************
WinControlDC
*********************************************************************************************************/

		WinControlDC::WinControlDC(HWND Handle)
		{
			FControlHandle=Handle;
			FHandle=GetDC(FControlHandle);
			Init();
		}

		WinControlDC::~WinControlDC()
		{
			ReleaseDC(FControlHandle, FHandle);
		}

/*********************************************************************************************************
WinProxyDC
*********************************************************************************************************/

		WinProxyDC::WinProxyDC()
		{
			FHandle=NULL;
		}

		WinProxyDC::~WinProxyDC()
		{
		}

		void WinProxyDC::Initialize(HDC Handle)
		{
			FHandle=Handle;
			Init();
		}

/*********************************************************************************************************
WinImageDC
*********************************************************************************************************/

		WinImageDC::WinImageDC()
		{
			FHandle=CreateCompatibleDC(NULL);
			Init();
		}

		WinImageDC::~WinImageDC()
		{
			DeleteDC(FHandle);
		}

		}
	}
}


/***********************************************************************
.\GDI\WINGDIAPPLICATION.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace windows
		{
			using namespace vl::collections;

			class GdiWindowsNativeWindowListener : public Object, public INativeWindowListener
			{
			protected:
				Ptr<WinBitmap>					buffer;
				INativeWindow*					window;
				bool							needPaintAfterResize = false;

				vint DetermineBufferLength(vint minSize, vint minBound, vint maxBound, vint currentSize)
				{
					if(currentSize<minSize || currentSize>maxBound)
					{
						return minBound;
					}
					else
					{
						return currentSize;
					}
				}

				NativeSize CalculateBufferSize()
				{
					NativeSize nativeWindowSize = window->GetClientSize();
					Size windowSize(nativeWindowSize.x.value, nativeWindowSize.y.value);
					Size minBounds(windowSize.x * 5 / 4, windowSize.y * 5 / 4);
					Size maxBounds(windowSize.x * 3 / 2, windowSize.y * 3 / 2);
					Size currentSize = buffer ? Size(buffer->GetWidth(), buffer->GetHeight()) : Size(0, 0);
					vint newWidth = DetermineBufferLength(windowSize.x, minBounds.x, maxBounds.x, currentSize.x);
					vint newHeight = DetermineBufferLength(windowSize.y, minBounds.y, maxBounds.y, currentSize.y);
					return NativeSize(newWidth, newHeight);
				}

				void RebuildCanvas(NativeSize size)
				{
					if (size.x < 256)size.x = 256;
					if (size.y < 256)size.y = 256;
					if (buffer)
					{
						if (buffer->GetWidth() != size.x.value || buffer->GetHeight() != size.y.value)
						{
							buffer = 0;
							needPaintAfterResize = true;
						}
					}
					if (!buffer)
					{
						buffer = Ptr(new WinBitmap(size.x.value, size.y.value, WinBitmap::vbb32Bits, true));
						buffer->GetWinDC()->SetBackTransparent(true);
					}
				}
			public:
				GdiWindowsNativeWindowListener(INativeWindow* _window)
					:window(_window)
				{
				}

				void Moved()
				{
					RebuildCanvas(CalculateBufferSize());
				}

				void Paint()
				{
					// for the first Paint() call after Moved()
					// trigger the global timer so that all GuiGraphicsHost with needToRender=true will call Render(false)
					// so that to fill the buffer to prevent from seeing a black frame
					// but it still leave some black color since the layout may needs more than 1 frame to finish
					if (needPaintAfterResize)
					{
						needPaintAfterResize = false;
						GetWindowsNativeController()->CallbackService()->Invoker()->InvokeGlobalTimer();
					}
					IWindowsForm* form=GetWindowsForm(window);
					WinControlDC controlDC(form->GetWindowHandle());
					controlDC.Draw(0, 0, buffer);
				}

				WinDC* GetWinDC()
				{
					if(!buffer) Moved();
					return buffer->GetWinDC();
				}
			};

			class GdiWindowsNativeControllerListener : public Object, public INativeControllerListener
			{
			public:
				Dictionary<INativeWindow*, Ptr<GdiWindowsNativeWindowListener>>		nativeWindowListeners;

				~GdiWindowsNativeControllerListener()
				{
					// TODO: (enumerable) foreach on dictionary
					for (vint i = 0; i < nativeWindowListeners.Count(); i++)
					{
						auto window = nativeWindowListeners.Keys()[i];
						auto listener = nativeWindowListeners.Values()[i];
						window->UninstallListener(listener.Obj());
					}
				}

				void NativeWindowCreated(INativeWindow* window)
				{
					auto listener=Ptr(new GdiWindowsNativeWindowListener(window));
					window->InstallListener(listener.Obj());
					nativeWindowListeners.Add(window, listener);
				}

				void NativeWindowDestroying(INativeWindow* window)
				{
					auto listener=nativeWindowListeners[window];
					nativeWindowListeners.Remove(window);
					window->UninstallListener(listener.Obj());
				}
			};

			GdiWindowsNativeControllerListener* gdiListener = nullptr;

			WinDC* GetNativeWindowDC(INativeWindow* window)
			{
				vint index=gdiListener->nativeWindowListeners.Keys().IndexOf(window);
				return index==-1?0:gdiListener->nativeWindowListeners.Values().Get(index)->GetWinDC();
			}

			HDC GetNativeWindowHDC(INativeWindow* window)
			{
				WinDC* dc=GetNativeWindowDC(window);
				return dc?dc->GetHandle():NULL;
			}
		}

		namespace elements_windows_gdi
		{
/***********************************************************************
OS Supporting
***********************************************************************/

			class WinGDIApplicationGDIObjectProvider : public IWindowsGDIObjectProvider
			{
			protected:
				IMLangFontLink2*				mLangFontLink;

			public:
				WinGDIApplicationGDIObjectProvider()
					:mLangFontLink(0)
				{
					CoCreateInstance(CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, IID_IMLangFontLink2, (void**)&mLangFontLink);
				}

				~WinGDIApplicationGDIObjectProvider()
				{
					mLangFontLink->Release();
				}

				windows::WinDC* GetNativeWindowDC(INativeWindow* window)override
				{
					return vl::presentation::windows::GetNativeWindowDC(window);
				}

				IWindowsGDIRenderTarget* GetBindedRenderTarget(INativeWindow* window)override
				{
					return dynamic_cast<IWindowsGDIRenderTarget*>(vl::presentation::windows::GetWindowsForm(window)->GetGraphicsHandler());
				}

				void SetBindedRenderTarget(INativeWindow* window, IWindowsGDIRenderTarget* renderTarget)override
				{
					vl::presentation::windows::GetWindowsForm(window)->SetGraphicsHandler(renderTarget);
				}

				IWICImagingFactory* GetWICImagingFactory()override
				{
					return vl::presentation::windows::GetWICImagingFactory();
				}

				IWICBitmap* GetWICBitmap(INativeImageFrame* frame)override
				{
					return vl::presentation::windows::GetWICBitmap(frame);
				}

				IMLangFontLink2* GetMLangFontLink()override
				{
					return mLangFontLink;
				}
			};
		}
	}
}

using namespace vl;
using namespace vl::presentation;
using namespace vl::presentation::windows;
using namespace vl::presentation::elements_windows_gdi;

int SetupWindowsGDIRendererInternal(bool hosted, bool raw)
{
	InitDpiAwareness(false);
	CoInitializeEx(NULL, COINIT_MULTITHREADED);
	HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
	WinGDIApplicationGDIObjectProvider objectProvider;
	SetWindowsGDIObjectProvider(&objectProvider);
	EnableCrossKernelCrashing();

	// create controller
	GuiHostedController* hostedController = nullptr;
	StartWindowsNativeController(hInstance);
	auto nativeController = GetWindowsNativeController();
	if (hosted)
	{
		hostedController = new GuiHostedController(nativeController);
		SetNativeController(hostedController);
		SetHostedApplication(hostedController->GetHostedApplication());
	}
	else
	{
		SetNativeController(nativeController);
	}

	Ptr<INativeAutomationService> automationService;
	if (hosted)
	{
		automationService = Ptr(new WindowsAutomationServiceHosted);
	}
	else
	{
		automationService = Ptr(new WindowsAutomationService);
	}
	GetNativeServiceSubstitution()->Substitute(automationService.Obj(), false);

	{
		// install listener
		GdiWindowsNativeControllerListener listener;
		nativeController->CallbackService()->InstallListener(&listener);
		gdiListener = &listener;
		// main
		RendererMainGDI(hostedController, raw);
		// uninstall listener
		gdiListener = nullptr;
		nativeController->CallbackService()->UninstallListener(&listener);
	}

	GetNativeServiceSubstitution()->Unsubstitute(automationService.Obj());
	automationService = nullptr;

	// destroy controller
	SetNativeController(nullptr);
	if (hostedController)
	{
		SetHostedApplication(nullptr);
		delete hostedController;
	}
	StopWindowsNativeController();
	return 0;
}

int SetupWindowsGDIRenderer()
{
	return SetupWindowsGDIRendererInternal(false, false);
}

int SetupHostedWindowsGDIRenderer()
{
	return SetupWindowsGDIRendererInternal(true, false);
}

int SetupRawWindowsGDIRenderer()
{
	return SetupWindowsGDIRendererInternal(false, true);
}

/***********************************************************************
.\GDI\RENDERERS\GUIGRAPHICSLAYOUTPROVIDERWINDOWSGDI.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace elements_windows_gdi
		{
			using namespace elements;
			using namespace collections;
			using namespace windows;

/***********************************************************************
WindowsGDIParagraph
***********************************************************************/

			class WindowsGDIParagraph : public Object, public IGuiGraphicsParagraph, protected UniscribeRun::IRendererCallback
			{
			protected:
				IGuiGraphicsLayoutProvider*			provider;
				Ptr<UniscribeParagraph>				paragraph;
				WString								text;
				IWindowsGDIRenderTarget*			renderTarget;

				vint								caret;
				Color								caretColor;
				bool								caretFrontSide;
				Ptr<WinPen>							caretPen;

				WinDC*								paragraphDC;
				Point								paragraphOffset;
				IGuiGraphicsParagraphCallback*		paragraphCallback;

				void PrepareUniscribeData()
				{
					if (paragraph->BuildUniscribeData(renderTarget->GetDC()))
					{
						vint width = paragraph->lastAvailableWidth == -1 ? 65536 : paragraph->lastAvailableWidth;
						paragraph->Layout(paragraph->lastWrapLine, width, paragraph->paragraphAlignment);
					}
				}

				WinDC* GetWinDC()
				{
					return paragraphDC;
				}

				Point GetParagraphOffset()
				{
					return paragraphOffset;
				}

				IGuiGraphicsParagraphCallback* GetParagraphCallback()
				{
					return paragraphCallback;
				}
			public:
				WindowsGDIParagraph(IGuiGraphicsLayoutProvider* _provider, const WString& _text, IGuiGraphicsRenderTarget* _renderTarget, IGuiGraphicsParagraphCallback* _paragraphCallback)
					:provider(_provider)
					,text(_text)
					,renderTarget(dynamic_cast<IWindowsGDIRenderTarget*>(_renderTarget))
					,caret(-1)
					,caretFrontSide(false)
					,paragraphDC(nullptr)
					,paragraphCallback(_paragraphCallback)
				{
					paragraph=Ptr(new UniscribeParagraph);
					paragraph->paragraphText=text;

					auto fragment=Ptr(new UniscribeFragment(_text));
					fragment->fontStyle=GetWindowsNativeController()->ResourceService()->GetDefaultFont();
					paragraph->documentFragments.Add(fragment);
				}

				~WindowsGDIParagraph()
				{
					DisableCaret();
				}

				IGuiGraphicsLayoutProvider* GetProvider()override
				{
					return provider;
				}

				IGuiGraphicsRenderTarget* GetRenderTarget()override
				{
					return renderTarget;
				}

				bool GetWrapLine()override
				{
					return paragraph->lastWrapLine;
				}

				void SetWrapLine(bool value)override
				{
					if (paragraph->lastWrapLine != value)
					{
						paragraph->BuildUniscribeData(renderTarget->GetDC());
						paragraph->Layout(value, paragraph->lastAvailableWidth, paragraph->paragraphAlignment);
					}
				}

				vint GetMaxWidth()override
				{
					return paragraph->lastAvailableWidth;
				}

				void SetMaxWidth(vint value)override
				{
					if (paragraph->lastAvailableWidth != value)
					{
						paragraph->BuildUniscribeData(renderTarget->GetDC());
						paragraph->Layout(paragraph->lastWrapLine, value, paragraph->paragraphAlignment);
					}
				}

				Alignment GetParagraphAlignment()override
				{
					return paragraph->paragraphAlignment;
				}

				void SetParagraphAlignment(Alignment value)override
				{
					if (paragraph->paragraphAlignment != value)
					{
						paragraph->BuildUniscribeData(renderTarget->GetDC());
						paragraph->Layout(paragraph->lastWrapLine, paragraph->lastAvailableWidth, value);
					}
				}

				bool SetFont(vint start, vint length, const WString& value)override
				{
					if(length==0) return true;
					if(0<=start && start<text.Length() && length>=0 && 0<=start+length && start+length<=text.Length())
					{
						return paragraph->SetFont(start, length, value);
					}
					else
					{
						return false;
					}
				}

				bool SetSize(vint start, vint length, vint value)override
				{
					if(length==0) return true;
					if(0<=start && start<text.Length() && length>=0 && 0<=start+length && start+length<=text.Length())
					{
						return paragraph->SetSize(start, length, value);
					}
					else
					{
						return false;
					}
				}

				bool SetStyle(vint start, vint length, TextStyle value)override
				{
					if(length==0) return true;
					if(0<=start && start<text.Length() && length>=0 && 0<=start+length && start+length<=text.Length())
					{
						return paragraph->SetStyle(start, length, (value&Bold)!=0, (value&Italic)!=0, (value&Underline)!=0, (value&Strikeline)!=0);
					}
					else
					{
						return false;
					}
				}

				bool SetColor(vint start, vint length, Color value)override
				{
					if(length==0) return true;
					if(0<=start && start<text.Length() && length>=0 && 0<=start+length && start+length<=text.Length())
					{
						return paragraph->SetColor(start, length, value);
					}
					else
					{
						return false;
					}
				}

				bool SetBackgroundColor(vint start, vint length, Color value)override
				{
					if(length==0) return true;
					if(0<=start && start<text.Length() && length>=0 && 0<=start+length && start+length<=text.Length())
					{
						return paragraph->SetBackgroundColor(start, length, value);
					}
					else
					{
						return false;
					}
				}

				bool SetInlineObject(vint start, vint length, const InlineObjectProperties& properties)override
				{
					if(length==0) return true;
					if(0<=start && start<text.Length() && length>=0 && 0<=start+length && start+length<=text.Length())
					{
						if(paragraph->SetInlineObject(start, length, properties))
						{
							if (properties.backgroundImage)
							{
								IGuiGraphicsRenderer* renderer=properties.backgroundImage->GetRenderer();
								if(renderer)
								{
									renderer->SetRenderTarget(renderTarget);
								}
							}
							return true;
						}
					}
					return false;
				}

				bool ResetInlineObject(vint start, vint length)override
				{
					if(length==0) return true;
					if(0<=start && start<text.Length() && length>=0 && 0<=start+length && start+length<=text.Length())
					{
						if (auto inlineObject = paragraph->ResetInlineObject(start, length))
						{
							if (auto element = inlineObject.Value().backgroundImage)
							{
								auto renderer=element->GetRenderer();
								if(renderer)
								{
									renderer->SetRenderTarget(0);
								}
							}
							return true;
						}
					}
					return false;
				}

				Size GetSize()override
				{
					PrepareUniscribeData();
					return Size(
						(paragraph->lastWrapLine ? 0 : paragraph->bounds.Width()),
						paragraph->bounds.Height());
				}

				bool EnableCaret(vint _caret, Color _color, bool _frontSide)override
				{
					if (!IsValidCaret(_caret)) return false;
					if (caret != -1) DisableCaret();
					caret = _caret;
					caretColor = _color;
					caretFrontSide = _frontSide;
					caretPen = GetWindowsGDIResourceManager()->CreateGdiPen(caretColor);
					return true;
				}

				void DisableCaret()override
				{
					if (caret == -1) return;
					caret = -1;
					GetWindowsGDIResourceManager()->DestroyGdiPen(caretColor);
					caretPen = {};
				}

				bool BlinkCaret()override
				{
					if (caret == -1) return false;
					if (caretPen)
					{
						GetWindowsGDIResourceManager()->DestroyGdiPen(caretColor);
						caretPen = {};
					}
					else
					{
						caretPen = GetWindowsGDIResourceManager()->CreateGdiPen(caretColor);
					}
					return true;
				}

				bool IsCaretBoundsFromTextRun(vint caret, bool caretFrontSide)
				{
					if (paragraph->lines.Count() == 1 && paragraph->lines[0]->scriptRuns.Count() == 0) return true;
					if (!paragraph->IsValidCaret(caret)) return false;

					vint frontLine = 0;
					vint backLine = 0;
					paragraph->GetLineIndexFromTextPos(caret, frontLine, backLine);
					if (frontLine == -1 || backLine == -1) return false;

					vint lineIndex = caretFrontSide ? frontLine : backLine;
					lineIndex = lineIndex < 0 ? 0 : lineIndex > paragraph->lines.Count() - 1 ? paragraph->lines.Count() - 1 : lineIndex;
					Ptr<UniscribeLine> line = paragraph->lines[caretFrontSide ? frontLine : backLine];

					vint frontRun = -1;
					vint backRun = -1;
					paragraph->GetRunIndexFromTextPos(caret, lineIndex, frontRun, backRun);
					if (frontRun == -1 || backRun == -1) return false;

					vint runIndex = caretFrontSide ? frontRun : backRun;
					runIndex = runIndex < 0 ? 0 : runIndex > line->scriptRuns.Count() - 1 ? line->scriptRuns.Count() - 1 : runIndex;
					Ptr<UniscribeRun> run = line->scriptRuns[runIndex];

					return run.Cast<UniscribeTextRun>();
				}

				void Render(Rect bounds)override
				{
					PrepareUniscribeData();

					paragraphDC = renderTarget->GetDC();
					paragraphOffset = bounds.LeftTop();
					paragraph->Render(this, true);
					paragraph->Render(this, false);
					paragraphDC = 0;

					if (caret != -1 && caretPen)
					{
						Rect caretBounds = GetCaretBounds(caret, caretFrontSide);
						vint x = caretBounds.x1 + bounds.x1;
						vint y1 = caretBounds.y1 + bounds.y1;
						vint y2 = y1 + (vint)(caretBounds.Height() * (IsCaretBoundsFromTextRun(caret, caretFrontSide) ? 1.5 : 1));

						WinDC* dc = renderTarget->GetDC();
						dc->SetPen(caretPen);
						dc->MoveTo(x - 1, y1);
						dc->LineTo(x - 1, y2);
						dc->MoveTo(x, y1);
						dc->LineTo(x, y2);
					}
				}

				vint GetCaret(vint comparingCaret, CaretRelativePosition position, bool& preferFrontSide)override
				{
					PrepareUniscribeData();
					return paragraph->GetCaret(comparingCaret, position, preferFrontSide);
				}

				Rect GetCaretBounds(vint caret, bool frontSide)override
				{
					PrepareUniscribeData();
					return paragraph->GetCaretBounds(caret, frontSide);
				}

				vint GetCaretFromPoint(Point point)override
				{
					PrepareUniscribeData();
					return paragraph->GetCaretFromPoint(point);
				}

				Nullable<InlineObjectProperties> GetInlineObjectFromPoint(Point point, vint& start, vint& length)override
				{
					PrepareUniscribeData();
					return paragraph->GetInlineObjectFromPoint(point, start, length);
				}

				vint GetNearestCaretFromTextPos(vint textPos, bool frontSide)override
				{
					PrepareUniscribeData();
					return paragraph->GetNearestCaretFromTextPos(textPos, frontSide);
				}

				bool IsValidCaret(vint caret)override
				{
					PrepareUniscribeData();
					return paragraph->IsValidCaret(caret);
				}

				bool IsValidTextPos(vint textPos)override
				{
					PrepareUniscribeData();
					return paragraph->IsValidTextPos(textPos);
				}
			};

/***********************************************************************
WindowsGDILayoutProvider
***********************************************************************/

			Ptr<IGuiGraphicsParagraph> WindowsGDILayoutProvider::CreateParagraph(const WString& text, IGuiGraphicsRenderTarget* renderTarget, elements::IGuiGraphicsParagraphCallback* callback)
			{
				return Ptr(new WindowsGDIParagraph(this, text, renderTarget, callback));
			}
		}
	}
}

/***********************************************************************
.\GDI\RENDERERS\GUIGRAPHICSRENDERERSWINDOWSGDI.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace elements_windows_gdi
		{
			using namespace windows;
			using namespace collections;

/***********************************************************************
GuiSolidBorderElementRenderer
***********************************************************************/

			void GuiFocusRectangleElementRenderer::InitializeInternal()
			{
				auto resourceManager = GetWindowsGDIResourceManager();
				pen = resourceManager->GetFocusRectanglePen();
				brush = resourceManager->CreateGdiBrush(Color(0, 0, 0, 0));
			}

			void GuiFocusRectangleElementRenderer::FinalizeInternal()
			{
			}

			void GuiFocusRectangleElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
			{
			}

			void GuiFocusRectangleElementRenderer::Render(Rect bounds)
			{
				int originRop2 = renderTarget->GetDC()->SetRasterOperation(R2_XORPEN);
				renderTarget->GetDC()->SetPen(pen);
				renderTarget->GetDC()->SetBrush(brush);
				renderTarget->GetDC()->Rectangle(bounds.Left(), bounds.Top(), bounds.Right() - 1, bounds.Bottom() - 1);
				renderTarget->GetDC()->SetRasterOperation(originRop2);
			}

			void GuiFocusRectangleElementRenderer::OnElementStateChanged()
			{
			}

/***********************************************************************
GuiSolidBorderElementRenderer
***********************************************************************/

			void GuiSolidBorderElementRenderer::InitializeInternal()
			{
				auto resourceManager=GetWindowsGDIResourceManager();
				oldColor=element->GetColor();
				pen=resourceManager->CreateGdiPen(oldColor);
				brush=resourceManager->CreateGdiBrush(Color(0, 0, 0, 0));
			}

			void GuiSolidBorderElementRenderer::FinalizeInternal()
			{
				auto resourceManager=GetWindowsGDIResourceManager();
				resourceManager->DestroyGdiPen(oldColor);
				resourceManager->DestroyGdiBrush(Color(0, 0, 0, 0));
			}

			void GuiSolidBorderElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
			{
			}

			void GuiSolidBorderElementRenderer::Render(Rect bounds)
			{
				if(oldColor.a>0)
				{
					renderTarget->GetDC()->SetBrush(brush);
					renderTarget->GetDC()->SetPen(pen);
					auto shape = element->GetShape();

					switch(shape.shapeType)
					{
					case ElementShapeType::Rectangle:
						renderTarget->GetDC()->Rectangle(bounds.Left(), bounds.Top(), bounds.Right()-1, bounds.Bottom()-1);
						break;
					case ElementShapeType::Ellipse:
						renderTarget->GetDC()->Ellipse(bounds.Left(), bounds.Top(), bounds.Right()-1, bounds.Bottom()-1);
						break;
					case ElementShapeType::RoundRect:
						renderTarget->GetDC()->RoundRect(bounds.Left(), bounds.Top(), bounds.Right() - 1, bounds.Bottom() - 1, shape.radiusX * 2, shape.radiusY * 2);
						break;
					}
				}
			}

			void GuiSolidBorderElementRenderer::OnElementStateChanged()
			{
				Color color=element->GetColor();
				if(oldColor!=color)
				{
					auto resourceManager=GetWindowsGDIResourceManager();
					resourceManager->DestroyGdiPen(oldColor);
					oldColor=color;
					pen=resourceManager->CreateGdiPen(oldColor);
				}
			}

/***********************************************************************
Gui3DBorderElementRenderer
***********************************************************************/

			void Gui3DBorderElementRenderer::InitializeInternal()
			{
				auto resourceManager=GetWindowsGDIResourceManager();
				oldColor1=element->GetColor1();
				oldColor2=element->GetColor2();
				pen1=resourceManager->CreateGdiPen(oldColor1);
				pen2=resourceManager->CreateGdiPen(oldColor2);
			}

			void Gui3DBorderElementRenderer::FinalizeInternal()
			{
				auto resourceManager=GetWindowsGDIResourceManager();
				resourceManager->DestroyGdiPen(oldColor1);
				resourceManager->DestroyGdiPen(oldColor2);
			}

			void Gui3DBorderElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
			{
			}

			void Gui3DBorderElementRenderer::Render(Rect bounds)
			{
				if(oldColor1.a>0)
				{
					renderTarget->GetDC()->SetPen(pen1);
					renderTarget->GetDC()->MoveTo(bounds.x1, bounds.y1);
					renderTarget->GetDC()->LineTo(bounds.x2, bounds.y1);
					renderTarget->GetDC()->MoveTo(bounds.x1, bounds.y1);
					renderTarget->GetDC()->LineTo(bounds.x1, bounds.y2);
				}
				if(oldColor2.a>0)
				{
					renderTarget->GetDC()->SetPen(pen2);
					renderTarget->GetDC()->MoveTo(bounds.x2-1, bounds.y2-1);
					renderTarget->GetDC()->LineTo(bounds.x1, bounds.y2-1);
					renderTarget->GetDC()->MoveTo(bounds.x2-1, bounds.y2-1);
					renderTarget->GetDC()->LineTo(bounds.x2-1, bounds.y1);
				}
			}

			void Gui3DBorderElementRenderer::OnElementStateChanged()
			{
				Color color1=element->GetColor1();
				if(oldColor1!=color1)
				{
					auto resourceManager=GetWindowsGDIResourceManager();
					resourceManager->DestroyGdiPen(oldColor1);
					oldColor1=color1;
					pen1=resourceManager->CreateGdiPen(oldColor1);
				}

				Color color2=element->GetColor2();
				if(oldColor2!=color2)
				{
					auto resourceManager=GetWindowsGDIResourceManager();
					resourceManager->DestroyGdiPen(oldColor2);
					oldColor2=color2;
					pen2=resourceManager->CreateGdiPen(oldColor2);
				}
			}

/***********************************************************************
Gui3DSplitterElementRenderer
***********************************************************************/

			void Gui3DSplitterElementRenderer::InitializeInternal()
			{
				auto resourceManager=GetWindowsGDIResourceManager();
				oldColor1=element->GetColor1();
				oldColor2=element->GetColor2();
				pen1=resourceManager->CreateGdiPen(oldColor1);
				pen2=resourceManager->CreateGdiPen(oldColor2);
			}

			void Gui3DSplitterElementRenderer::FinalizeInternal()
			{
				auto resourceManager=GetWindowsGDIResourceManager();
				resourceManager->DestroyGdiPen(oldColor1);
				resourceManager->DestroyGdiPen(oldColor2);
			}

			void Gui3DSplitterElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
			{
			}

			void Gui3DSplitterElementRenderer::Render(Rect bounds)
			{
				Point p11, p12, p21, p22;
				switch(element->GetDirection())
				{
				case Gui3DSplitterElement::Horizontal:
					{
						vint y=bounds.y1+bounds.Height()/2-1;
						p11=Point(bounds.x1, y);
						p12=Point(bounds.x2, y);
						p21=Point(bounds.x1, y+1);
						p22=Point(bounds.x2, y+1);
					}
					break;
				case Gui3DSplitterElement::Vertical:
					{
						vint x=bounds.x1+bounds.Width()/2-1;
						p11=Point(x, bounds.y1);
						p12=Point(x, bounds.y2);
						p21=Point(x+1, bounds.y1);
						p22=Point(x+1, bounds.y2);
					}
					break;
				}
				if(oldColor1.a>0)
				{
					renderTarget->GetDC()->SetPen(pen1);
					renderTarget->GetDC()->MoveTo(p11.x, p11.y);
					renderTarget->GetDC()->LineTo(p12.x, p12.y);
				}
				if(oldColor2.a>0)
				{
					renderTarget->GetDC()->SetPen(pen2);
					renderTarget->GetDC()->MoveTo(p21.x, p21.y);
					renderTarget->GetDC()->LineTo(p22.x, p22.y);
				}
			}

			void Gui3DSplitterElementRenderer::OnElementStateChanged()
			{
				Color color1=element->GetColor1();
				if(oldColor1!=color1)
				{
					auto resourceManager=GetWindowsGDIResourceManager();
					resourceManager->DestroyGdiPen(oldColor1);
					oldColor1=color1;
					pen1=resourceManager->CreateGdiPen(oldColor1);
				}

				Color color2=element->GetColor2();
				if(oldColor2!=color2)
				{
					auto resourceManager=GetWindowsGDIResourceManager();
					resourceManager->DestroyGdiPen(oldColor2);
					oldColor2=color2;
					pen2=resourceManager->CreateGdiPen(oldColor2);
				}
			}

/***********************************************************************
GuiSolidBackgroundElementRenderer
***********************************************************************/

			void GuiSolidBackgroundElementRenderer::InitializeInternal()
			{
				auto resourceManager=GetWindowsGDIResourceManager();
				oldColor=element->GetColor();
				pen=resourceManager->CreateGdiPen(oldColor);
				brush=resourceManager->CreateGdiBrush(oldColor);
			}

			void GuiSolidBackgroundElementRenderer::FinalizeInternal()
			{
				auto resourceManager=GetWindowsGDIResourceManager();
				resourceManager->DestroyGdiPen(oldColor);
				resourceManager->DestroyGdiBrush(oldColor);
			}

			void GuiSolidBackgroundElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
			{
			}

			void GuiSolidBackgroundElementRenderer::Render(Rect bounds)
			{
				if(oldColor.a>0)
				{
					renderTarget->GetDC()->SetPen(pen);
					renderTarget->GetDC()->SetBrush(brush);
					auto shape = element->GetShape();

					switch(shape.shapeType)
					{
					case ElementShapeType::Rectangle:
						renderTarget->GetDC()->FillRect(bounds.Left(), bounds.Top(), bounds.Right(), bounds.Bottom());
						break;
					case ElementShapeType::Ellipse:
						renderTarget->GetDC()->Ellipse(bounds.Left(), bounds.Top(), bounds.Right()-1, bounds.Bottom()-1);
						break;
					case ElementShapeType::RoundRect:
						renderTarget->GetDC()->RoundRect(bounds.Left(), bounds.Top(), bounds.Right() - 1, bounds.Bottom() - 1, shape.radiusX * 2, shape.radiusY * 2);
						break;
					}
				}
			}

			void GuiSolidBackgroundElementRenderer::OnElementStateChanged()
			{
				Color color=element->GetColor();
				if(oldColor!=color)
				{
					auto resourceManager=GetWindowsGDIResourceManager();
					resourceManager->DestroyGdiPen(oldColor);
					resourceManager->DestroyGdiBrush(oldColor);
					oldColor=color;
					pen=resourceManager->CreateGdiPen(oldColor);
					brush=resourceManager->CreateGdiBrush(oldColor);
				}
			}

/***********************************************************************
GuiGradientBackgroundElementRenderer
***********************************************************************/

			void GuiGradientBackgroundElementRenderer::InitializeInternal()
			{
			}

			void GuiGradientBackgroundElementRenderer::FinalizeInternal()
			{
			}

			void GuiGradientBackgroundElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
			{
			}

			void GuiGradientBackgroundElementRenderer::Render(Rect bounds)
			{
				Color color1 = element->GetColor1();
				Color color2 = element->GetColor2();
				if (color1.a > 0 || color2.a > 0)
				{
					Ptr<WinRegion> targetRegion, oldRegion, newRegion;
					auto shape = element->GetShape();
					switch (shape.shapeType)
					{
					case ElementShapeType::Ellipse:
						targetRegion = Ptr(new WinRegion(bounds.x1, bounds.y1, bounds.x2 + 1, bounds.y2 + 1, false));
						break;
					case ElementShapeType::RoundRect:
						targetRegion = Ptr(new WinRegion(bounds.x1, bounds.y1, bounds.x2 + 1, bounds.y2 + 1, shape.radiusX * 2, shape.radiusY * 2));
						break;
					}

					if (targetRegion)
					{
						oldRegion = renderTarget->GetDC()->GetClipRegion();
						newRegion = Ptr(new WinRegion(oldRegion, targetRegion, RGN_AND));
						renderTarget->GetDC()->ClipRegion(newRegion);
					}

					switch (element->GetDirection())
					{
					case GuiGradientBackgroundElement::Horizontal:
					case GuiGradientBackgroundElement::Vertical:
						{
							TRIVERTEX vertices[2];
							GRADIENT_RECT rectangles[1];

							vertices[0].x = (int)bounds.x1;
							vertices[0].y = (int)bounds.y1;
							vertices[1].x = (int)bounds.x2;
							vertices[1].y = (int)bounds.y2;

							rectangles[0].UpperLeft = 0;
							rectangles[0].LowerRight = 1;

							vertices[0].Red = color1.r << 8;
							vertices[0].Green = color1.g << 8;
							vertices[0].Blue = color1.b << 8;
							vertices[0].Alpha = color1.a << 8;

							vertices[1].Red = color2.r << 8;
							vertices[1].Green = color2.g << 8;
							vertices[1].Blue = color2.b << 8;
							vertices[1].Alpha = color2.a << 8;

							switch (element->GetDirection())
							{
							case GuiGradientBackgroundElement::Horizontal:
								renderTarget->GetDC()->GradientRectH(vertices, sizeof(vertices) / sizeof(*vertices), rectangles, sizeof(rectangles) / sizeof(*rectangles));
								break;
							case GuiGradientBackgroundElement::Vertical:
								renderTarget->GetDC()->GradientRectV(vertices, sizeof(vertices) / sizeof(*vertices), rectangles, sizeof(rectangles) / sizeof(*rectangles));
								break;
							}
						}
						break;
					case GuiGradientBackgroundElement::Slash:
					case GuiGradientBackgroundElement::Backslash:
						{
							TRIVERTEX vertices[4];
							GRADIENT_TRIANGLE triangles[2];

							switch (element->GetDirection())
							{
							case GuiGradientBackgroundElement::Slash:
								vertices[0].x = (int)bounds.x2;
								vertices[0].y = (int)bounds.y1;
								vertices[1].x = (int)bounds.x1;
								vertices[1].y = (int)bounds.y1;
								vertices[2].x = (int)bounds.x2;
								vertices[2].y = (int)bounds.y2;
								vertices[3].x = (int)bounds.x1;
								vertices[3].y = (int)bounds.y2;
								break;
							case GuiGradientBackgroundElement::Backslash:
								vertices[0].x = (int)bounds.x1;
								vertices[0].y = (int)bounds.y1;
								vertices[1].x = (int)bounds.x1;
								vertices[1].y = (int)bounds.y2;
								vertices[2].x = (int)bounds.x2;
								vertices[2].y = (int)bounds.y1;
								vertices[3].x = (int)bounds.x2;
								vertices[3].y = (int)bounds.y2;
								break;
							}

							triangles[0].Vertex1 = 0;
							triangles[0].Vertex2 = 1;
							triangles[0].Vertex3 = 2;
							triangles[1].Vertex1 = 1;
							triangles[1].Vertex2 = 2;
							triangles[1].Vertex3 = 3;

							vertices[0].Red = color1.r << 8;
							vertices[0].Green = color1.g << 8;
							vertices[0].Blue = color1.b << 8;
							vertices[0].Alpha = color1.a << 8;

							vertices[1].Red = ((color1.r + color2.r) / 2) << 8;
							vertices[1].Green = ((color1.g + color2.g) / 2) << 8;
							vertices[1].Blue = ((color1.b + color2.b) / 2) << 8;
							vertices[1].Alpha = ((color1.a + color2.a) / 2) << 8;

							vertices[2].Red = ((color1.r + color2.r) / 2) << 8;
							vertices[2].Green = ((color1.g + color2.g) / 2) << 8;
							vertices[2].Blue = ((color1.b + color2.b) / 2) << 8;
							vertices[2].Alpha = ((color1.a + color2.a) / 2) << 8;

							vertices[3].Red = color2.r << 8;
							vertices[3].Green = color2.g << 8;
							vertices[3].Blue = color2.b << 8;
							vertices[3].Alpha = color2.a << 8;

							renderTarget->GetDC()->GradientTriangle(vertices, sizeof(vertices) / sizeof(*vertices), triangles, sizeof(triangles) / sizeof(*triangles));
						}
						break;
					}

					if (targetRegion)
					{
						renderTarget->GetDC()->ClipRegion(oldRegion);
					}
				}
			}

			void GuiGradientBackgroundElementRenderer::OnElementStateChanged()
			{
			}

/***********************************************************************
GuiSolidLabelElementRenderer
***********************************************************************/

			void GuiInnerShadowElementRenderer::InitializeInternal()
			{
			}

			void GuiInnerShadowElementRenderer::FinalizeInternal()
			{
			}

			void GuiInnerShadowElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
			{
			}

			GuiInnerShadowElementRenderer::GuiInnerShadowElementRenderer()
			{
			}

			void GuiInnerShadowElementRenderer::Render(Rect bounds)
			{
			}

			void GuiInnerShadowElementRenderer::OnElementStateChanged()
			{
			}

/***********************************************************************
GuiSolidLabelElementRenderer
***********************************************************************/

			void GuiSolidLabelElementRenderer::UpdateMinSize()
			{
				if(renderTarget)
				{
					renderTarget->GetDC()->SetFont(font);
					SIZE size={0};
					const WString& text=element->GetText();
					if(element->GetWrapLine())
					{
						if(element->GetWrapLineHeightCalculation())
						{
							if(oldMaxWidth==-1 || text.Length()==0)
							{
								size=renderTarget->GetDC()->MeasureBuffer(L" ");
							}
							else
							{
								size=renderTarget->GetDC()->MeasureWrapLineString(text, oldMaxWidth);
							}
						}
					}
					else
					{
						size=text.Length()==0
							?renderTarget->GetDC()->MeasureBuffer(L" ")
							:renderTarget->GetDC()->MeasureString(text)
							;
					}
					minSize=Size((element->GetEllipse()?0:size.cx), size.cy);
				}
				else
				{
					minSize=Size();
				}
			}

			void GuiSolidLabelElementRenderer::InitializeInternal()
			{
				auto resourceManager=GetWindowsGDIResourceManager();
				oldFont=element->GetFont();
				font=resourceManager->CreateGdiFont(oldFont);
			}

			void GuiSolidLabelElementRenderer::FinalizeInternal()
			{
				auto resourceManager=GetWindowsGDIResourceManager();
				resourceManager->DestroyGdiFont(oldFont);
			}

			void GuiSolidLabelElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
			{
				UpdateMinSize();
			}

			GuiSolidLabelElementRenderer::GuiSolidLabelElementRenderer()
				:oldMaxWidth(-1)
			{
			}

			void GuiSolidLabelElementRenderer::Render(Rect bounds)
			{
				Color color=element->GetColor();
				if(color.a>0)
				{
					renderTarget->GetDC()->SetFont(font);
					renderTarget->GetDC()->SetTextColor(RGB(color.r, color.g, color.b));

					UINT format=DT_NOPREFIX;
					RECT rect;
					rect.left=(int)bounds.Left();
					rect.top=(int)bounds.Top();
					rect.right=(int)bounds.Right();
					rect.bottom=(int)bounds.Bottom();

					if(element->GetMultiline() || element->GetWrapLine())
					{
						format|=DT_EDITCONTROL;
					}
					else
					{
						format|=DT_SINGLELINE;
						switch(element->GetVerticalAlignment())
						{
						case Alignment::Top:
							format|=DT_TOP;
							break;
						case Alignment::Center:
							format|=DT_VCENTER;
							break;
						case Alignment::Bottom:
							format|=DT_BOTTOM;
							break;
						}
					}

					switch(element->GetHorizontalAlignment())
					{
					case Alignment::Left:
						format|=DT_LEFT;
						break;
					case Alignment::Center:
						format|=DT_CENTER;
						break;
					case Alignment::Right:
						format|=DT_RIGHT;
						break;
					}

					if(element->GetWrapLine())
					{
						format|=DT_WORDBREAK;
					}
					if(element->GetEllipse())
					{
						format|=DT_END_ELLIPSIS;
					}
					renderTarget->GetDC()->DrawString(rect, element->GetText(), format);
					if(oldMaxWidth!=bounds.Width())
					{
						oldMaxWidth=bounds.Width();
						UpdateMinSize();
					}
				}
			}

			void GuiSolidLabelElementRenderer::OnElementStateChanged()
			{
				FontProperties fontProperties=element->GetFont();
				if(oldFont!=fontProperties)
				{
					auto resourceManager=GetWindowsGDIResourceManager();
					resourceManager->DestroyGdiFont(oldFont);
					oldFont=fontProperties;
					font=resourceManager->CreateGdiFont(oldFont);
				}
				UpdateMinSize();
			}

/***********************************************************************
GuiImageFrameElementRenderer
***********************************************************************/

			void GuiImageFrameElementRenderer::UpdateBitmap()
			{
				if (element->GetImage() && 0 <= element->GetFrameIndex() && element->GetFrameIndex() < element->GetImage()->GetFrameCount())
				{
					auto resourceManager = GetWindowsGDIResourceManager();
					INativeImageFrame* frame = element->GetImage()->GetFrame(element->GetFrameIndex());
					bitmap = resourceManager->GetBitmap(frame, element->GetEnabled());

					if (element->GetStretch())
					{
						minSize = Size(0, 0);
					}
					else
					{
						minSize = frame->GetSize();
					}
				}
				else
				{
					bitmap = nullptr;
					minSize = Size(0, 0);
				}
			}

			void GuiImageFrameElementRenderer::InitializeInternal()
			{
				UpdateBitmap();
			}

			void GuiImageFrameElementRenderer::FinalizeInternal()
			{
			}

			void GuiImageFrameElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
			{
			}

			GuiImageFrameElementRenderer::GuiImageFrameElementRenderer()
			{
			}

			void GuiImageFrameElementRenderer::Render(Rect bounds)
			{
				if(bitmap)
				{
					WinDC* dc=renderTarget->GetDC();
					Rect source(0, 0, minSize.x, minSize.y);
					Rect destination;
					if(element->GetStretch())
					{
						INativeImageFrame* frame=element->GetImage()->GetFrame(element->GetFrameIndex());
						source = Rect(Point(0, 0), frame->GetSize());
						destination=Rect(bounds.x1, bounds.y1, bounds.x2, bounds.y2);
					}
					else
					{
						vint x=0;
						vint y=0;
						switch(element->GetHorizontalAlignment())
						{
						case Alignment::Left:
							x=bounds.Left();
							break;
						case Alignment::Center:
							x=bounds.Left()+(bounds.Width()-minSize.x)/2;
							break;
						case Alignment::Right:
							x=bounds.Right()-minSize.x;
							break;
						}
						switch(element->GetVerticalAlignment())
						{
						case Alignment::Top:
							y=bounds.Top();
							break;
						case Alignment::Center:
							y=bounds.Top()+(bounds.Height()-minSize.y)/2;
							break;
						case Alignment::Bottom:
							y=bounds.Bottom()-minSize.y;
							break;
						}
						destination=Rect(x, y, x+minSize.x, y+minSize.y);
					}
					if(element->GetImage()->GetFormat()==INativeImage::Gif && element->GetFrameIndex()>0)
					{
						auto resourceManager=GetWindowsGDIResourceManager();
						vint max=element->GetFrameIndex();
						for(vint i=0;i<=max;i++)
						{
							Ptr<WinBitmap> frameBitmap=resourceManager->GetBitmap(element->GetImage()->GetFrame(i), element->GetEnabled());
							dc->Draw(
								destination.Left(), destination.Top(), destination.Width(), destination.Height(),
								frameBitmap,
								source.Left(), source.Top(), source.Width(), source.Height()
								);
						}
					}
					else
					{
						dc->Draw(
							destination.Left(), destination.Top(), destination.Width(), destination.Height(),
							bitmap,
							source.Left(), source.Top(), source.Width(), source.Height()
							);
					}
				}
			}

			void GuiImageFrameElementRenderer::OnElementStateChanged()
			{
				UpdateBitmap();
			}

/***********************************************************************
GuiPolygonElementRenderer
***********************************************************************/

			void GuiPolygonElementRenderer::InitializeInternal()
			{
				auto resourceManager=GetWindowsGDIResourceManager();
				pen=resourceManager->CreateGdiPen(oldPenColor);
				brush=resourceManager->CreateGdiBrush(oldBrushColor);
			}

			void GuiPolygonElementRenderer::FinalizeInternal()
			{
				auto resourceManager=GetWindowsGDIResourceManager();
				resourceManager->DestroyGdiPen(oldPenColor);
				resourceManager->DestroyGdiBrush(oldBrushColor);
			}

			void GuiPolygonElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
			{
			}

			GuiPolygonElementRenderer::GuiPolygonElementRenderer()
				:points(0)
				,pointCount(0)
				,oldPenColor(0, 0, 0, 0)
				,oldBrushColor(0, 0, 0, 0)
			{
			}

			GuiPolygonElementRenderer::~GuiPolygonElementRenderer()
			{
				if(points) delete[] points;
			}

			void GuiPolygonElementRenderer::Render(Rect bounds)
			{
				if(pointCount>=3 && (oldPenColor.a || oldBrushColor.a))
				{
					vint offsetX=(bounds.Width()-minSize.x)/2+bounds.x1;
					vint offsetY=(bounds.Height()-minSize.y)/2+bounds.y1;
					for(vint i=0;i<pointCount;i++)
					{
						points[i].x+=(int)offsetX;
						points[i].y+=(int)offsetY;
					}
					renderTarget->GetDC()->SetPen(pen);
					renderTarget->GetDC()->SetBrush(brush);
					renderTarget->GetDC()->PolyGon(points, pointCount);
					for(vint i=0;i<pointCount;i++)
					{
						points[i].x-=(int)offsetX;
						points[i].y-=(int)offsetY;
					}
				}
			}

			void GuiPolygonElementRenderer::OnElementStateChanged()
			{
				minSize=element->GetSize();
				{
					if(points)
					{
						delete[] points;
						points=0;
					}
					pointCount=element->GetPointCount();
					if(pointCount>0)
					{
						points=new POINT[pointCount];
						for(vint i=0;i<pointCount;i++)
						{
							Point p=element->GetPoint(i);
							points[i].x=(int)p.x;
							points[i].y=(int)p.y;
						}
					}
				}

				auto resourceManager=GetWindowsGDIResourceManager();
				if(oldPenColor!=element->GetBorderColor() || !pen)
				{
					resourceManager->DestroyGdiPen(oldPenColor);
					oldPenColor=element->GetBorderColor();
					pen=resourceManager->CreateGdiPen(oldPenColor);
				}
				if(oldBrushColor!=element->GetBackgroundColor() || !brush)
				{
					resourceManager->DestroyGdiPen(oldBrushColor);
					oldBrushColor=element->GetBackgroundColor();
					brush=resourceManager->CreateGdiBrush(oldBrushColor);
				}
			}

/***********************************************************************
GuiGDIElementRenderer
***********************************************************************/

			void GuiGDIElementRenderer::InitializeInternal()
			{
			}

			void GuiGDIElementRenderer::FinalizeInternal()
			{
			}

			void GuiGDIElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
			{
			}

			GuiGDIElementRenderer::GuiGDIElementRenderer()
			{
			}

			GuiGDIElementRenderer::~GuiGDIElementRenderer()
			{
			}
			
			void GuiGDIElementRenderer::Render(Rect bounds)
			{
				if(renderTarget)
				{
					renderTarget->PushClipper(bounds, element);
					if(!renderTarget->IsClipperCoverWholeTarget())
					{
						WinDC* dc=renderTarget->GetDC();
						GuiGDIElementEventArgs arguments(element, dc, bounds);
						element->Rendering.Execute(arguments);
					}
					renderTarget->PopClipper(element);
				}
			}

			void GuiGDIElementRenderer::OnElementStateChanged()
			{
			}
		}
	}
}

/***********************************************************************
.\GDI\RENDERERS\GUIGRAPHICSUNISCRIBE.CPP
***********************************************************************/

#pragma comment(lib, "usp10.lib")

namespace vl
{
	namespace presentation
	{
		namespace elements_windows_gdi
		{
			using namespace regex;

/***********************************************************************
UniscribeFragment
***********************************************************************/

			UniscribeFragment::UniscribeFragment(const WString& _text)
				:text(_text)
			{
				colors.Add(UniscribeColorRange(0, text.Length()), UniscribeColor(Color(0, 0, 0), Color(0, 0, 0, 0)));
			}

			WString UniscribeFragment::GetFingerprint()
			{
				return fontStyle.fontFamily+L"#"
					+itow(fontStyle.size)+L"#"
					+(fontStyle.bold?L"B":L"N")+L"#"
					+(fontStyle.italic?L"I":L"N")+L"#"
					+(fontStyle.underline?L"U":L"N")+L"#"
					+(fontStyle.strikeline?L"S":L"N")+L"#"
					;
			}

			void UniscribeFragment::CutColors(vint start, vint length)
			{
				vint end=start+length;
				for(vint i=colors.Count()-1;i>=0;i--)
				{
					UniscribeColorRange key=colors.Keys()[i];
					if(key.start<end && start<key.end)
					{
						UniscribeColor value=colors.Values()[i];

						vint s1=key.start;
						vint s2=key.start>start?key.start:start;
						vint s3=key.end<end?key.end:end;
						vint s4=key.end;

						colors.Remove(key);
						if(s1<s2)
						{
							colors.Add(UniscribeColorRange(s1, s2), value);
						}
						if(s2<s3)
						{
							colors.Add(UniscribeColorRange(s2, s3), value);
						}
						if(s3<s4)
						{
							colors.Add(UniscribeColorRange(s3, s4), value);
						}
					}
				}
			}

			void UniscribeFragment::UpdateOverlappedColors(vint start, vint length, Color UniscribeColor::* colorField, Color color)
			{
				vint end=start+length;
				for(vint i=colors.Count()-1;i>=0;i--)
				{
					UniscribeColorRange key=colors.Keys()[i];
					if(key.start<end && start<key.end)
					{
						UniscribeColor value=colors.Values()[i];
						value.*colorField=color;
						colors.Set(key, value);
					}
				}
			}

			void UniscribeFragment::DefragmentColors()
			{
				vint lastIndex=-1;
				UniscribeColor lastColor;
				for(vint i=colors.Count()-1;i>=-1;i--)
				{
					if(lastIndex==-1)
					{
						lastIndex=i;
						if(i!=-1)
						{
							lastColor=colors.Values()[i];
						}
					}
					else if(i==-1 || colors.Values()[i]!=lastColor)
					{
						if(lastIndex-i>0)
						{
							vint start=colors.Keys()[i+1].start;
							vint end=colors.Keys()[lastIndex].end;
							UniscribeColorRange key(start, end);

							for(vint j=lastIndex;j>i;j--)
							{
								colors.Remove(colors.Keys()[j]);
							}
							colors.Add(key, lastColor);
						}
						lastIndex=i;
						if(i!=-1)
						{
							lastColor=colors.Values()[i];
						}
					}
				}
			}

			UniscribeColor UniscribeFragment::GetCharColor(vint charIndex)
			{
				vint start=0;
				vint end=colors.Count()-1;
				while(start<=end)
				{
					vint middle=(start+end)/2;
					UniscribeColorRange key=colors.Keys()[middle];
					if(charIndex<key.start)
					{
						end=middle-1;
					}
					else if(charIndex>=key.end)
					{
						start=middle+1;
					}
					else
					{
						return colors.Values()[middle];
					}
				}
				return UniscribeColor();
			}

			Ptr<UniscribeFragment> UniscribeFragment::Copy(vint start, vint length)
			{
				vint end=start+length;
				auto fragment=Ptr(new UniscribeFragment(length==0?L"":text.Sub(start, length)));
				fragment->fontStyle=fontStyle;
				fragment->fontObject=fontObject;

				if(length!=0)
				{
					fragment->colors.Clear();
					CutColors(start, length);
					for(vint i=0;i<colors.Count();i++)
					{
						UniscribeColorRange key=colors.Keys()[i];
						if(key.start<end && start<key.end)
						{
							UniscribeColor value=colors.Values()[i];
							fragment->colors.Add(UniscribeColorRange(key.start-start, key.end-start), value);
						}
					}
				}
				return fragment;
			}

/***********************************************************************
UniscribeGlyphData
***********************************************************************/

			UniscribeGlyphData::UniscribeGlyphData()
			{
				ClearUniscribeData(0, 0);
			}

			void UniscribeGlyphData::ClearUniscribeData(vint glyphCount, vint length)
			{
				glyphs.Resize(glyphCount);
				glyphVisattrs.Resize(glyphCount);
				glyphAdvances.Resize(glyphCount);
				glyphOffsets.Resize(glyphCount);
				charCluster.Resize(length);
				memset(&runAbc, 0, sizeof(runAbc));
				memset(&sa, 0, sizeof(sa));
			}
			
			bool UniscribeGlyphData::BuildUniscribeData(WinDC* dc, SCRIPT_ITEM* scriptItem, SCRIPT_CACHE& scriptCache, const wchar_t* runText, vint length, List<vint>& breakings, List<bool>& breakingAvailabilities)
			{
				vint glyphCount=glyphs.Count();
				bool resizeGlyphData=false;
				if(glyphCount==0)
				{
					glyphCount=(vint)(1.5*length+16);
					resizeGlyphData=true;
				}
				sa=scriptItem->a;
				WinDC* dcParameter=0;
				{
					// generate shape information
					if(resizeGlyphData)
					{
						glyphs.Resize(glyphCount);
						glyphVisattrs.Resize(glyphCount);
						charCluster.Resize(length);
					}

					while(true)
					{
						int availableGlyphCount=0;
						HRESULT hr=ScriptShape(
							(dcParameter?dcParameter->GetHandle():NULL),
							&scriptCache,
							runText,
							(int)length,
							(int)glyphCount,
							&sa,
							&glyphs[0],
							&charCluster[0],
							&glyphVisattrs[0],
							&availableGlyphCount
							);
						if(hr==0)
						{
							glyphCount=availableGlyphCount;
							break;
						}
						else if(hr==E_PENDING)
						{
							dcParameter=dc;
						}
						else if(hr==E_OUTOFMEMORY)
						{
							if(resizeGlyphData)
							{
								glyphCount+=length;
							}
							else
							{
								goto BUILD_UNISCRIBE_DATA_FAILED;
							}
						}
						else if(hr==USP_E_SCRIPT_NOT_IN_FONT)
						{
							if(sa.eScript==SCRIPT_UNDEFINED)
							{
								goto BUILD_UNISCRIBE_DATA_FAILED;
							}
							else
							{
								sa.eScript=SCRIPT_UNDEFINED;
							}
						}
						else
						{
							goto BUILD_UNISCRIBE_DATA_FAILED;
						}
					}
					if(resizeGlyphData)
					{
						glyphs.Resize(glyphCount);
						glyphVisattrs.Resize(glyphCount);
					}
				}

				{
					SCRIPT_FONTPROPERTIES fp;
					memset(&fp, 0, sizeof(fp));
					fp.cBytes=sizeof(fp);
					HRESULT hr=ScriptGetFontProperties(
							(dcParameter?dcParameter->GetHandle():NULL),
							&scriptCache,
							&fp
							);
					WORD invalidGlyph=fp.wgDefault;
					if(hr!=S_OK)
					{
						invalidGlyph=0;
					}

					// generate breaking information
					breakings.Add(0);
					vint charIndex=0;
					bool lastGlyphAvailable=false;
					while(charIndex<length)
					{
						vint glyphIndex=charCluster[charIndex];
						vint nextCharIndex=charIndex;
						while(nextCharIndex<length && charCluster[nextCharIndex]==glyphIndex)
						{
							nextCharIndex++;
						}

						vint glyphCount=0;
						if(nextCharIndex==length)
						{
							glyphCount=glyphs.Count()-glyphIndex;
						}
						else
						{
							glyphCount=charCluster[nextCharIndex]-glyphIndex;
						}

						if(scriptItem->a.fRTL)
						{
							glyphCount=-glyphCount;
							glyphIndex-=glyphCount-1;
						}

						bool available=true;
						for(vint i=0;i<glyphCount;i++)
						{
							if(glyphs[i+glyphIndex]==invalidGlyph)
							{
								available=false;
							}
						}

						if(charIndex==0)
						{
							lastGlyphAvailable=available;
							breakingAvailabilities.Add(available);
						}
						else if(lastGlyphAvailable!=available)
						{
							breakings.Add(charIndex);
							lastGlyphAvailable=available;
							breakingAvailabilities.Add(available);
						}

						charIndex=nextCharIndex;
					}
				}

				if(breakings.Count()==1)
				{
					// generate place information
					WinDC* dcParameter=0;
					if(resizeGlyphData)
					{
						glyphAdvances.Resize(glyphCount);
						glyphOffsets.Resize(glyphCount);
					}
					while(true)
					{
						HRESULT hr=ScriptPlace(
							(dcParameter?dcParameter->GetHandle():NULL),
							&scriptCache,
							&glyphs[0],
							(int)glyphCount,
							&glyphVisattrs[0],
							&sa,
							&glyphAdvances[0],
							&glyphOffsets[0],
							&runAbc
							);
						if(hr==0)
						{
							break;
						}
						else if(hr==E_PENDING)
						{
							dcParameter=dc;
						}
						else
						{
							goto BUILD_UNISCRIBE_DATA_FAILED;
						}
					}
				}

				return true;
	BUILD_UNISCRIBE_DATA_FAILED:
				return false;
			}

			void UniscribeGlyphData::BuildUniscribeData(WinDC* dc, SCRIPT_ITEM* scriptItem, SCRIPT_LOGATTR* charLogattrs, const wchar_t* runText, vint length)
			{
				vint glyphCount=0;
				for(vint i=0;i<length;i++)
				{
					if(i==0 || charLogattrs[i].fCharStop)
					{
						glyphCount++;
					}
				}

				ClearUniscribeData(glyphCount, length);
				sa=scriptItem->a;
				memset(&glyphs[0], 0, sizeof(glyphs[0])*glyphs.Count());
				memset(&glyphVisattrs[0], 0, sizeof(glyphVisattrs[0])*glyphVisattrs.Count());
				memset(&glyphAdvances[0], 0, sizeof(glyphAdvances[0])*glyphAdvances.Count());
				memset(&glyphOffsets[0], 0, sizeof(glyphOffsets[0])*glyphOffsets.Count());
				memset(&charCluster[0], 0, sizeof(charCluster[0])*charCluster.Count());

				for(vint i=0;i<glyphCount;i++)
				{
					glyphs[i]=(WORD)i;
				}

				if(sa.fRTL)
				{
					vint currentGlyphCount=0;
					for(vint i=0;i<length;i++)
					{
						if(i==0 || charLogattrs[i].fCharStop)
						{
							currentGlyphCount++;
						}
						charCluster[i]=(WORD)(glyphCount-currentGlyphCount);
					}
				}
				else
				{
					vint currentGlyphCount=0;
					for(vint i=0;i<length;i++)
					{
						if(i==0 || charLogattrs[i].fCharStop)
						{
							currentGlyphCount++;
						}
						charCluster[i]=(WORD)(currentGlyphCount-1);
					}
				}

				{
					vint lastCharIndex=0;
					vint lastGlyphIndex=0;
					for(vint i=1;i<=length;i++)
					{
						if(i==0 || charLogattrs[i].fCharStop)
						{
							vint glyphLength=i-lastCharIndex;
							const wchar_t* glyphText=sa.fRTL?runText+length-lastCharIndex-glyphLength:runText+lastCharIndex;
							SIZE size=dc->MeasureBuffer(runText, glyphLength, -1);
							glyphAdvances[lastGlyphIndex]=size.cx;
							lastCharIndex=i;
							lastGlyphIndex++;
						}
					}
				}

				for(vint i=0;i<glyphCount;i++)
				{
					runAbc.abcB+=glyphAdvances[i];
				}
			}

/***********************************************************************
UniscribeItem
***********************************************************************/

			UniscribeItem::UniscribeItem()
				:startFromLine(0)
				,length(0)
				,itemText(0)
			{
			}

			UniscribeItem::~UniscribeItem()
			{
			}

			void UniscribeItem::ClearUniscribeData()
			{
				charLogattrs.Resize(0);
			}

			bool UniscribeItem::BuildUniscribeData()
			{
				// generate break information
				charLogattrs.Resize(length);

				HRESULT hr=ScriptBreak(
					itemText,
					(int)length,
					&scriptItem.a,
					&charLogattrs[0]
					);
				if(hr!=0)
				{
					goto BUILD_UNISCRIBE_DATA_FAILED;
				}

				return true;
	BUILD_UNISCRIBE_DATA_FAILED:
				ClearUniscribeData();
				return false;
			}

			bool UniscribeItem::IsRightToLeft()
			{
				return scriptItem.a.fRTL;
			}

/***********************************************************************
UniscribeRun
***********************************************************************/

			UniscribeRun::UniscribeRun()
				:documentFragment(0)
				,scriptItem(0)
				,startFromLine(0)
				,startFromFragment(0)
				,length(0)
				,runText(0)
			{
			}

			UniscribeRun::~UniscribeRun()
			{
			}

/***********************************************************************
UniscribeTextRun
***********************************************************************/

			UniscribeTextRun::UniscribeTextRun()
				:scriptCache(0)
				,advance(0)
				,needFontFallback(false)
			{
			}

			UniscribeTextRun::~UniscribeTextRun()
			{
				ClearUniscribeData();
			}

			void UniscribeTextRun::ClearUniscribeData()
			{
				if(scriptCache)
				{
					ScriptFreeCache(&scriptCache);
					scriptCache=0;
				}
				advance=0;
				needFontFallback=false;
				wholeGlyph.ClearUniscribeData(0, 0);
			}

			void UniscribeTextRun::SearchSingleGlyphCluster(vint charStart, vint& charLength, vint& cluster, vint& nextCluster)
			{
				cluster=wholeGlyph.charCluster[charStart];
				vint nextChar=charStart;
				while(nextChar<wholeGlyph.charCluster.Count())
				{
					if(wholeGlyph.charCluster[nextChar]!=cluster)
					{
						break;
					}
					nextChar++;
				}
				charLength=nextChar-charStart;
				SearchGlyphCluster(charStart, charLength, cluster, nextCluster);
			}

			void UniscribeTextRun::SearchSingleChar(vint charStart, vint& charLength, vint& cluster, vint& nextCluster)
			{
				charLength=0;
				vint startFromItem=charStart+startFromLine-scriptItem->startFromLine;
				vint currentFromItem=startFromItem;
				while(++currentFromItem<scriptItem->length)
				{
					if(scriptItem->charLogattrs[currentFromItem].fCharStop)
					{
						break;
					}
				}
				charLength=currentFromItem-startFromItem;
				SearchGlyphCluster(charStart, charLength, cluster, nextCluster);
			}

			void UniscribeTextRun::SearchGlyphCluster(vint charStart, vint charLength, vint& cluster, vint& nextCluster)
			{
				cluster=wholeGlyph.charCluster[charStart];
				if(charStart+charLength>length)
				{
					charLength=length-charStart;
				}
				if(scriptItem->IsRightToLeft())
				{
					nextCluster
						=charStart+charLength==length
						?-1
						:wholeGlyph.charCluster[charStart+charLength];
				}
				else
				{
					nextCluster
						=charStart+charLength==length
						?wholeGlyph.glyphs.Count()
						:wholeGlyph.charCluster[charStart+charLength];
				}
			}

			bool UniscribeTextRun::BuildUniscribeData(WinDC* dc, List<vint>& breakings)
			{
				ClearUniscribeData();

				dc->SetFont(documentFragment->fontObject);
				List<bool> breakingAvailabilities;
				if(!wholeGlyph.BuildUniscribeData(dc, &scriptItem->scriptItem, scriptCache, runText, length, breakings, breakingAvailabilities))
				{
					goto BUILD_UNISCRIBE_DATA_FAILED;
				}

				if(breakings.Count()==1 && !breakingAvailabilities[0])
				{
					SCRIPT_LOGATTR* charLogattrs=&scriptItem->charLogattrs[0]+startFromLine-scriptItem->startFromLine;
					wholeGlyph.BuildUniscribeData(dc, &scriptItem->scriptItem, charLogattrs, runText, length);
					needFontFallback=true;
				}
				advance=wholeGlyph.runAbc.abcA+wholeGlyph.runAbc.abcB+wholeGlyph.runAbc.abcC;

				return true;
	BUILD_UNISCRIBE_DATA_FAILED:
				ClearUniscribeData();
				return false;
			}

			vint UniscribeTextRun::SumWidth(vint charStart, vint charLength)
			{
				vint cluster=0;
				vint nextCluster=0;
				SearchGlyphCluster(charStart, charLength, cluster, nextCluster);
				vint width=0;
				if(scriptItem->IsRightToLeft())
				{
					for(vint i=cluster;i>nextCluster;i--)
					{
						width+=wholeGlyph.glyphAdvances[i];
					}
				}
				else
				{
					for(vint i=cluster;i<nextCluster;i++)
					{
						width+=wholeGlyph.glyphAdvances[i];
					}
				}
				return width;
			}

			vint UniscribeTextRun::SumHeight()
			{
				return documentFragment->fontStyle.size;
			}

			vint UniscribeTextRun::SumTextHeight()
			{
				return SumHeight();
			}

			void UniscribeTextRun::SearchForLineBreak(vint tempStart, bool wrapLine, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances)
			{
				vint width=0;
				charLength=0;
				charAdvances=0;
				for(vint i=tempStart;i<=length;)
				{
					if(i==length || scriptItem->charLogattrs[i+(startFromLine-scriptItem->startFromLine)].fSoftBreak==TRUE)
					{
						if(!wrapLine || width<=maxWidth || (firstRun && charLength==0))
						{
							charLength=i-tempStart;
							charAdvances=width;
						}
						else
						{
							return;
						}
					}
					if(i==length) break;

					vint cluster=wholeGlyph.charCluster[i];
					vint clusterLength=1;
					while(i+clusterLength<length)
					{
						if(wholeGlyph.charCluster[i+clusterLength]==cluster)
						{
							clusterLength++;
						}
						else
						{
							break;
						}
					}

					if(scriptItem->IsRightToLeft())
					{
						vint nextCluster
							=i+clusterLength==length
							?-1
							:wholeGlyph.charCluster[i+clusterLength];
						for(vint j=cluster;j>nextCluster;j--)
						{
							width+=wholeGlyph.glyphAdvances[j];
						}
					}
					else
					{
						vint nextCluster
							=i+clusterLength==length
							?wholeGlyph.glyphs.Count()
							:wholeGlyph.charCluster[i+clusterLength];
						for(vint j=cluster;j<nextCluster;j++)
						{
							width+=wholeGlyph.glyphAdvances[j];
						}
					}
					i+=clusterLength;
				}
			}

			void UniscribeTextRun::Render(IRendererCallback* callback, vint fragmentBoundsIndex, vint offsetX, vint offsetY, bool renderBackground)
			{
				auto dc = callback->GetWinDC();
				RunFragmentBounds& fragment=fragmentBounds[fragmentBoundsIndex];
				if(fragment.length==0) return;

				vint startFromFragmentBounds=0;
				vint accumulatedWidth=0;
				while(startFromFragmentBounds<fragment.length)
				{
					vint charIndex=fragment.startFromRun+startFromFragmentBounds;
					vint charLength=0;
					vint cluster=0;
					vint nextCluster=0;
					SearchSingleGlyphCluster(charIndex, charLength, cluster, nextCluster);

					vint clusterStart=0;
					vint clusterCount=0;
					if(scriptItem->IsRightToLeft())
					{
						clusterStart=nextCluster+1;
						clusterCount=cluster-nextCluster;
					}
					else
					{
						clusterStart=cluster;
						clusterCount=nextCluster-cluster;
					}

					vint clusterWidth=0;
					for(vint i=0;i<clusterCount;i++)
					{
						clusterWidth+=wholeGlyph.glyphAdvances[i+clusterStart];
					}

					vint x=0;
					if(scriptItem->IsRightToLeft())
					{
						x=fragment.bounds.x2-accumulatedWidth-clusterWidth;
					}
					else
					{
						x=fragment.bounds.x1+accumulatedWidth;
					}
					RECT rect;
					rect.left=(int)(x+offsetX);
					rect.top=(int)(fragment.bounds.Top()+offsetY);
					rect.right=(int)(rect.left+clusterWidth);
					rect.bottom=(int)(rect.top+fragment.bounds.Height()*1.5);

					UniscribeColor color=documentFragment->GetCharColor(charIndex+startFromFragment);
					if(renderBackground)
					{
						Color backgroundColor=color.backgroundColor;

						if(backgroundColor.a>0)
						{
							auto brush=Ptr(new WinBrush(RGB(backgroundColor.r, backgroundColor.g, backgroundColor.b)));
							dc->SetBrush(brush);
							dc->FillRect(rect);
						}
					}
					else
					{
						Color fontColor=color.fontColor;
						dc->SetFont(documentFragment->fontObject);
						dc->SetTextColor(RGB(fontColor.r, fontColor.g, fontColor.b));

						if(needFontFallback)
						{
							dc->DrawBuffer(rect.left, rect.top, runText+charIndex, charLength);
						}
						else
						{
							HRESULT hr=ScriptTextOut(
								dc->GetHandle(),
								&scriptCache,
								rect.left,
								rect.top,
								0,
								&rect,
								&wholeGlyph.sa,
								NULL,
								0,
								&wholeGlyph.glyphs[clusterStart],
								(int)(clusterCount),
								&wholeGlyph.glyphAdvances[clusterStart],
								NULL,
								&wholeGlyph.glyphOffsets[clusterStart]
								);
						}
					}

					startFromFragmentBounds+=charLength;
					accumulatedWidth+=clusterWidth;
				}
			}

/***********************************************************************
UniscribeEmbeddedObjectRun
***********************************************************************/

			UniscribeEmbeddedObjectRun::UniscribeEmbeddedObjectRun()
			{
			}

			UniscribeEmbeddedObjectRun::~UniscribeEmbeddedObjectRun()
			{
			}

			bool UniscribeEmbeddedObjectRun::BuildUniscribeData(WinDC* dc, List<vint>& breakings)
			{
				breakings.Add(0);
				return true;
			}

			vint UniscribeEmbeddedObjectRun::SumWidth(vint charStart, vint charLength)
			{
#define ERROR_MESSAGE_PREFIX L"vl::presentation::elements_windows_gdi::UniscribeEmbeddedObjectRun::SumWidth(...)#"
				CHECK_ERROR(charStart == 0 && charLength == length, ERROR_MESSAGE_PREFIX L"Object can't split.");
				return properties.size.x;
#undef ERROR_MESSAGE_PREFIX
			}

			vint UniscribeEmbeddedObjectRun::SumHeight()
			{
				return properties.size.y;
			}

			vint UniscribeEmbeddedObjectRun::SumTextHeight()
			{
				return 0;
			}

			void UniscribeEmbeddedObjectRun::SearchForLineBreak(vint tempStart, bool wrapLine, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances)
			{
#define ERROR_MESSAGE_PREFIX L"vl::presentation::elements_windows_gdi::UniscribeEmbeddedObjectRun::SearchForLineBreak(...)#"
				CHECK_ERROR(tempStart == 0, ERROR_MESSAGE_PREFIX L"Object can't split.");
				if (firstRun || !wrapLine || properties.size.x <= maxWidth)
				{
					charLength = length;
					charAdvances = properties.size.x;
				}
				else
				{
					charLength = 0;
					charAdvances = 0;
				}
#undef ERROR_MESSAGE_PREFIX
			}

			void UniscribeEmbeddedObjectRun::Render(IRendererCallback* callback, vint fragmentBoundsIndex, vint offsetX, vint offsetY, bool renderBackground)
			{
				auto dc = callback->GetWinDC();
				RunFragmentBounds& fragment=fragmentBounds[fragmentBoundsIndex];
				if(renderBackground)
				{
					RECT rect;
					rect.left=(int)(fragment.bounds.Left()+offsetX)-2;
					rect.top=(int)(fragment.bounds.Top()+offsetY)-2;
					rect.right=(int)(fragment.bounds.Right()+offsetX)+2;
					rect.bottom=(int)(fragment.bounds.Bottom()+offsetY)+2;

					Color backgroundColor = properties.backgroundColor;

					if(backgroundColor.a>0)
					{
						auto brush=Ptr(new WinBrush(RGB(backgroundColor.r, backgroundColor.g, backgroundColor.b)));
						dc->SetBrush(brush);
						dc->FillRect(rect);
					}
				}
				else
				{
					if (properties.backgroundImage)
					{
						Rect bounds=fragment.bounds;
						bounds.x1+=offsetX;
						bounds.x2+=offsetX;
						bounds.y1+=offsetY;
						bounds.y2+=offsetY;
						IGuiGraphicsRenderer* renderer=properties.backgroundImage->GetRenderer();
						if(renderer)
						{
							renderer->Render(bounds);
						}
					}

					if (properties.callbackId != -1)
					{
						if (auto paragraphCallback = callback->GetParagraphCallback())
						{
							auto offset = callback->GetParagraphOffset();
							vint x = fragment.bounds.x1 + offsetX - offset.x;
							vint y = fragment.bounds.y1 + offsetY - offset.y;
							auto size = paragraphCallback->OnRenderInlineObject(properties.callbackId, Rect(Point(x, y), fragment.bounds.GetSize()));
							properties.size = size;
						}
					}
				}
			}

/***********************************************************************
UniscribeVirtualLine
***********************************************************************/

			UniscribeVirtualLine::UniscribeVirtualLine()
				:startFromLine(0)
				,length(0)
				,runText(0)
				,firstRunIndex(-1)
				,firstRunBoundsIndex(-1)
				,lastRunIndex(-1)
				,lastRunBoundsIndex(-1)
			{
			}

/***********************************************************************
UniscribeLine
***********************************************************************/

			UniscribeLine::UniscribeLine()
				:startFromParagraph(0)
			{
			}

			void UniscribeLine::ClearUniscribeData()
			{
				scriptItems.Clear();
				scriptRuns.Clear();
				virtualLines.Clear();
			}

			bool UniscribeLine::BuildUniscribeData(WinDC* dc)
			{
				lineText=L"";
				ClearUniscribeData();
				vint current=0;
				List<vint> fragmentStarts;
				for (auto fragment : documentFragments)
				{
					fragmentStarts.Add(current);
					lineText+=fragment->text;
					current+=fragment->text.Length();
				}

				if(lineText!=L"")
				{
					{
						SCRIPT_DIGITSUBSTITUTE sds={0};
						ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &sds);
						SCRIPT_CONTROL sc={0};
						SCRIPT_STATE ss={0};
						ScriptApplyDigitSubstitution(&sds, &sc, &ss);

						// itemize a line
						Array<SCRIPT_ITEM> items(lineText.Length()+2);
						int scriptItemCount=0;
						HRESULT hr=ScriptItemize(
							lineText.Buffer(),
							(int)lineText.Length(),
							(int)(items.Count()-1),
							&sc,
							&ss,
							&items[0],
							&scriptItemCount
							);
						if(hr!=0)
						{
							goto BUILD_UNISCRIBE_DATA_FAILED;
						}

						items.Resize(scriptItemCount+1);
						for(vint i=0;i<scriptItemCount;i++)
						{
							SCRIPT_ITEM item=items[i];
							auto scriptItem = Ptr(new UniscribeItem);
							scriptItem->startFromLine=item.iCharPos;
							scriptItem->length=items[i+1].iCharPos-item.iCharPos;
							scriptItem->itemText=lineText.Buffer()+item.iCharPos;
							scriptItem->scriptItem=item;

							if(!scriptItem->BuildUniscribeData())
							{
								goto BUILD_UNISCRIBE_DATA_FAILED;
							}
							scriptItems.Add(scriptItem);
						}
					}
					{
						// use item and document fragment information to produce runs
						// one item is constructed by one or more runs
						// characters in each run contains the same style
						vint fragmentIndex=0;
						vint fragmentStart=0;
						for(vint i=0;i<scriptItems.Count();i++)
						{
							Ptr<UniscribeItem> scriptItem=scriptItems[i];
							vint currentStart=scriptItem->startFromLine;

							while(currentStart<scriptItem->startFromLine+scriptItem->length)
							{
								UniscribeFragment* fragment=0;
								vint itemRemainLength=scriptItem->length-(currentStart-scriptItem->startFromLine);
								vint fragmentRemainLength=0;
								while(true)
								{
									fragment=documentFragments[fragmentIndex].Obj();
									fragmentRemainLength=fragment->text.Length()-(currentStart-fragmentStart);
									if(fragmentRemainLength<=0)
									{
										fragmentStart+=fragment->text.Length();
										fragmentIndex++;
									}
									else
									{
										break;
									}
								}
								vint shortLength=itemRemainLength<fragmentRemainLength?itemRemainLength:fragmentRemainLength;
								bool skip=false;
								{
									vint elementCurrent=0;
									for (auto elementFragment : documentFragments)
									{
										vint elementLength=elementFragment->text.Length();
										if(elementFragment->inlineObjectProperties)
										{
											if(elementCurrent<=currentStart && currentStart+shortLength<=elementCurrent+elementLength)
											{
												if(elementCurrent==currentStart)
												{
													auto run=Ptr(new UniscribeEmbeddedObjectRun);
													run->documentFragment=fragment;
													run->scriptItem=scriptItem.Obj();
													run->startFromLine=currentStart;
													run->startFromFragment=currentStart-fragmentStarts[fragmentIndex];
													run->length=elementLength;
													run->runText=lineText.Buffer()+currentStart;
													run->properties=elementFragment->inlineObjectProperties.Value();
													scriptRuns.Add(run);
												}
												skip=true;
												break;
											}
										}
										elementCurrent+=elementLength;
									}
								}
								if(!skip)
								{
									auto run = Ptr(new UniscribeTextRun);
									run->documentFragment=fragment;
									run->scriptItem=scriptItem.Obj();
									run->startFromLine=currentStart;
									run->startFromFragment=currentStart-fragmentStarts[fragmentIndex];
									run->length=shortLength;
									run->runText=lineText.Buffer()+currentStart;
									scriptRuns.Add(run);
								}
								currentStart+=shortLength;
							}
						}

						// for each run, generate shape information
						vint runIndex=0;
						while(runIndex<scriptRuns.Count())
						{
							Ptr<UniscribeRun> run=scriptRuns[runIndex];
							List<vint> breakings;
							if(!run->BuildUniscribeData(dc, breakings))
							{
								goto BUILD_UNISCRIBE_DATA_FAILED;
							}
							else if(breakings.Count()>1)
							{
								if(Ptr<UniscribeTextRun> textRun=run.Cast<UniscribeTextRun>())
								{
									scriptRuns.RemoveAt(runIndex);
									for(vint i=0;i<breakings.Count();i++)
									{
										vint start=breakings[i];
										vint length=i==breakings.Count()-1?textRun->length-start:breakings[i+1]-start;

										auto newRun = Ptr(new UniscribeTextRun);
										newRun->documentFragment=run->documentFragment;
										newRun->scriptItem=run->scriptItem;
										newRun->startFromLine=start+run->startFromLine;
										newRun->startFromFragment=start+run->startFromFragment;
										newRun->length=length;
										newRun->runText=run->runText+newRun->startFromLine-run->startFromLine;
										scriptRuns.Insert(runIndex+i, newRun);
									}
									continue;
								}
							}
							runIndex++;
						}
					}
				}
				return true;
	BUILD_UNISCRIBE_DATA_FAILED:
				ClearUniscribeData();
				return false;
			}

			void UniscribeLine::Layout(bool wrapLine, vint availableWidth, Alignment alignment, vint top, vint& totalHeight)
			{
				vint cx = 0;
				vint cy = top;
				virtualLines.Clear();
				if (scriptRuns.Count() == 0)
				{
					// if this line doesn't contains any run, skip and render a blank line
					vint minHeight = documentFragments[0]->fontStyle.size;
					bounds = Rect(Point(cx, cy), Size(0, minHeight));
					cy += minHeight;
				}
				else
				{
					for (auto run : scriptRuns)
					{
						run->fragmentBounds.Clear();
					}

					// render this line into lines with auto line wrapping
					vint beginRun = 0;
					vint beginRunOffset = 0;
					vint endRun = 0;
					vint endRunOffset = 0;
					vint currentWidth = 0;

					while (beginRun < scriptRuns.Count())
					{
						vint currentWidth = 0;
						bool firstRun = true;

						// search for a range to fit in the given width
						while (endRun < scriptRuns.Count())
						{
							vint charLength = 0;
							vint charAdvances = 0;
							UniscribeRun* run = scriptRuns[endRun].Obj();
							run->SearchForLineBreak(endRunOffset, wrapLine, availableWidth - currentWidth, firstRun, charLength, charAdvances);
							firstRun = false;

							if (charLength == run->length - endRunOffset)
							{
								if (endRun == scriptRuns.Count() - 1)
								{
									endRunOffset = run->length;
									currentWidth += charAdvances;
									break;
								}
								else
								{
									endRun++;
									endRunOffset = 0;
									currentWidth += charAdvances;
								}
							}
							else
							{
								if (endRunOffset == 0 && charLength == 0)
								{
									endRun--;
									endRunOffset = scriptRuns[endRun]->length;
								}
								else
								{
									endRunOffset = endRunOffset + charLength;
									currentWidth += charAdvances;
								}
								break;
							}
						}

						// if the range is empty, than this should be the end of line, ignore it
						if (beginRun < endRun || (beginRun == endRun && beginRunOffset < endRunOffset))
						{
							// calculate the max line height in this range;
							vint maxHeight = 0;
							vint maxTextHeight = 0;
							for (vint i = beginRun; i <= endRun; i++)
							{
								{
									vint size = scriptRuns[i]->SumHeight();
									if (maxHeight < size) maxHeight = size;
								}
								{
									vint size = scriptRuns[i]->SumTextHeight();
									if (maxTextHeight < size) maxTextHeight = size;
								}
							}

							// determine the rendering order for all runs inside this range
							Array<BYTE> levels(endRun - beginRun + 1);
							Array<int> runVisualToLogical(levels.Count());
							Array<int> runLogicalToVisual(levels.Count());
							for (vint i = beginRun; i <= endRun; i++)
							{
								levels[i - beginRun] = scriptRuns[i]->scriptItem->scriptItem.a.s.uBidiLevel;
							}
							ScriptLayout((int)levels.Count(), &levels[0], &runVisualToLogical[0], &runLogicalToVisual[0]);

							// render all runs inside this range
							vint beginRunFirstFragment = scriptRuns[beginRun]->fragmentBounds.Count();
							for (vint i = 0; i < levels.Count(); i++)
							{
								vint runIndex = runVisualToLogical[i] + beginRun;
								UniscribeRun* run = scriptRuns[runIndex].Obj();
								vint start = runIndex == beginRun ? beginRunOffset : 0;
								vint end = runIndex == endRun ? endRunOffset : run->length;
								vint length = end - start;
								vint runWidth = run->SumWidth(start, length);
								vint runHeight = run->SumHeight();

								UniscribeRun::RunFragmentBounds fragmentBounds;
								fragmentBounds.startFromRun = start;
								fragmentBounds.length = length;
								fragmentBounds.bounds = Rect(
									Point(cx, cy + maxHeight - runHeight),
									Size(runWidth, runHeight)
								);
								run->fragmentBounds.Add(fragmentBounds);

								cx += runWidth;
							}

							// adjust alignment
							vint cxOffset = 0;
							switch (alignment)
							{
							case Alignment::Center:
								cxOffset = (availableWidth - cx) / 2;
								break;
							case Alignment::Right:
								cxOffset = availableWidth - cx;
								break;
							}

							// shift all bounds using alignment
							if (cxOffset != 0)
							{
								for (vint i = beginRun; i <= endRun; i++)
								{
									UniscribeRun* run = scriptRuns[i].Obj();
									for (vint j = (i == beginRun ? beginRunFirstFragment : 0); j < run->fragmentBounds.Count(); j++)
									{
										UniscribeRun::RunFragmentBounds& fragmentBounds = run->fragmentBounds[j];
										fragmentBounds.bounds.x1 += cxOffset;
										fragmentBounds.bounds.x2 += cxOffset;
									}
								}
							}

							// create a virtual line
							{
								auto virtualLine = Ptr(new UniscribeVirtualLine);
								virtualLine->firstRunIndex = beginRun;
								virtualLine->firstRunBoundsIndex = beginRunFirstFragment;
								virtualLine->lastRunIndex = endRun;
								virtualLine->lastRunBoundsIndex = scriptRuns[endRun]->fragmentBounds.Count() - 1;
								UniscribeRun* firstRun = scriptRuns[virtualLine->firstRunIndex].Obj();
								UniscribeRun* lastRun = scriptRuns[virtualLine->lastRunIndex].Obj();
								UniscribeRun::RunFragmentBounds& firstBounds = firstRun->fragmentBounds[virtualLine->firstRunBoundsIndex];
								UniscribeRun::RunFragmentBounds& lastBounds = lastRun->fragmentBounds[virtualLine->lastRunBoundsIndex];

								virtualLine->startFromLine = firstRun->startFromLine + firstBounds.startFromRun;
								virtualLine->length = lastRun->startFromLine + lastBounds.startFromRun + lastBounds.length - virtualLine->startFromLine;
								virtualLine->runText = lineText.Buffer() + virtualLine->startFromLine;

								bool updateBounds = false;
								for (vint i = beginRun; i <= endRun; i++)
								{
									UniscribeRun* run = scriptRuns[i].Obj();
									for (vint j = (i == beginRun ? beginRunFirstFragment : 0); j < run->fragmentBounds.Count(); j++)
									{
										UniscribeRun::RunFragmentBounds& fragmentBounds = run->fragmentBounds[j];
										if (updateBounds)
										{
											if (virtualLine->bounds.x1 > fragmentBounds.bounds.x1) virtualLine->bounds.x1 = fragmentBounds.bounds.x1;
											if (virtualLine->bounds.x2 < fragmentBounds.bounds.x2) virtualLine->bounds.x2 = fragmentBounds.bounds.x2;
											if (virtualLine->bounds.y1 > fragmentBounds.bounds.y1) virtualLine->bounds.y1 = fragmentBounds.bounds.y1;
											if (virtualLine->bounds.y2 < fragmentBounds.bounds.y2) virtualLine->bounds.y2 = fragmentBounds.bounds.y2;
										}
										else
										{
											virtualLine->bounds = fragmentBounds.bounds;
											updateBounds = true;
										}
									}
								}
								virtualLines.Add(virtualLine);
							}

							cx = 0;
							cy += (vint)(maxHeight + maxTextHeight * 0.5);
						}

						// prepare for the next virtual line
						if (endRunOffset == scriptRuns[endRun]->length)
						{
							endRun++;
							endRunOffset = 0;
						}
						beginRun = endRun;
						beginRunOffset = endRunOffset;
					}

					// calculate line bounds
					vint minX = 0;
					vint minY = top;
					vint maxX = 0;
					vint maxY = top;
					for (auto run : scriptRuns)
					{
						for (auto fragmentBounds : run->fragmentBounds)
						{
							Rect bounds = fragmentBounds.bounds;
							if (minX > bounds.Left()) minX = bounds.Left();
							if (minY > bounds.Top()) minY = bounds.Top();
							if (maxX < bounds.Right()) maxX = bounds.Right();
							if (maxY < bounds.Bottom()) maxY = bounds.Bottom();
						}
					}
					bounds = Rect(minX, minY, maxX, maxY);
				}
				totalHeight = cy;
			}

			void UniscribeLine::Render(UniscribeRun::IRendererCallback* callback, vint offsetX, vint offsetY, bool renderBackground)
			{
				for (auto run : scriptRuns)
				{
					for(vint i=0;i<run->fragmentBounds.Count();i++)
					{
						run->Render(callback, i, offsetX, offsetY, renderBackground);
					}
				}
			}

/***********************************************************************
UniscribeParagraph
***********************************************************************/

			UniscribeParagraph::UniscribeParagraph()
				:lastWrapLine(true)
				,lastAvailableWidth(-1)
				,paragraphAlignment(Alignment::Left)
				,built(false)
			{
			}

			UniscribeParagraph::~UniscribeParagraph()
			{
				ClearUniscribeData();
			}

/***********************************************************************
UniscribeParagraph (Initialization)
***********************************************************************/

			void UniscribeParagraph::ClearUniscribeData()
			{
				for (auto fragment : documentFragments)
				{
					GetWindowsGDIResourceManager()->DestroyGdiFont(fragment->fontStyle);
					fragment->fontObject=0;
				}
				lines.Clear();
			}

			bool UniscribeParagraph::BuildUniscribeData(WinDC* dc)
			{
				if(built) return false;

				built=true;
				ClearUniscribeData();
				Dictionary<WString, Ptr<WinFont>> fonts;
				for (auto fragment : documentFragments)
				{
					if(!fragment->fontObject)
					{
						WString fragmentFingerPrint=fragment->GetFingerprint();
						vint index=fonts.Keys().IndexOf(fragmentFingerPrint);
						if(index==-1)
						{
							fragment->fontObject=GetWindowsGDIResourceManager()->CreateGdiFont(fragment->fontStyle);
							fonts.Add(fragmentFingerPrint, fragment->fontObject);
						}
						else
						{
							fragment->fontObject=fonts.Values().Get(index);
						}
					}
				}
				{
					Regex regexLine(L"\r\n");
					Ptr<UniscribeLine> line;
					for (auto fragment : documentFragments)
					{
						if(fragment->inlineObjectProperties)
						{
							if(!line)
							{
								line=Ptr(new UniscribeLine);
								lines.Add(line);
							}
							line->documentFragments.Add(fragment);
						}
						else
						{
							RegexMatch::List textLines;
							regexLine.Split(fragment->text, true, textLines);

							for(vint i=0;i<textLines.Count();i++)
							{
								RegexString rs=textLines[i]->Result();
								WString text=rs.Value();
								if(i>0)
								{
									line=0;
								}
								if(!line)
								{
									line=Ptr(new UniscribeLine);
									lines.Add(line);
								}

								if(textLines.Count()==1)
								{
									line->documentFragments.Add(fragment);
								}
								else
								{
									Ptr<UniscribeFragment> runFragment=fragment->Copy(rs.Start(), rs.Length());
									line->documentFragments.Add(runFragment);
								}
							}
						}
					}
				}

				for (auto line : lines)
				{
					line->BuildUniscribeData(dc);
				}

				vint lineStart=0;
				for (auto line : lines)
				{
					line->startFromParagraph=lineStart;
					lineStart+=line->lineText.Length()+2;
				}

				return true;
			}

			void UniscribeParagraph::Layout(bool wrapLine, vint availableWidth, Alignment alignment)
			{
				lastWrapLine = wrapLine;
				lastAvailableWidth = availableWidth;
				paragraphAlignment = alignment;

				if (lastAvailableWidth == -1)
				{
					return;
				}

				vint cy = 0;
				for (auto line : lines)
				{
					line->Layout(wrapLine, availableWidth, alignment, cy, cy);
				}

				// calculate paragraph bounds
				vint minX = 0;
				vint minY = 0;
				vint maxX = 0;
				vint maxY = 0;
				for (auto line : lines)
				{
					Rect bounds = line->bounds;
					if (minX > bounds.Left()) minX = bounds.Left();
					if (minY > bounds.Top()) minY = bounds.Top();
					if (maxX < bounds.Right()) maxX = bounds.Right();
					if (maxY < bounds.Bottom()) maxY = bounds.Bottom();
				}

				vint offsetY = 0;
				for (auto line : lines)
				{
					for (auto fragment : line->documentFragments)
					{
						vint size = fragment->fontStyle.size / 3;
						if (size > offsetY)
						{
							offsetY = size;
						}
					}
				}
				bounds = Rect(minX, minY, maxX, maxY + offsetY);
			}

			void UniscribeParagraph::Render(UniscribeRun::IRendererCallback* callback, bool renderBackground)
			{
				auto offset = callback->GetParagraphOffset();
				for (auto line : lines)
				{
					line->Render(callback, offset.x, offset.y, renderBackground);
				}
			}

/***********************************************************************
UniscribeParagraph (Formatting Helper)
***********************************************************************/

			void UniscribeParagraph::SearchFragment(vint start, vint length, vint& fs, vint& ss, vint& fe, vint& se)
			{
				fs=-1;
				ss=-1;
				fe=-1;
				se=-1;
				vint current=0;
				for(vint i=0;i<documentFragments.Count();i++)
				{
					vint fragmentLength=documentFragments[i]->text.Length();
					if(current<=start && start<current+fragmentLength)
					{
						fs=i;
						ss=start-current;
					}
					if(current<=start+length && start+length<=current+fragmentLength)
					{
						fe=i;
						se=start+length-current;
					}
					if(fs!=-1 && fe!=-1)
					{
						break;
					}
					current+=fragmentLength;
				}
			}

			bool UniscribeParagraph::CutFragment(vint fs, vint ss, vint fe, vint se, vint& f1, vint& f2)
			{
				f1=-1;
				f2=-1;
				if(fs==fe)
				{
					Ptr<UniscribeFragment> fragment=documentFragments[fs];
					if(fragment->inlineObjectProperties)
					{
						if(ss==0 && se==fragment->text.Length())
						{
							f1=f2=fs;
							return true;
						}
						else
						{
							return false;
						}
					}
				}
				for(vint i=fs;i<=fe;i++)
				{
					if(documentFragments[i]->inlineObjectProperties)
					{
						return false;
					}
				}
				if(fs==fe)
				{
					Ptr<UniscribeFragment> fragment=documentFragments[fs];
					vint length=fragment->text.Length();
					if(ss==0)
					{
						if(se==length)
						{
							f1=f2=fs;
						}
						else
						{
							f1=f2=fs;
							Ptr<UniscribeFragment> leftFragment=fragment->Copy(0, se);
							Ptr<UniscribeFragment> rightFragment=fragment->Copy(se, length-se);
							documentFragments.RemoveAt(fs);
							documentFragments.Insert(fs, leftFragment);
							documentFragments.Insert(fs+1, rightFragment);
						}
					}
					else
					{
						if(se==length)
						{
							f1=fs+1;
							f2=fs+1;
							Ptr<UniscribeFragment> leftFragment=fragment->Copy(0, ss);
							Ptr<UniscribeFragment> rightFragment=fragment->Copy(ss, length-ss);
							documentFragments.RemoveAt(fs);
							documentFragments.Insert(fs, leftFragment);
							documentFragments.Insert(fs+1, rightFragment);
						}
						else
						{
							f1=fs+1;
							f2=fs+1;
							Ptr<UniscribeFragment> leftFragment=fragment->Copy(0, ss);
							Ptr<UniscribeFragment> middleFragment=fragment->Copy(ss, se-ss);
							Ptr<UniscribeFragment> rightFragment=fragment->Copy(se, length-se);
							documentFragments.RemoveAt(fs);
							documentFragments.Insert(fs, leftFragment);
							documentFragments.Insert(fs+1, middleFragment);
							documentFragments.Insert(fs+2, rightFragment);
						}
					}
				}
				else
				{
					Ptr<UniscribeFragment> fragmentStart=documentFragments[fs];
					Ptr<UniscribeFragment> fragmentEnd=documentFragments[fe];
					if(ss==0)
					{
						f1=fs;
					}
					else
					{
						f1=fs+1;
						fe++;
						vint length=fragmentStart->text.Length();
						Ptr<UniscribeFragment> leftFragment=fragmentStart->Copy(0, ss);
						Ptr<UniscribeFragment> rightFragment=fragmentStart->Copy(ss, length-ss);
						documentFragments.RemoveAt(fs);
						documentFragments.Insert(fs, leftFragment);
						documentFragments.Insert(fs+1, rightFragment);
					}
					if(se==fragmentEnd->text.Length())
					{
						f2=fe;
					}
					else
					{
						f2=fe;
						fe++;
						vint length=fragmentEnd->text.Length();
						Ptr<UniscribeFragment> leftFragment= fragmentEnd->Copy(0, se);
						Ptr<UniscribeFragment> rightFragment=fragmentEnd->Copy(se, length-se);
						documentFragments.RemoveAt(fe-1);
						documentFragments.Insert(fe-1, leftFragment);
						documentFragments.Insert(fe, rightFragment);
					}
				}
				return true;
			}

			void UniscribeParagraph::CutFragmentColors(vint fs, vint ss, vint fe, vint se, Color UniscribeColor::* colorField, Color color)
			{
				for(vint f=fs;f<=fe;f++)
				{
					Ptr<UniscribeFragment> fragment=documentFragments[f];
					vint start=f==fs?ss:0;
					vint end=f==fe?se:fragment->text.Length();

					fragment->CutColors(start, end-start);
					fragment->UpdateOverlappedColors(start, end-start, colorField, color);
					fragment->DefragmentColors();
				}
			}

/***********************************************************************
UniscribeParagraph (Formatting)
***********************************************************************/

			bool UniscribeParagraph::SetFont(vint start, vint length, const WString& value)
			{
				vint fs, ss, fe, se, f1, f2;
				SearchFragment(start, length, fs, ss, fe, se);
				if(CutFragment(fs, ss, fe, se, f1, f2))
				{
					for(vint i=f1;i<=f2;i++)
					{
						documentFragments[i]->fontStyle.fontFamily=value;
					}
					built=false;
					return true;
				}
				else
				{
					return false;
				}
			}

			bool UniscribeParagraph::SetSize(vint start, vint length, vint value)
			{
				vint fs, ss, fe, se, f1, f2;
				SearchFragment(start, length, fs, ss, fe, se);
				if(CutFragment(fs, ss, fe, se, f1, f2))
				{
					for(vint i=f1;i<=f2;i++)
					{
						documentFragments[i]->fontStyle.size=value;
					}
					built=false;
					return true;
				}
				else
				{
					return false;
				}
			}

			bool UniscribeParagraph::SetStyle(vint start, vint length, bool bold, bool italic, bool underline, bool strikeline)
			{
				vint fs, ss, fe, se, f1, f2;
				SearchFragment(start, length, fs, ss, fe, se);
				if(CutFragment(fs, ss, fe, se, f1, f2))
				{
					for(vint i=f1;i<=f2;i++)
					{
						documentFragments[i]->fontStyle.bold=bold;
						documentFragments[i]->fontStyle.italic=italic;
						documentFragments[i]->fontStyle.underline=underline;
						documentFragments[i]->fontStyle.strikeline=strikeline;
					}
					built=false;
					return true;
				}
				else
				{
					return false;
				}
			}

			bool UniscribeParagraph::SetColor(vint start, vint length, Color value)
			{
				vint fs, ss, fe, se;
				SearchFragment(start, length, fs, ss, fe, se);
				if(fs==-1 || ss==-1 || fe==-1 || se==-1) return false;

				CutFragmentColors(fs, ss, fe, se, &UniscribeColor::fontColor, value);
				return true;
			}

			bool UniscribeParagraph::SetBackgroundColor(vint start, vint length, Color value)
			{
				vint fs, ss, fe, se;
				SearchFragment(start, length, fs, ss, fe, se);
				if(fs==-1 || ss==-1 || fe==-1 || se==-1) return false;

				CutFragmentColors(fs, ss, fe, se, &UniscribeColor::backgroundColor, value);
				return true;
			}

			bool UniscribeParagraph::SetInlineObject(vint start, vint length, const IGuiGraphicsParagraph::InlineObjectProperties& properties)
			{
				vint fs, ss, fe, se, f1, f2;
				SearchFragment(start, length, fs, ss, fe, se);
				if(CutFragment(fs, ss, fe, se, f1, f2))
				{
					WString text;
					for(vint i=f1;i<=f2;i++)
					{
						text+=documentFragments[f1]->text;
					}
					auto elementFragment=Ptr(new UniscribeFragment(text));

					for(vint i=f1;i<=f2;i++)
					{
						elementFragment->cachedTextFragment.Add(documentFragments[f1]);
						documentFragments.RemoveAt(f1);
					}
					elementFragment->inlineObjectProperties=properties;
					documentFragments.Insert(f1, elementFragment);
					built=false;
					return true;
				}
				else
				{
					return false;
				}
			}

			InlineObject UniscribeParagraph::ResetInlineObject(vint start, vint length)
			{
				vint fs, ss, fe, se;
				SearchFragment(start, length, fs, ss, fe, se);
				Ptr<UniscribeFragment> fragment = documentFragments[fs];
				if(fs==fe && ss==0 && se==fragment->text.Length() && fragment->inlineObjectProperties)
				{
					documentFragments.RemoveAt(fs);
					for(vint i=0;i<fragment->cachedTextFragment.Count();i++)
					{
						documentFragments.Insert(fs+i, fragment->cachedTextFragment[i]);
					}
					built=false;
					return fragment->inlineObjectProperties;
				}
				return InlineObject();
			}

/***********************************************************************
UniscribeParagraph (Caret Helper)
***********************************************************************/

			void UniscribeParagraph::GetLineIndexFromTextPos(vint textPos, vint& frontLine, vint& backLine)
			{
				frontLine=-1;
				backLine=-1;
				if(!IsValidTextPos(textPos)) return;

				vint start=0;
				vint end=lines.Count()-1;
				while(start<=end)
				{
					vint middle=(start+end)/2;
					Ptr<UniscribeLine> line=lines[middle];
					vint lineStart=line->startFromParagraph;
					vint lineEnd=line->startFromParagraph+line->lineText.Length();
					if(textPos<lineStart)
					{
						if(textPos==lineStart-1)
						{
							frontLine=middle-1;
							backLine=middle;
							return;
						}
						else
						{
							end=middle-1;
						}
					}
					else if(textPos>lineEnd)
					{
						if(textPos==lineEnd+1)
						{
							frontLine=middle;
							backLine=middle+1;
							return;
						}
						else
						{
							start=middle+1;
						}
					}
					else
					{
						frontLine=middle;
						backLine=middle;
						return;
					}
				}
			}

			void UniscribeParagraph::GetVirtualLineIndexFromTextPos(vint textPos, vint lineIndex, vint& frontLine, vint& backLine)
			{
				frontLine=-1;
				backLine=-1;
				if(!IsValidTextPos(textPos)) return;
				if(lineIndex<0 || lineIndex>=lines.Count()) return;

				Ptr<UniscribeLine> line=lines[lineIndex];
				vint start=0;
				vint end=line->virtualLines.Count()-1;
				while(start<=end)
				{
					vint middle=(start+end)/2;
					Ptr<UniscribeVirtualLine> vline=line->virtualLines[middle];
					vint lineStart=line->startFromParagraph+vline->startFromLine;
					vint lineEnd=line->startFromParagraph+vline->startFromLine+vline->length;
					if(textPos<lineStart)
					{
						end=middle-1;
					}
					else if(textPos>lineEnd)
					{
						start=middle+1;
					}
					else if(textPos==lineStart)
					{
						frontLine=middle==0?0:middle-1;
						backLine=middle;
						return;
					}
					else if(textPos==lineEnd)
					{
						frontLine=middle;
						backLine=middle==line->virtualLines.Count()-1?middle:middle+1;
						return;
					}
					else
					{
						frontLine=middle;
						backLine=middle;
						return;
					}
				}
			}

			void UniscribeParagraph::GetItemIndexFromTextPos(vint textPos, vint lineIndex, vint& frontItem, vint& backItem)
			{
				frontItem=-1;
				backItem=-1;
				if(!IsValidTextPos(textPos)) return;
				if(lineIndex<0 || lineIndex>=lines.Count()) return;

				Ptr<UniscribeLine> line=lines[lineIndex];
				vint start=0;
				vint end=line->scriptItems.Count()-1;
				while(start<=end)
				{
					vint middle=(start+end)/2;
					Ptr<UniscribeItem> item=line->scriptItems[middle];
					vint lineStart=line->startFromParagraph+item->startFromLine;
					vint lineEnd=line->startFromParagraph+item->startFromLine+item->length;
					if(textPos<lineStart)
					{
						end=middle-1;
					}
					else if(textPos>lineEnd)
					{
						start=middle+1;
					}
					else if(textPos==lineStart)
					{
						frontItem=middle==0?0:middle-1;
						backItem=middle;
						return;
					}
					else if(textPos==lineEnd)
					{
						frontItem=middle;
						backItem=middle==line->scriptItems.Count()-1?middle:middle+1;
						return;
					}
					else
					{
						frontItem=middle;
						backItem=middle;
						return;
					}
				}
			}

			void UniscribeParagraph::GetRunIndexFromTextPos(vint textPos, vint lineIndex, vint& frontRun, vint& backRun)
			{
				frontRun = -1;
				backRun = -1;
				if (!IsValidTextPos(textPos)) return;
				if (lineIndex < 0 || lineIndex >= lines.Count()) return;

				Ptr<UniscribeLine> line = lines[lineIndex];
				vint start = 0;
				vint end = line->scriptRuns.Count() - 1;
				while (start <= end)
				{
					vint middle = (start + end) / 2;
					Ptr<UniscribeRun> run = line->scriptRuns[middle];
					vint lineStart = line->startFromParagraph + run->startFromLine;
					vint lineEnd = line->startFromParagraph + run->startFromLine + run->length;
					if (textPos < lineStart)
					{
						end = middle - 1;
					}
					else if (textPos > lineEnd)
					{
						start = middle + 1;
					}
					else if (textPos == lineStart)
					{
						frontRun = middle == 0 ? 0 : middle - 1;
						backRun = middle;
						return;
					}
					else if (textPos == lineEnd)
					{
						frontRun = middle;
						backRun = middle == line->scriptItems.Count() - 1 ? middle : middle + 1;
						return;
					}
					else
					{
						frontRun = middle;
						backRun = middle;
						return;
					}
				}
			}

			Rect UniscribeParagraph::GetCaretBoundsWithLine(vint caret, vint lineIndex, vint virtualLineIndex, bool frontSide)
			{
				Ptr<UniscribeLine> line = lines[lineIndex];
				if (line->startFromParagraph <= caret && caret <= line->startFromParagraph + line->lineText.Length())
				{
					if (line->lineText == L"") return line->bounds;
					Ptr<UniscribeVirtualLine> virtualLine = line->virtualLines[virtualLineIndex];

					for (vint i = virtualLine->firstRunIndex; i <= virtualLine->lastRunIndex; i++)
					{
						Ptr<UniscribeRun> run = line->scriptRuns[i];
						vint firstBounds = i == virtualLine->firstRunIndex ? virtualLine->firstRunBoundsIndex : 0;
						vint lastBounds = i == virtualLine->lastRunIndex ? virtualLine->lastRunBoundsIndex : run->fragmentBounds.Count() - 1;

						for (vint j = firstBounds; j <= lastBounds; j++)
						{
							UniscribeRun::RunFragmentBounds& bounds = run->fragmentBounds[j];
							vint boundsStart = line->startFromParagraph + run->startFromLine + bounds.startFromRun;
							if (boundsStart == caret)
							{
								if (!frontSide || i == virtualLine->firstRunIndex && j == virtualLine->firstRunBoundsIndex)
								{
									if (run->scriptItem->scriptItem.a.fRTL)
									{
										return Rect(bounds.bounds.x2, bounds.bounds.y1, bounds.bounds.x2, bounds.bounds.y2);
									}
									else
									{
										return Rect(bounds.bounds.x1, bounds.bounds.y1, bounds.bounds.x1, bounds.bounds.y2);
									}
								}
							}
							else if (caret == boundsStart + bounds.length)
							{
								if (frontSide || i == virtualLine->lastRunIndex && j == virtualLine->lastRunBoundsIndex)
								{
									if (run->scriptItem->scriptItem.a.fRTL)
									{
										return Rect(bounds.bounds.x1, bounds.bounds.y1, bounds.bounds.x1, bounds.bounds.y2);
									}
									else
									{
										return Rect(bounds.bounds.x2, bounds.bounds.y1, bounds.bounds.x2, bounds.bounds.y2);
									}
								}
							}
							else if (boundsStart < caret && caret < boundsStart + bounds.length)
							{
								if (Ptr<UniscribeTextRun> textRun = run.Cast<UniscribeTextRun>())
								{
									vint accumulatedWidth = 0;
									vint lastRunChar = bounds.startFromRun;
									for (vint i = 0; i <= bounds.length; i++)
									{
										vint charIndex = bounds.startFromRun + i;
										vint newLastRunChar = lastRunChar;
										if (i > 0)
										{
											if (i == bounds.length)
											{
												newLastRunChar = charIndex;
											}
											else
											{
												WORD cluster1 = textRun->wholeGlyph.charCluster[charIndex - 1];
												WORD cluster2 = textRun->wholeGlyph.charCluster[charIndex];
												if (cluster1 != cluster2)
												{
													newLastRunChar = charIndex;
												}
											}
										}

										if (newLastRunChar != lastRunChar)
										{
											WORD glyph1 = 0;
											WORD glyph2 = 0;
											if (run->scriptItem->scriptItem.a.fRTL)
											{
												glyph2 = textRun->wholeGlyph.charCluster[lastRunChar] + 1;
												glyph1 = newLastRunChar == run->length ? 0 : textRun->wholeGlyph.charCluster[newLastRunChar] + 1;
											}
											else
											{
												glyph1 = textRun->wholeGlyph.charCluster[lastRunChar];
												glyph2 = newLastRunChar == run->length ? (WORD)textRun->wholeGlyph.glyphs.Count() : textRun->wholeGlyph.charCluster[newLastRunChar];
											}

											vint glyphWidth = 0;
											for (WORD g = glyph1; g < glyph2; g++)
											{
												glyphWidth += textRun->wholeGlyph.glyphAdvances[g];
											}
											accumulatedWidth += glyphWidth;
											lastRunChar = newLastRunChar;

											if (line->startFromParagraph + run->startFromLine + lastRunChar == caret)
											{
												vint x = 0;
												if (run->scriptItem->scriptItem.a.fRTL)
												{
													x = bounds.bounds.x2 - accumulatedWidth;
												}
												else
												{
													x = bounds.bounds.x1 + accumulatedWidth;
												}
												return Rect(x, bounds.bounds.y1, x, bounds.bounds.y2);
											}
										}
									}
								}
								else
								{
									if (frontSide == !run->scriptItem->scriptItem.a.fRTL)
									{
										return Rect(bounds.bounds.x1, bounds.bounds.y1, bounds.bounds.x1, bounds.bounds.y2);
									}
									else
									{
										return Rect(bounds.bounds.x2, bounds.bounds.y1, bounds.bounds.x2, bounds.bounds.y2);
									}
								}
							}
						}
					}
				}
				return Rect();
			}

			vint UniscribeParagraph::GetCaretFromXWithTextRunBounds(vint x, vint lineIndex, vint runIndex, vint runBoundsIndex)
			{
				Ptr<UniscribeLine> line=lines[lineIndex];
				if(line->lineText==L"") return line->startFromParagraph;
				Ptr<UniscribeTextRun> run=line->scriptRuns[runIndex].Cast<UniscribeTextRun>();
				UniscribeRun::RunFragmentBounds& bounds=run->fragmentBounds[runBoundsIndex];
				
				vint startFromFragmentBounds=0;
				vint accumulatedWidth=0;
				while(startFromFragmentBounds<bounds.length)
				{
					vint charIndex=bounds.startFromRun+startFromFragmentBounds;
					vint charLength=0;
					vint cluster=0;
					vint nextCluster=0;
					run->SearchSingleChar(charIndex, charLength, cluster, nextCluster);

					vint clusterStart=0;
					vint clusterCount=0;
					if(run->scriptItem->IsRightToLeft())
					{
						clusterStart=nextCluster+1;
						clusterCount=cluster-nextCluster;
					}
					else
					{
						clusterStart=cluster;
						clusterCount=nextCluster-cluster;
					}

					vint clusterWidth=0;
					for(vint i=0;i<clusterCount;i++)
					{
						clusterWidth+=run->wholeGlyph.glyphAdvances[i+clusterStart];
					}

					if(run->scriptItem->scriptItem.a.fRTL)
					{
						vint x2=bounds.bounds.x2-accumulatedWidth;
						vint x1=x2-clusterWidth;
						if(x1<=x && x<x2)
						{
							vint d1=x-x1;
							vint d2=x2-x;
							if(d1<=d2)
							{
								return line->startFromParagraph+run->startFromLine+charIndex+charLength;
							}
							else
							{
								return line->startFromParagraph+run->startFromLine+charIndex;
							}
						}
					}
					else
					{
						vint x1=bounds.bounds.x1+accumulatedWidth;
						vint x2=x1+clusterWidth;
						if(x1<=x && x<x2)
						{
							vint d1=x-x1;
							vint d2=x2-x;
							if(d1<=d2)
							{
								return line->startFromParagraph+run->startFromLine+charIndex;
							}
							else
							{
								return line->startFromParagraph+run->startFromLine+charIndex+charLength;
							}
						}
					}

					startFromFragmentBounds+=charLength;
					accumulatedWidth+=clusterWidth;
				}
				return -1;
			}

			vint UniscribeParagraph::GetCaretFromXWithLine(vint x, vint lineIndex, vint virtualLineIndex)
			{
				Ptr<UniscribeLine> line=lines[lineIndex];
				if(line->virtualLines.Count()==0) return line->startFromParagraph;
				Ptr<UniscribeVirtualLine> virtualLine=line->virtualLines[virtualLineIndex];
				if(x<virtualLine->bounds.x1) return line->startFromParagraph+virtualLine->startFromLine;
				if(x>=virtualLine->bounds.x2) return line->startFromParagraph+virtualLine->startFromLine+virtualLine->length;

				for(vint i=virtualLine->firstRunIndex;i<=virtualLine->lastRunIndex;i++)
				{
					Ptr<UniscribeRun> run=line->scriptRuns[i];
					if(Ptr<UniscribeTextRun> textRun=run.Cast<UniscribeTextRun>())
					{
						vint firstBounds=i==virtualLine->firstRunIndex?virtualLine->firstRunBoundsIndex:0;
						vint lastBounds=i==virtualLine->lastRunIndex?virtualLine->lastRunBoundsIndex:run->fragmentBounds.Count()-1;
					
						for(vint j=firstBounds;j<=lastBounds;j++)
						{
							UniscribeRun::RunFragmentBounds& bounds=run->fragmentBounds[j];
							if(bounds.bounds.x1<=x && x<bounds.bounds.x2)
							{
								return GetCaretFromXWithTextRunBounds(x, lineIndex, i, j);
							}
						}
					}
					else
					{
						Rect bounds=run->fragmentBounds[0].bounds;
						if(bounds.x1<=x && x<bounds.x2)
						{
							vint d1=x-bounds.x1;
							vint d2=bounds.x2-x;
							if(d1<=d2)
							{
								return line->startFromParagraph+run->startFromLine;
							}
							else
							{
								return line->startFromParagraph+run->startFromLine+run->length;
							}
						}
					}
				}
				return -1;
			}

			InlineObject UniscribeParagraph::GetInlineObjectFromXWithLine(vint x, vint lineIndex, vint virtualLineIndex, vint& start, vint& length)
			{
				Ptr<UniscribeLine> line=lines[lineIndex];
				if(line->virtualLines.Count()==0) return InlineObject();
				Ptr<UniscribeVirtualLine> virtualLine=line->virtualLines[virtualLineIndex];
				if(x<virtualLine->bounds.x1) return InlineObject();
				if(x>=virtualLine->bounds.x2) return InlineObject();

				for(vint i=virtualLine->firstRunIndex;i<=virtualLine->lastRunIndex;i++)
				{
					Ptr<UniscribeRun> run=line->scriptRuns[i];
					if(auto elementRun=run.Cast<UniscribeEmbeddedObjectRun>())
					{
						Rect bounds=run->fragmentBounds[0].bounds;
						if(bounds.x1<=x && x<bounds.x2)
						{
							start=line->startFromParagraph+elementRun->startFromLine;
							length=elementRun->length;
							return elementRun->properties;
						}
					}
				}
				return InlineObject();
			}

			vint UniscribeParagraph::GetLineY(vint lineIndex)
			{
				if(lineIndex==lines.Count())
				{
					return bounds.Height();
				}
				else
				{
					return lines[lineIndex]->bounds.y1;
				}
			}

			vint UniscribeParagraph::GetVirtualLineY(vint lineIndex, vint virtualLineIndex)
			{
				Ptr<UniscribeLine> line=lines[lineIndex];
				if(virtualLineIndex==line->virtualLines.Count())
				{
					return GetLineY(lineIndex+1);
				}
				else
				{
					return line->virtualLines[virtualLineIndex]->bounds.y1;
				}
			}

			vint UniscribeParagraph::GetLineIndexFromY(vint y)
			{
				if(y<lines[0]->bounds.y1) return 0;
				if(y>=lines[lines.Count()-1]->bounds.y2) return lines.Count()-1;

				vint start=0;
				vint end=lines.Count()-1;
				while(start<=end)
				{
					vint middle=(start+end)/2;
					vint minY=GetLineY(middle);
					vint maxY=GetLineY(middle+1);
					if(y<minY)
					{
						end=middle-1;
					}
					else if(y>=maxY)
					{
						start=middle+1;
					}
					else
					{
						return middle;
					}
				}
				return -1;
			}

			vint UniscribeParagraph::GetVirtualLineIndexFromY(vint y, vint lineIndex)
			{
				Ptr<UniscribeLine> line=lines[lineIndex];
				if(y<line->bounds.y1) return 0;
				if(y>=line->bounds.y2) return line->virtualLines.Count()-1;

				vint start=0;
				vint end=line->virtualLines.Count()-1;
				while(start<=end)
				{
					vint middle=(start+end)/2;
					vint minY=GetVirtualLineY(lineIndex, middle);
					vint maxY=GetVirtualLineY(lineIndex, middle+1);
					if(y<minY)
					{
						end=middle-1;
					}
					else if(y>=maxY)
					{
						start=middle+1;
					}
					else
					{
						return middle;
					}
				}
				return -1;
			}

/***********************************************************************
UniscribeParagraph (Caret)
***********************************************************************/

			vint UniscribeParagraph::GetCaret(vint comparingCaret, IGuiGraphicsParagraph::CaretRelativePosition position, bool& preferFrontSide)
			{
				if(position==IGuiGraphicsParagraph::CaretFirst) return 0;
				if(position==IGuiGraphicsParagraph::CaretLast) return paragraphText.Length();
				if(!IsValidCaret(comparingCaret)) return -1;

				if(position==IGuiGraphicsParagraph::CaretMoveLeft)
				{
					return comparingCaret==0?0:GetNearestCaretFromTextPos(comparingCaret-1, true);
				}
				if(position==IGuiGraphicsParagraph::CaretMoveRight)
				{
					return comparingCaret==paragraphText.Length()?paragraphText.Length():GetNearestCaretFromTextPos(comparingCaret+1, false);
				}

				vint frontLine=0;
				vint backLine=0;
				GetLineIndexFromTextPos(comparingCaret, frontLine, backLine);

				Ptr<UniscribeLine> line=lines[frontLine];
				if(line->virtualLines.Count()==0)
				{
					switch(position)
					{
					case IGuiGraphicsParagraph::CaretLineFirst:
					case IGuiGraphicsParagraph::CaretLineLast:
						return line->startFromParagraph;
					}
				}

				vint frontVirtualLine=0;
				vint backVirtualLine=0;
				GetVirtualLineIndexFromTextPos(comparingCaret, frontLine, frontVirtualLine, backVirtualLine);
				vint virtualLineIndex=preferFrontSide?frontVirtualLine:backVirtualLine;

				Ptr<UniscribeVirtualLine> virtualLine=virtualLineIndex==-1?nullptr:line->virtualLines[virtualLineIndex];

				switch(position)
				{
				case IGuiGraphicsParagraph::CaretLineFirst:
					return line->startFromParagraph+virtualLine->startFromLine;
				case IGuiGraphicsParagraph::CaretLineLast:
					return line->startFromParagraph+virtualLine->startFromLine+virtualLine->length;
				case IGuiGraphicsParagraph::CaretMoveUp:
					{
						if(frontLine==0 && virtualLineIndex<=0) return comparingCaret;
						Rect bounds=GetCaretBoundsWithLine(comparingCaret, frontLine, virtualLineIndex, preferFrontSide);
						if(bounds.Height()!=0)
						{
							if(virtualLineIndex<=0)
							{
								frontLine--;
								virtualLineIndex=lines[frontLine]->virtualLines.Count()-1;
							}
							else
							{
								virtualLineIndex--;
							}
							preferFrontSide=true;
							return GetCaretFromXWithLine(bounds.x1, frontLine, virtualLineIndex);
						}
					}
					break;
				case IGuiGraphicsParagraph::CaretMoveDown:
					{
						if(frontLine==lines.Count()-1 && virtualLineIndex==line->virtualLines.Count()-1) return comparingCaret;
						Rect bounds=GetCaretBoundsWithLine(comparingCaret, frontLine, virtualLineIndex, preferFrontSide);
						if(bounds.Height()!=0)
						{
							if(virtualLineIndex==line->virtualLines.Count()-1)
							{
								frontLine++;
								virtualLineIndex=0;
							}
							else
							{
								virtualLineIndex++;
							}
							preferFrontSide=false;
							return GetCaretFromXWithLine(bounds.x1, frontLine, virtualLineIndex);
						}
					}
					break;
				}
				return -1;
			}

			Rect UniscribeParagraph::GetCaretBounds(vint caret, bool frontSide)
			{
				if(!IsValidCaret(caret)) return Rect();

				vint frontLine=0;
				vint backLine=0;
				GetLineIndexFromTextPos(caret, frontLine, backLine);

				vint frontVirtualLine=0;
				vint backVirtualLine=0;
				GetVirtualLineIndexFromTextPos(caret, frontLine, frontVirtualLine, backVirtualLine);
				vint virtualLineIndex=frontSide?frontVirtualLine:backVirtualLine;

				return GetCaretBoundsWithLine(caret, frontLine, virtualLineIndex, frontSide);
			}

			vint UniscribeParagraph::GetCaretFromPoint(Point point)
			{
				vint lineIndex=GetLineIndexFromY(point.y);
				if(lineIndex==-1) return -1;

				Ptr<UniscribeLine> line=lines[lineIndex];
				if(line->virtualLines.Count()==0) return line->startFromParagraph;

				vint virtualLineIndex=GetVirtualLineIndexFromY(point.y, lineIndex);
				if(virtualLineIndex==-1) return -1;

				return GetCaretFromXWithLine(point.x, lineIndex, virtualLineIndex);
			}

			InlineObject UniscribeParagraph::GetInlineObjectFromPoint(Point point, vint& start, vint& length)
			{
				start = -1;
				length = 0;
				vint lineIndex = GetLineIndexFromY(point.y);
				if (lineIndex == -1) return InlineObject();

				Ptr<UniscribeLine> line = lines[lineIndex];
				if (line->virtualLines.Count() == 0) return InlineObject();

				vint virtualLineIndex = GetVirtualLineIndexFromY(point.y, lineIndex);
				if (virtualLineIndex == -1) return InlineObject();

				return GetInlineObjectFromXWithLine(point.x, lineIndex, virtualLineIndex, start, length);
			}

			vint UniscribeParagraph::GetNearestCaretFromTextPos(vint textPos, bool frontSide)
			{
				if (!IsValidTextPos(textPos)) return -1;
				vint frontLine = 0;
				vint backLine = 0;
				GetLineIndexFromTextPos(textPos, frontLine, backLine);
				if (frontLine == -1 || backLine == -1) return -1;
				if (frontLine != backLine)
				{
					return frontSide ? textPos - 1 : textPos + 1;
				}

				Ptr<UniscribeLine> line = lines[frontLine];
				if (textPos == line->startFromParagraph || textPos == line->startFromParagraph + line->lineText.Length())
				{
					return textPos;
				}

				vint frontRun = -1;
				vint backRun = -1;
				GetRunIndexFromTextPos(textPos, frontLine, frontRun, backRun);
				if (frontRun == -1 || backRun == -1) return -1;
				if (frontRun != backRun)
				{
					return textPos;
				}

				Ptr<UniscribeRun> run = line->scriptRuns[frontRun];
				if (auto objectRun = run.Cast<UniscribeEmbeddedObjectRun>())
				{
					return frontSide
						? line->startFromParagraph + run->startFromLine
						: line->startFromParagraph + run->startFromLine + run->length
						;
				}
				vint lineTextPos = textPos - line->startFromParagraph;
				if (lineTextPos == run->startFromLine) return textPos;
				if (lineTextPos == run->startFromLine + run->length) return textPos;

				auto item = run->scriptItem;
				vint itemTextPos = lineTextPos - item->startFromLine;
				if (item->charLogattrs[itemTextPos].fCharStop) return textPos;

				if (frontSide)
				{
					for (vint i = itemTextPos - 1; i >= 0; i--)
					{
						if (item->charLogattrs[i].fCharStop)
						{
							return i + line->startFromParagraph + item->startFromLine;
						}
					}
					return line->startFromParagraph + item->startFromLine;
				}
				else
				{
					for (vint i = itemTextPos + 1; i < item->length; i++)
					{
						if (item->charLogattrs[i].fCharStop)
						{
							return i + line->startFromParagraph + item->startFromLine;
						}
					}
					return line->startFromParagraph + item->startFromLine + item->length;
				}
			}

			bool UniscribeParagraph::IsValidCaret(vint caret)
			{
				if(!IsValidTextPos(caret)) return false;
				return GetNearestCaretFromTextPos(caret, true)==caret;
			}

			bool UniscribeParagraph::IsValidTextPos(vint textPos)
			{
				return 0<=textPos && textPos<=paragraphText.Length();
			}
		}
	}
}

/***********************************************************************
.\GDI\RENDERERS\GUIGRAPHICSWINDOWSGDI.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace elements
		{

/***********************************************************************
GuiGDIElement
***********************************************************************/

			GuiGDIElement::GuiGDIElement()
			{
			}
		}

		namespace elements_windows_gdi
		{
			using namespace windows;
			using namespace elements;
			using namespace collections;

/***********************************************************************
WindowsGDIRenderTarget
***********************************************************************/

			class WindowsGDIRenderTarget : public IWindowsGDIRenderTarget
			{
			protected:
				INativeWindow*				window = nullptr;
				WinDC*						dc = nullptr;
				List<Rect>					clippers;
				vint						clipperCoverWholeTargetCounter = 0;
				bool						hostedRendering = false;
				bool						rendering = false;

				void ApplyClipper(Rect validArea, bool clipperExists)
				{
					if (clipperExists)
					{
						dc->ClipRegion(Ptr(new WinRegion(validArea.Left(), validArea.Top(), validArea.Right(), validArea.Bottom(), true)));
					}
					else
					{
						dc->RemoveClip();
					}
				}

				void StartRenderingOnNativeWindow() override
				{
					dc = GetWindowsGDIObjectProvider()->GetNativeWindowDC(window);
				}

				RenderTargetFailure StopRenderingOnNativeWindow() override
				{
					dc = nullptr;
					return RenderTargetFailure::None;
				}

				Size GetCanvasSize() override
				{
					return window->Convert(window->GetClientSize());
				}

				void AfterPushedClipper(Rect clipper, Rect validArea, reflection::DescriptableObject* generator) override
				{
					ApplyClipper(validArea, true);
				}

				void AfterPushedClipperAndBecameInvalid(Rect clipper, reflection::DescriptableObject* generator) override
				{
				}

				void AfterPoppedClipperAndBecameValid(Rect validArea, bool clipperExists, reflection::DescriptableObject* generator) override
				{
					ApplyClipper(validArea, clipperExists);
				}

				void AfterPoppedClipper(Rect validArea, bool clipperExists, reflection::DescriptableObject* generator) override
				{
					ApplyClipper(validArea, clipperExists);
				}
			public:
				WindowsGDIRenderTarget(INativeWindow* _window)
					: window(_window)
				{
				}

				WinDC* GetDC()override
				{
					return dc ? dc : GetWindowsGDIObjectProvider()->GetNativeWindowDC(window);
				}
			};

/***********************************************************************
CachedResourceAllocator
***********************************************************************/

			class CachedPenAllocator : public GuiCachedResourceAllocatorBase<CachedPenAllocator, Color, Ptr<WinPen>>
			{
			public:
				Ptr<WinPen> CreateInternal(Color color)
				{
					return Ptr(new WinPen(PS_SOLID, 1, RGB(color.r, color.g, color.b)));
				}
			};

			class CachedBrushAllocator : public GuiCachedResourceAllocatorBase<CachedBrushAllocator, Color, Ptr<WinBrush>>
			{
			public:
				Ptr<WinBrush> CreateInternal(Color color)
				{
					return color.a == 0 ? Ptr(new WinBrush) : Ptr(new WinBrush(RGB(color.r, color.g, color.b)));
				}
			};

			class CachedFontAllocator : public GuiCachedResourceAllocatorBase<CachedFontAllocator, FontProperties, Ptr<WinFont>>
			{
			public:
				static Ptr<WinFont> CreateGdiFont(const FontProperties& value)
				{
					vint size = value.size < 0 ? value.size : -value.size;
					return Ptr(new WinFont(value.fontFamily, size, 0, 0, 0, (value.bold ? FW_BOLD : FW_NORMAL), value.italic, value.underline, value.strikeline, value.antialias));
				}

				Ptr<WinFont> CreateInternal(const FontProperties& value)
				{
					return CreateGdiFont(value);
				}
			};

/***********************************************************************
WindowsGDIResourceManager
***********************************************************************/

			class WindowsGDIImageFrameCache : public Object, public INativeImageFrameCache
			{
			protected:
				IWindowsGDIResourceManager* resourceManager = nullptr;
				INativeImageFrame* cachedFrame = nullptr;
				Ptr<WinBitmap>						bitmap;
				Ptr<WinBitmap>						disabledBitmap;
			public:
				WindowsGDIImageFrameCache(IWindowsGDIResourceManager* _resourceManager)
					:resourceManager(_resourceManager)
				{
				}

				~WindowsGDIImageFrameCache()
				{
				}

				void OnAttach(INativeImageFrame* frame)override
				{
					cachedFrame = frame;
					Size size = frame->GetSize();
					bitmap = Ptr(new WinBitmap(size.x, size.y, WinBitmap::vbb32Bits, true));

					IWICBitmap* wicBitmap = GetWindowsGDIObjectProvider()->GetWICBitmap(frame);
					WICRect rect;
					rect.X = 0;
					rect.Y = 0;
					rect.Width = (INT)size.x;
					rect.Height = (INT)size.y;
					wicBitmap->CopyPixels(&rect, (UINT)bitmap->GetLineBytes(), (UINT)(bitmap->GetLineBytes() * size.y), (BYTE*)bitmap->GetScanLines()[0]);

					bitmap->BuildAlphaChannel(false);
				}

				void OnDetach(INativeImageFrame* frame)override
				{
					resourceManager->DestroyBitmapCache(cachedFrame);
				}

				INativeImageFrame* GetFrame()
				{
					return cachedFrame;
				}

				Ptr<WinBitmap> GetBitmap(bool enabled)
				{
					if (enabled)
					{
						return bitmap;
					}
					else
					{
						if (!disabledBitmap)
						{
							vint w = bitmap->GetWidth();
							vint h = bitmap->GetHeight();
							disabledBitmap = Ptr(new WinBitmap(w, h, WinBitmap::vbb32Bits, true));
							for (vint y = 0; y < h; y++)
							{
								BYTE* read = bitmap->GetScanLines()[y];
								BYTE* write = disabledBitmap->GetScanLines()[y];
								for (vint x = 0; x < w; x++)
								{
									BYTE g = (read[0] + read[1] + read[2]) / 6 + read[3] / 2;
									write[0] = g;
									write[1] = g;
									write[2] = g;
									write[3] = read[3];
									read += 4;
									write += 4;
								}
							}
							disabledBitmap->BuildAlphaChannel(false);
						}
						return disabledBitmap;
					}
				}
			};

			class WindowsGDIResourceManager : public GuiGraphicsResourceManager, public IWindowsGDIResourceManager, public INativeControllerListener
			{
				typedef SortedList<Ptr<WindowsGDIImageFrameCache>> ImageCacheList;
			protected:
				SortedList<Ptr<WindowsGDIRenderTarget>>		renderTargets;
				Ptr<WindowsGDILayoutProvider>				layoutProvider;
				Ptr<WinPen>									focusRectanglePen;
				CachedPenAllocator							pens;
				CachedBrushAllocator						brushes;
				CachedFontAllocator							fonts;
				ImageCacheList								imageCaches;
			public:
				WindowsGDIResourceManager()
				{
					layoutProvider = Ptr(new WindowsGDILayoutProvider);
				}

				IGuiGraphicsRenderTarget* GetRenderTarget(INativeWindow* window)override
				{
					return GetWindowsGDIObjectProvider()->GetBindedRenderTarget(window);
				}

				void RecreateRenderTarget(INativeWindow* window)override
				{
				}

				void ResizeRenderTarget(INativeWindow* window)override
				{
				}

				IGuiGraphicsLayoutProvider* GetLayoutProvider()override
				{
					return layoutProvider.Obj();
				}

				Ptr<IGuiGraphicsElement> CreateRawElement() override
				{
					return Ptr(GuiGDIElement::Create());
				}

				void NativeWindowCreated(INativeWindow* window)override
				{
					auto renderTarget = Ptr(new WindowsGDIRenderTarget(window));
					renderTargets.Add(renderTarget);
					GetWindowsGDIObjectProvider()->SetBindedRenderTarget(window, renderTarget.Obj());
				}

				void NativeWindowDestroying(INativeWindow* window)override
				{
					WindowsGDIRenderTarget* renderTarget = dynamic_cast<WindowsGDIRenderTarget*>(GetWindowsGDIObjectProvider()->GetBindedRenderTarget(window));
					GetWindowsGDIObjectProvider()->SetBindedRenderTarget(window, 0);
					renderTargets.Remove(renderTarget);
				}

				Ptr<windows::WinPen> GetFocusRectanglePen()override
				{
					if (!focusRectanglePen)
					{
						DWORD styleArray[] = { 1,1 };
						focusRectanglePen = Ptr(new WinPen(PS_USERSTYLE, PS_ENDCAP_FLAT, PS_JOIN_BEVEL, 1, RGB(255, 255, 255), (DWORD)(sizeof(styleArray) / sizeof(*styleArray)), styleArray));
					}
					return focusRectanglePen;
				}

				Ptr<windows::WinPen> CreateGdiPen(Color color)override
				{
					return pens.Create(color);
				}

				void DestroyGdiPen(Color color)override
				{
					pens.Destroy(color);
				}

				Ptr<windows::WinBrush> CreateGdiBrush(Color color)override
				{
					return brushes.Create(color);
				}

				void DestroyGdiBrush(Color color)override
				{
					brushes.Destroy(color);
				}

				Ptr<windows::WinFont> CreateGdiFont(const FontProperties& fontProperties)override
				{
					return fonts.Create(fontProperties);
				}

				void DestroyGdiFont(const FontProperties& fontProperties)override
				{
					fonts.Destroy(fontProperties);
				}

				Ptr<windows::WinBitmap> GetBitmap(INativeImageFrame* frame, bool enabled)override
				{
					Ptr<INativeImageFrameCache> cache = frame->GetCache(this);
					if (cache)
					{
						return cache.Cast<WindowsGDIImageFrameCache>()->GetBitmap(enabled);
					}
					else
					{
						auto gdiCache = Ptr(new WindowsGDIImageFrameCache(this));
						if (frame->SetCache(this, gdiCache))
						{
							return gdiCache->GetBitmap(enabled);
						}
						else
						{
							return 0;
						}
					}
				}

				void DestroyBitmapCache(INativeImageFrame* frame)override
				{
					WindowsGDIImageFrameCache* cache = frame->GetCache(this).Cast<WindowsGDIImageFrameCache>().Obj();
					imageCaches.Remove(cache);
				}
			};
		}

		namespace elements_windows_gdi
		{
			IWindowsGDIResourceManager* windowsGDIResourceManager = nullptr;

			IWindowsGDIResourceManager* GetWindowsGDIResourceManager()
			{
				return windowsGDIResourceManager;
			}

			void SetWindowsGDIResourceManager(IWindowsGDIResourceManager* resourceManager)
			{
				windowsGDIResourceManager = resourceManager;
			}

/***********************************************************************
OS Supporting
***********************************************************************/

			IWindowsGDIObjectProvider* windowsGDIObjectProvider = nullptr;

			IWindowsGDIObjectProvider* GetWindowsGDIObjectProvider()
			{
				return windowsGDIObjectProvider;
			}

			void SetWindowsGDIObjectProvider(IWindowsGDIObjectProvider* provider)
			{
				windowsGDIObjectProvider = provider;
			}
		}
	}
}

/***********************************************************************
NativeMain
***********************************************************************/

using namespace vl::presentation;
using namespace vl::presentation::elements;

extern void GuiRawMain();
extern void GuiApplicationMain();

void RendererMainGDI(GuiHostedController* hostedController, bool raw)
{
	elements_windows_gdi::WindowsGDIResourceManager resourceManager;
	elements_windows_gdi::SetWindowsGDIResourceManager(&resourceManager);
	windows::GetWindowsNativeController()->CallbackService()->InstallListener(&resourceManager);

	auto hostedResourceManager = hostedController ? new GuiHostedGraphicsResourceManager(hostedController, &resourceManager) : nullptr;
	SetGuiGraphicsResourceManager(
		hostedResourceManager
		? hostedResourceManager
		: static_cast<IGuiGraphicsResourceManager*>(&resourceManager)
		);

	{
		elements_windows_gdi::GuiFocusRectangleElementRenderer::Register();
		elements_windows_gdi::GuiSolidBorderElementRenderer::Register();
		elements_windows_gdi::Gui3DBorderElementRenderer::Register();
		elements_windows_gdi::Gui3DSplitterElementRenderer::Register();
		elements_windows_gdi::GuiSolidBackgroundElementRenderer::Register();
		elements_windows_gdi::GuiGradientBackgroundElementRenderer::Register();
		elements_windows_gdi::GuiInnerShadowElementRenderer::Register();
		elements_windows_gdi::GuiSolidLabelElementRenderer::Register();
		elements_windows_gdi::GuiImageFrameElementRenderer::Register();
		elements_windows_gdi::GuiPolygonElementRenderer::Register();
		elements_windows_gdi::GuiGDIElementRenderer::Register();
		elements::GuiDocumentElementRenderer::Register();

		if (hostedController) hostedController->Initialize();
		if (raw)
		{
			GuiRawMain();
		}
		else
		{
			GuiApplicationMain();
		}
		if (hostedController) hostedController->Finalize();
	}

	SetGuiGraphicsResourceManager(nullptr);
	if (hostedResourceManager) delete hostedResourceManager;

	windows::GetWindowsNativeController()->CallbackService()->UninstallListener(&resourceManager);
	elements_windows_gdi::SetWindowsGDIResourceManager(nullptr);
}

/***********************************************************************
.\SERVICESIMPL\WINDOWSCLIPBOARDSERVICE.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace windows
		{
			using namespace collections;

/***********************************************************************
WindowsClipboardReader
***********************************************************************/

			bool WindowsClipboardReader::ContainsFormat(UINT format)
			{
				UINT currentFormat = 0;
				while (currentFormat = ::EnumClipboardFormats(currentFormat))
				{
					if (currentFormat == format)
					{
						return true;
					}
				}
				return false;
			}

			WindowsClipboardReader::WindowsClipboardReader(WindowsClipboardService* _service)
				:service(_service)
			{
			}

			WindowsClipboardReader::~WindowsClipboardReader()
			{
				CloseClipboard();
			}

			bool WindowsClipboardReader::ContainsText()
			{
				return ContainsFormat(CF_UNICODETEXT);
			}

			WString WindowsClipboardReader::GetText()
			{
				WString result;
				HANDLE handle = ::GetClipboardData(CF_UNICODETEXT);
				if (handle != 0)
				{
					wchar_t* buffer = (wchar_t*)::GlobalLock(handle);
					result = buffer;
					::GlobalUnlock(handle);
				}
				return result;
			}

			bool WindowsClipboardReader::ContainsDocument()
			{
				return ContainsFormat(service->WCF_Document);
			}

			Ptr<DocumentModel> WindowsClipboardReader::GetDocument()
			{
				HANDLE handle = ::GetClipboardData(service->WCF_Document);
				if (handle != 0)
				{
					auto buffer = ::GlobalLock(handle);
					auto size = ::GlobalSize(handle);
					stream::MemoryWrapperStream memoryStream(buffer, (vint)size);
					auto document = LoadDocumentFromClipboardStream(memoryStream);
					::GlobalUnlock(handle);
					return document;
				}
				return nullptr;
			}

			bool WindowsClipboardReader::ContainsImage()
			{
				return ContainsFormat(CF_BITMAP);
			}

			Ptr<INativeImage> WindowsClipboardReader::GetImage()
			{
				HBITMAP handle = (HBITMAP)::GetClipboardData(CF_BITMAP);
				if (handle != 0)
				{
					return CreateImageFromHBITMAP(handle);
				}
				return nullptr;
			}

			void WindowsClipboardReader::CloseClipboard()
			{
				if (service->reader)
				{
					::CloseClipboard();
					service->reader = nullptr;
				}
			}

/***********************************************************************
WindowsClipboardWriter
***********************************************************************/

			void WindowsClipboardWriter::SetClipboardData(UINT format, stream::MemoryStream& memoryStream)
			{
				memoryStream.SeekFromBegin(0);
				HGLOBAL data = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)memoryStream.Size());
				auto buffer = ::GlobalLock(data);
				memoryStream.Read(buffer, (vint)memoryStream.Size());
				::GlobalUnlock(data);
				::SetClipboardData(format, data);
			}

			WindowsClipboardWriter::WindowsClipboardWriter(WindowsClipboardService* _service)
				:service(_service)
			{
			}

			WindowsClipboardWriter::~WindowsClipboardWriter()
			{
			}

			void WindowsClipboardWriter::SetText(const WString& value)
			{
				textData = value;
			}

			void WindowsClipboardWriter::SetDocument(Ptr<DocumentModel> value)
			{
				if (!textData)
				{
					textData = value->GetTextForReading(WString::Unmanaged(L"\r\n\r\n"));
				}

				if (!imageData)
				{
					imageData = GetImageFromSingleImageDocument(value);
				}

				documentData = value;
				ModifyDocumentForClipboard(documentData);
			}

			void WindowsClipboardWriter::SetImage(Ptr<INativeImage> value)
			{
				imageData = value;
			}

			bool WindowsClipboardWriter::Submit()
			{
				if (service->reader)
				{
					service->reader->CloseClipboard();
				}

				if (!::OpenClipboard(service->ownerHandle)) return false;
				::EmptyClipboard();

				if (textData)
				{
					vint size = (textData.Value().Length() + 1) * sizeof(wchar_t);
					HGLOBAL data = ::GlobalAlloc(GMEM_MOVEABLE, size);
					auto buffer = (wchar_t*)::GlobalLock(data);
					memcpy(buffer, textData.Value().Buffer(), size);
					::GlobalUnlock(data);
					::SetClipboardData(CF_UNICODETEXT, data);
				}

				if (documentData)
				{
					{
						stream::MemoryStream memoryStream;
						SaveDocumentToClipboardStream(documentData, memoryStream);
						SetClipboardData(service->WCF_Document, memoryStream);
					}
					{
						stream::MemoryStream memoryStream;
						SaveDocumentToRtfStream(documentData, memoryStream);
						SetClipboardData(service->WCF_RTF, memoryStream);
					}
					{
						stream::MemoryStream memoryStream;
						SaveDocumentToHtmlClipboardStream(documentData, memoryStream);
						SetClipboardData(service->WCF_HTML, memoryStream);
					}
				}

				if (imageData && imageData->GetFrameCount()>0)
				{
					if (auto wicBitmap = GetWICBitmap(imageData->GetFrame(0)))
					{
						UINT width = 0;
						UINT height = 0;
						wicBitmap->GetSize(&width, &height);
						auto bitmap = Ptr(new WinBitmap((vint)width, (vint)height, WinBitmap::vbb32Bits, true));

						WICRect rect;
						rect.X = 0;
						rect.Y = 0;
						rect.Width = (INT)width;
						rect.Height = (INT)height;
						wicBitmap->CopyPixels(&rect, (UINT)bitmap->GetLineBytes(), (UINT)(bitmap->GetLineBytes()*height), (BYTE*)bitmap->GetScanLines()[0]);

						stream::MemoryStream memoryStream;
						bitmap->SaveToStream(memoryStream, true);
						SetClipboardData(CF_DIBV5, memoryStream);
					}
				}

				::CloseClipboard();
				return true;
			}

/***********************************************************************
WindowsClipboardService
***********************************************************************/

			class WindowsFakeClipboardReader : public Object, public INativeClipboardReader
			{
			public:
				bool							ContainsText()override { return false; }
				WString							GetText()override { return L""; }
				bool							ContainsDocument()override { return false; }
				Ptr<DocumentModel>				GetDocument()override { return nullptr; }
				bool							ContainsImage()override { return false; }
				Ptr<INativeImage>				GetImage()override { return nullptr; }
			};

			WindowsClipboardService::WindowsClipboardService()
				:ownerHandle(NULL)
			{
				WCF_Document = ::RegisterClipboardFormat(L"GacUI Document Format");
				WCF_RTF = ::RegisterClipboardFormat(L"Rich Text Format");
				WCF_HTML = ::RegisterClipboardFormat(L"HTML Format");
			}

			Ptr<INativeClipboardReader> WindowsClipboardService::ReadClipboard()
			{
				if (!reader)
				{
					if (!::OpenClipboard(ownerHandle)) return Ptr(new WindowsFakeClipboardReader);
					reader = new WindowsClipboardReader(this);
				}
				return Ptr(reader);
			}

			Ptr<INativeClipboardWriter> WindowsClipboardService::WriteClipboard()
			{
				return Ptr(new WindowsClipboardWriter(this));
			}

			void WindowsClipboardService::SetOwnerHandle(HWND handle)
			{
				HWND oldHandle=ownerHandle;
				ownerHandle=handle;
				if(handle==NULL)
				{
					::RemoveClipboardFormatListener(oldHandle);
				}
				else
				{
					::AddClipboardFormatListener(ownerHandle);
				}
			}
		}
	}
}

/***********************************************************************
.\SERVICESIMPL\WINDOWSDIALOGSERVICE.CPP
***********************************************************************/
#define _WINSOCKAPI_
#include <Vfw.h>

#pragma comment(lib, "Vfw32.lib")

namespace vl
{
	namespace presentation
	{
		namespace windows
		{
			using namespace collections;

/***********************************************************************
WindowsDialogService
***********************************************************************/

			WindowsDialogService::WindowsDialogService(HandleRetriver _handleRetriver)
				:handleRetriver(_handleRetriver)
			{
			}

			INativeDialogService::MessageBoxButtonsOutput WindowsDialogService::ShowMessageBox(
				INativeWindow* window,
				const WString& text,
				const WString& title,
				MessageBoxButtonsInput buttons,
				MessageBoxDefaultButton defaultButton,
				MessageBoxIcons icon,
				MessageBoxModalOptions modal)
			{
				WString realTitle=title;
				if(title==L"" && window!=0)
				{
					realTitle=window->GetTitle();
				}
				HWND hWnd=handleRetriver(window);
				LPCTSTR lpText=text.Buffer();
				LPCTSTR lpCaption=realTitle.Buffer();
				UINT uType=0;
				
#define MAP(A, B) case A: uType|=(B); break
				switch(buttons)
				{
					MAP(DisplayOK, MB_OK);
					MAP(DisplayOKCancel, MB_OKCANCEL);
					MAP(DisplayYesNo, MB_YESNO);
					MAP(DisplayYesNoCancel, MB_YESNOCANCEL);
					MAP(DisplayRetryCancel, MB_RETRYCANCEL);
					MAP(DisplayAbortRetryIgnore, MB_ABORTRETRYIGNORE);
					MAP(DisplayCancelTryAgainContinue, MB_CANCELTRYCONTINUE);
				}
				switch(defaultButton)
				{
					MAP(DefaultFirst, MB_DEFBUTTON1);
					MAP(DefaultSecond, MB_DEFBUTTON2);
					MAP(DefaultThird, MB_DEFBUTTON3);
				}
				switch(icon)
				{
					MAP(IconError, MB_ICONERROR);
					MAP(IconQuestion, MB_ICONQUESTION);
					MAP(IconWarning, MB_ICONWARNING);
					MAP(IconInformation, MB_ICONINFORMATION);
				}
				switch(modal)
				{
					MAP(ModalWindow, MB_APPLMODAL);
					MAP(ModalSystem, MB_SYSTEMMODAL);
					MAP(ModalTask, MB_TASKMODAL);
				}
#undef MAP

				vint result=MessageBox(hWnd, lpText, lpCaption, uType);
				switch(result)
				{
				case IDABORT: return SelectAbort;
				case IDCANCEL: return SelectCancel;
				case IDCONTINUE: return SelectContinue;
				case IDIGNORE: return SelectIgnore;
				case IDNO: return SelectNo;
				case IDOK: return SelectOK;
				case IDRETRY: return SelectRetry;
				case IDTRYAGAIN: return SelectTryAgain;
				case IDYES: return SelectYes;
				default: return SelectOK;
				}
			}

			bool WindowsDialogService::ShowColorDialog(INativeWindow* window, Color& selection, bool selected, ColorDialogCustomColorOptions customColorOptions, Color* customColors)
			{
				CHOOSECOLOR chooseColor;
				ZeroMemory(&chooseColor, sizeof(chooseColor));
				COLORREF customColorsBuffer[16]={0};
				if(customColors)
				{
					for(vint i=0;i<sizeof(customColorsBuffer)/sizeof(*customColorsBuffer);i++)
					{
						customColorsBuffer[i]=RGB(customColors[i].r, customColors[i].g, customColors[i].b);
					}
				}

				chooseColor.lStructSize=sizeof(chooseColor);
				chooseColor.hwndOwner=handleRetriver(window);
				chooseColor.rgbResult=RGB(selection.r, selection.g, selection.b);
				chooseColor.lpCustColors=customColorsBuffer;
				chooseColor.Flags=CC_ANYCOLOR;
				
#define MAP(A, B) case A: chooseColor.Flags|=(B); break
				switch(customColorOptions)
				{
					MAP(CustomColorDisabled, CC_PREVENTFULLOPEN);
					MAP(CustomColorOpened, CC_FULLOPEN);
				}
#undef MAP
				if(selected)
				{
					chooseColor.Flags|=CC_RGBINIT;
				}

				BOOL result=ChooseColor(&chooseColor);
				if(result)
				{
					selection=Color(GetRValue(chooseColor.rgbResult), GetGValue(chooseColor.rgbResult), GetBValue(chooseColor.rgbResult));
					if(customColors)
					{
						for(vint i=0;i<sizeof(customColorsBuffer)/sizeof(*customColorsBuffer);i++)
						{
							COLORREF color=customColorsBuffer[i];
							customColors[i]=Color(GetRValue(color), GetGValue(color), GetBValue(color));
						}
					}
				}
				return result!=FALSE;
			}

			bool WindowsDialogService::ShowFontDialog(INativeWindow* window, FontProperties& selectionFont, Color& selectionColor, bool selected, bool showEffect, bool forceFontExist)
			{
				LOGFONT logFont;
				ZeroMemory(&logFont, sizeof(logFont));
				logFont.lfHeight=-(int)selectionFont.size;
				logFont.lfWeight=selectionFont.bold?FW_BOLD:FW_REGULAR;
				logFont.lfItalic=selectionFont.italic?TRUE:FALSE;
				logFont.lfUnderline=selectionFont.underline?TRUE:FALSE;
				logFont.lfStrikeOut=selectionFont.strikeline?TRUE:FALSE;
				wcscpy_s(logFont.lfFaceName, selectionFont.fontFamily.Buffer());

				CHOOSEFONT chooseFont;
				ZeroMemory(&chooseFont, sizeof(chooseFont));
				chooseFont.lStructSize=sizeof(chooseFont);
				chooseFont.hwndOwner=handleRetriver(window);
				chooseFont.lpLogFont=&logFont;
				chooseFont.iPointSize=0;
				chooseFont.Flags=(showEffect?CF_EFFECTS:0) | (forceFontExist?CF_FORCEFONTEXIST:0) | (selected?CF_INITTOLOGFONTSTRUCT:0);
				chooseFont.rgbColors=RGB(selectionColor.r, selectionColor.g, selectionColor.b);

				BOOL result=ChooseFont(&chooseFont);
				if(result)
				{
					selectionFont.fontFamily=logFont.lfFaceName;
					selectionFont.bold=logFont.lfWeight>FW_REGULAR;
					selectionFont.italic=logFont.lfItalic!=FALSE;
					selectionFont.underline=logFont.lfUnderline!=FALSE;
					selectionFont.strikeline=logFont.lfStrikeOut!=FALSE;
					selectionFont.size=-logFont.lfHeight;

					selectionColor=Color(GetRValue(chooseFont.rgbColors), GetGValue(chooseFont.rgbColors), GetBValue(chooseFont.rgbColors));
				}
				return result!=FALSE;
			}

			bool WindowsDialogService::ShowFileDialog(INativeWindow* window, collections::List<WString>& selectionFileNames, vint& selectionFilterIndex, FileDialogTypes dialogType, const WString& title, const WString& initialFileName, const WString& initialDirectory, const WString& defaultExtension, const WString& filter, FileDialogOptions options)
			{
				Array<wchar_t> fileNamesBuffer(65536>initialFileName.Length()+1?65536:initialFileName.Length()+1);
				wcscpy_s(&fileNamesBuffer[0], fileNamesBuffer.Count(), initialFileName.Buffer());

				OPENFILENAME ofn;
				ZeroMemory(&ofn, sizeof(ofn));
				ofn.lStructSize=sizeof(ofn);
				ofn.hwndOwner=handleRetriver(window);
				ofn.hInstance=NULL;
				ofn.lpstrCustomFilter=NULL;
				ofn.nMaxCustFilter=0;
				ofn.nFilterIndex=(int)selectionFilterIndex+1;
				ofn.lpstrFile=&fileNamesBuffer[0];
				ofn.nMaxFile=(int)fileNamesBuffer.Count();
				ofn.lpstrFileTitle=NULL;
				ofn.nMaxFileTitle=0;
				ofn.lpstrInitialDir=initialDirectory==L""?NULL:initialDirectory.Buffer();
				ofn.lpstrTitle=title==L""?NULL:title.Buffer();
				ofn.lpstrDefExt=defaultExtension==L""?NULL:defaultExtension.Buffer();

				List<vint> filterSeparators;
				for(vint i=0;i<filter.Length();i++)
				{
					if(filter[i]==L'|')
					{
						filterSeparators.Add(i);
					}
				}
				if(filterSeparators.Count()%2==1)
				{
					filterSeparators.Add(filter.Length());
				}

				Array<wchar_t> filterBuffer(filter.Length()+2);
				vint index=0;
				// TODO: (enumerable) foreach
				for(vint i=0;i<filterSeparators.Count();i++)
				{
					vint end=filterSeparators[i];
					wcsncpy_s(&filterBuffer[index], filterBuffer.Count()-index, filter.Buffer()+index, end-index);
					filterBuffer[end]=0;
					index=end+1;
				}
				filterBuffer[index]=0;
				ofn.lpstrFilter=&filterBuffer[0];

				ofn.Flags=OFN_ENABLESIZING | OFN_EXPLORER | OFN_LONGNAMES;
				if(options&FileDialogAllowMultipleSelection)	ofn.Flags|=OFN_ALLOWMULTISELECT;
				if(options&FileDialogFileMustExist)				ofn.Flags|=OFN_FILEMUSTEXIST;
				if(!(options&FileDialogShowReadOnlyCheckBox))	ofn.Flags|=OFN_HIDEREADONLY;
				if(!(options&FileDialogDereferenceLinks))		ofn.Flags|=OFN_NODEREFERENCELINKS;
				if(!(options&FileDialogShowNetworkButton))		ofn.Flags|=OFN_NONETWORKBUTTON;
				if(options&FileDialogPromptCreateFile)			ofn.Flags|=OFN_CREATEPROMPT;
				if(options&FileDialogPromptOverwriteFile)		ofn.Flags|=OFN_OVERWRITEPROMPT;
				if(options&FileDialogDirectoryMustExist)		ofn.Flags|=OFN_PATHMUSTEXIST;
				if(!(options&FileDialogAddToRecent))			ofn.Flags|=OFN_DONTADDTORECENT;

				BOOL result=FALSE;
				switch(dialogType)
				{
				case FileDialogOpen:
					result=GetOpenFileName(&ofn);
					break;
				case FileDialogOpenPreview:
					result=GetOpenFileNamePreview(&ofn);
					break;
				case FileDialogSave:
					result=GetSaveFileName(&ofn);
					break;
				case FileDialogSavePreview:
					result=GetSaveFileNamePreview(&ofn);
					break;
				}
				if(result)
				{
					selectionFilterIndex=ofn.nFilterIndex-1;
					selectionFileNames.Clear();
					if(options&FileDialogAllowMultipleSelection)
					{
						const wchar_t* reading=&fileNamesBuffer[0];
						WString directory=reading;
						reading+=directory.Length()+1;
						while(*reading)
						{
							WString fileName=reading;
							reading+=fileName.Length()+1;
							selectionFileNames.Add(directory+L"\\"+fileName);
						}
						if(selectionFileNames.Count()==0)
						{
							selectionFileNames.Add(directory);
						}
					}
					else
					{
						selectionFileNames.Add(&fileNamesBuffer[0]);
					}
				}
				return result!=FALSE;
			}
		}
	}
}

/***********************************************************************
.\SERVICESIMPL\WINDOWSIMAGESERVICE.CPP
***********************************************************************/
#define _WINSOCKAPI_
#include <Shlwapi.h>

#pragma comment(lib, "WindowsCodecs.lib")

namespace vl
{
	namespace presentation
	{
		namespace windows
		{
			using namespace collections;

/***********************************************************************
WindowsImageFrame
***********************************************************************/

			void WindowsImageFrame::Initialize(IWICBitmapSource* bitmapSource)
			{
				IWICImagingFactory* factory=GetWICImagingFactory();
				ComPtr<IWICFormatConverter> converter;
				{
					IWICFormatConverter* formatConverter=0;
					HRESULT hr=factory->CreateFormatConverter(&formatConverter);
					if(SUCCEEDED(hr))
					{
						converter=formatConverter;
						converter->Initialize(
							bitmapSource,
							GUID_WICPixelFormat32bppPBGRA,
							WICBitmapDitherTypeNone,
							NULL,
							0.0f,
							WICBitmapPaletteTypeCustom);
					}
				}

				IWICBitmap* bitmap=0;
				IWICBitmapSource* convertedBitmapSource=0;
				if(converter)
				{
					convertedBitmapSource=converter.Obj();
				}
				else
				{
					convertedBitmapSource=bitmapSource;
				}
				HRESULT hr=factory->CreateBitmapFromSource(convertedBitmapSource, WICBitmapCacheOnLoad, &bitmap);
				if(SUCCEEDED(hr))
				{
					frameBitmap=bitmap;
				}
			}

			WindowsImageFrame::WindowsImageFrame(INativeImage* _image, IWICBitmapFrameDecode* frameDecode)
				:image(_image)
			{
				Initialize(frameDecode);
			}

			WindowsImageFrame::WindowsImageFrame(INativeImage* _image, IWICBitmap* sourceBitmap)
				:image(_image)
			{
				Initialize(sourceBitmap);
			}

			WindowsImageFrame::~WindowsImageFrame()
			{
			}

			INativeImage* WindowsImageFrame::GetImage()
			{
				return image;
			}

			Size WindowsImageFrame::GetSize()
			{
				UINT width=0;
				UINT height=0;
				frameBitmap->GetSize(&width, &height);
				return Size(width, height);
			}

			IWICBitmap* WindowsImageFrame::GetFrameBitmap()
			{
				return frameBitmap.Obj();
			}

			void WindowsImageFrame::SaveBitmapToStream(stream::IStream& imageStream)
			{
				UINT width = 0;
				UINT height = 0;
				frameBitmap->GetSize(&width, &height);
				auto bitmap = Ptr(new WinBitmap((vint)width, (vint)height, WinBitmap::vbb32Bits, true));

				WICRect rect;
				rect.X = 0;
				rect.Y = 0;
				rect.Width = (INT)width;
				rect.Height = (INT)height;
				frameBitmap->CopyPixels(&rect, (UINT)bitmap->GetLineBytes(), (UINT)(bitmap->GetLineBytes()*height), (BYTE*)bitmap->GetScanLines()[0]);

				bitmap->SaveToStream(imageStream, false);
			}

/***********************************************************************
WindowsImage
***********************************************************************/

			WindowsImage::WindowsImage(INativeImageService* _imageService, IWICBitmapDecoder* _bitmapDecoder)
				:imageService(_imageService)
				,bitmapDecoder(_bitmapDecoder)
			{
				UINT count=0;
				bitmapDecoder->GetFrameCount(&count);
				frames.Resize(count);
			}

			WindowsImage::~WindowsImage()
			{
			}

			INativeImageService* WindowsImage::GetImageService()
			{
				return imageService;
			}

			INativeImage::FormatType WindowsImage::GetFormat()
			{
				GUID formatGUID;
				HRESULT hr=bitmapDecoder->GetContainerFormat(&formatGUID);
				if(SUCCEEDED(hr))
				{
					if(formatGUID==GUID_ContainerFormatBmp)
					{
						return INativeImage::Bmp;
					}
					else if(formatGUID==GUID_ContainerFormatPng)
					{
						return INativeImage::Png;
					}
					else if(formatGUID==GUID_ContainerFormatGif)
					{
						return INativeImage::Gif;
					}
					else if(formatGUID==GUID_ContainerFormatJpeg)
					{
						return INativeImage::Jpeg;
					}
					else if(formatGUID==GUID_ContainerFormatIco)
					{
						return INativeImage::Icon;
					}
					else if(formatGUID==GUID_ContainerFormatTiff)
					{
						return INativeImage::Tiff;
					}
					else if(formatGUID==GUID_ContainerFormatWmp)
					{
						return INativeImage::Wmp;
					}
				}
				return INativeImage::Unknown;
			}

			vint WindowsImage::GetFrameCount()
			{
				return frames.Count();
			}

			INativeImageFrame* WindowsImage::GetFrame(vint index)
			{
				if(0<=index && index<GetFrameCount())
				{
					Ptr<WindowsImageFrame>& frame=frames[index];
					if(!frame)
					{
						IWICBitmapFrameDecode* frameDecode=0;
						HRESULT hr=bitmapDecoder->GetFrame((int)index, &frameDecode);
						if(SUCCEEDED(hr))
						{
							frame=Ptr(new WindowsImageFrame(this, frameDecode));
							frameDecode->Release();
						}
					}
					return frame.Obj();
				}
				else
				{
					return 0;
				}
			}

			void CopyMetadataBody(IWICMetadataQueryReader* reader, IWICMetadataQueryWriter* writer, const WString& prefix)
			{
				IEnumString* enumString = nullptr;
				HRESULT hr = reader->GetEnumerator(&enumString);
				if (enumString)
				{
					while (true)
					{
						LPOLESTR metadataName = nullptr;
						ULONG fetched = 0;
						hr = enumString->Next(1, &metadataName, &fetched);
						if (hr != S_OK) break;
						if (fetched == 0) break;

						PROPVARIANT metadataValue;
						PropVariantInit(&metadataValue);
						hr = reader->GetMetadataByName(metadataName, &metadataValue);
						if (hr == S_OK)
						{
							if (metadataValue.vt == VT_UNKNOWN && metadataValue.punkVal)
							{
								IWICMetadataQueryReader* embeddedReader = nullptr;
								hr = metadataValue.punkVal->QueryInterface<IWICMetadataQueryReader>(&embeddedReader);
								if (embeddedReader)
								{
									CopyMetadataBody(embeddedReader, writer, prefix + metadataName);
									embeddedReader->Release();
								}
							}
							else
							{
								hr = writer->SetMetadataByName((prefix + metadataName).Buffer(), &metadataValue);
							}
							hr = PropVariantClear(&metadataValue);
						}

						CoTaskMemFree(metadataName);
					}
					enumString->Release();
				}
			}

			template<typename TDecoder, typename TEncoder>
			void CopyMetadata(TDecoder* decoder, TEncoder* encoder)
			{
				IWICMetadataQueryReader* reader = nullptr;
				IWICMetadataQueryWriter* writer = nullptr;
				HRESULT hr = decoder->GetMetadataQueryReader(&reader);
				hr = encoder->GetMetadataQueryWriter(&writer);
				if (reader && writer)
				{
					CopyMetadataBody(reader, writer, WString::Empty);
				}
				if (reader) reader->Release();
				if (writer) writer->Release();
			}

			GUID GetGuidFromFormat(INativeImage::FormatType formatType)
			{
				switch (formatType)
				{
				case INativeImage::Bmp: return GUID_ContainerFormatBmp;
				case INativeImage::Gif: return GUID_ContainerFormatGif;
				case INativeImage::Icon: return GUID_ContainerFormatIco;
				case INativeImage::Jpeg: return GUID_ContainerFormatJpeg;
				case INativeImage::Png: return GUID_ContainerFormatPng;
				case INativeImage::Tiff: return GUID_ContainerFormatTiff;
				case INativeImage::Wmp: return GUID_ContainerFormatWmp;
				default: CHECK_FAIL(L"GetGuidFromFormat(INativeImage::FormatType)#Unexpected format type.");
				}
			}

			void MoveIStreamToStream(IStream* pIStream, stream::IStream& outputStream)
			{
				LARGE_INTEGER dlibMove;
				dlibMove.QuadPart = 0;
				HRESULT hr = pIStream->Seek(dlibMove, STREAM_SEEK_SET, NULL);
				Array<char> buffer(65536);
				while (true)
				{
					ULONG count = (ULONG)buffer.Count();
					ULONG read = 0;
					hr = pIStream->Read(&buffer[0], count, &read);
					if (read > 0)
					{
						outputStream.Write(&buffer[0], (vint)read);
					}
					if (read != count)
					{
						break;
					}
				}
			}

			void WindowsImage::SaveToStream(stream::IStream& imageStream, FormatType formatType)
			{
				auto factory = GetWICImagingFactory();
				GUID formatGUID;
				HRESULT hr;

				bool sameFormat = formatType == INativeImage::Unknown || formatType == GetFormat();
				if (formatType == INativeImage::Unknown)
				{
					hr = bitmapDecoder->GetContainerFormat(&formatGUID);
					if (hr != S_OK) goto FAILED;
				}
				else
				{
					formatGUID = GetGuidFromFormat(formatType);
				}

				{
					IWICBitmapEncoder* bitmapEncoder = nullptr;
					hr = factory->CreateEncoder(formatGUID, NULL, &bitmapEncoder);
					if (!bitmapEncoder) goto FAILED;

					IStream* pIStream = nullptr;
					hr = CreateStreamOnHGlobal(NULL, TRUE, &pIStream);
					if (!pIStream)
					{
						bitmapEncoder->Release();
						goto FAILED;
					}

					hr = bitmapEncoder->Initialize(pIStream, WICBitmapEncoderNoCache);
					if (hr != S_OK)
					{
						pIStream->Release();
						bitmapEncoder->Release();
						goto FAILED;
					}

					{
						UINT actualCount = 0;
						Array<IWICColorContext*> colorContexts(16);
						hr = bitmapDecoder->GetColorContexts((UINT)colorContexts.Count(), &colorContexts[0], &actualCount);
						if (hr == S_OK)
						{
							if ((vint)actualCount > colorContexts.Count())
							{
								// TODO: (enumerable) foreach
								for (vint i = 0; i < colorContexts.Count(); i++) colorContexts[i]->Release();
								colorContexts.Resize((vint)actualCount);
								bitmapDecoder->GetColorContexts(actualCount, &colorContexts[0], &actualCount);
							}
							if (actualCount > 0)
							{
								bitmapEncoder->SetColorContexts(actualCount, &colorContexts[0]);
								for (vint i = 0; i < (vint)actualCount; i++) colorContexts[i]->Release();
							}
						}
					}
					{
						IWICPalette* palette = nullptr;
						hr = factory->CreatePalette(&palette);
						if (palette)
						{
							hr = bitmapDecoder->CopyPalette(palette);
							if (hr == S_OK)
							{
								bitmapEncoder->SetPalette(palette);
							}
							palette->Release();
						}
					}
					{
						IWICBitmapSource* source = nullptr;
						hr = bitmapDecoder->GetPreview(&source);
						if (source)
						{
							bitmapEncoder->SetPreview(source);
							source->Release();
						}
					}
					{
						IWICBitmapSource* source = nullptr;
						hr = bitmapDecoder->GetThumbnail(&source);
						if (source)
						{
							bitmapEncoder->SetThumbnail(source);
							source->Release();
						}
					}

					if (sameFormat)
					{
						CopyMetadata(bitmapDecoder.Obj(), bitmapEncoder);
					}

					UINT frameCount = 0;
					bitmapDecoder->GetFrameCount(&frameCount);
					for (UINT i = 0; i < frameCount; i++)
					{
						IWICBitmapFrameDecode* frameDecode = nullptr;
						IWICBitmapFrameEncode* frameEncode = nullptr;
						hr = bitmapDecoder->GetFrame(i, &frameDecode);
						hr = bitmapEncoder->CreateNewFrame(&frameEncode, NULL);
						if (frameDecode && frameEncode)
						{
							hr = frameEncode->Initialize(NULL);
							if (sameFormat)
							{
								CopyMetadata(frameDecode, frameEncode);
							}
							hr = frameEncode->WriteSource(frameDecode, NULL);
							hr = frameEncode->Commit();
						}
						if (frameDecode) frameDecode->Release();
						if (frameEncode) frameEncode->Release();
					}

					hr = bitmapEncoder->Commit();
					bitmapEncoder->Release();
					MoveIStreamToStream(pIStream, imageStream);
					pIStream->Release();
				}
			FAILED:;
			}

/***********************************************************************
WindowsBitmapImage
***********************************************************************/

			WindowsBitmapImage::WindowsBitmapImage(INativeImageService* _imageService, IWICBitmap* sourceBitmap, FormatType _formatType)
				:imageService(_imageService)
				,formatType(_formatType)
			{
				frame = Ptr(new WindowsImageFrame(this, sourceBitmap));
			}

			WindowsBitmapImage::~WindowsBitmapImage()
			{
			}

			INativeImageService* WindowsBitmapImage::GetImageService()
			{
				return imageService;
			}

			INativeImage::FormatType WindowsBitmapImage::GetFormat()
			{
				return formatType;
			}

			vint WindowsBitmapImage::GetFrameCount()
			{
				return 1;
			}

			INativeImageFrame* WindowsBitmapImage::GetFrame(vint index)
			{
				return index==0?frame.Obj():0;
			}

			void WindowsBitmapImage::SaveToStream(stream::IStream& imageStream, FormatType formatType)
			{
				auto factory = GetWICImagingFactory();
				if (formatType == INativeImage::Unknown)
				{
					formatType = INativeImage::Bmp;
				}
				GUID formatGUID = GetGuidFromFormat(formatType);

				IWICBitmapEncoder* bitmapEncoder = nullptr;
				HRESULT hr = factory->CreateEncoder(formatGUID, NULL, &bitmapEncoder);
				if (!bitmapEncoder) goto FAILED;

				{
					IStream* pIStream = nullptr;
					hr = CreateStreamOnHGlobal(NULL, TRUE, &pIStream);
					if (!pIStream)
					{
						bitmapEncoder->Release();
						goto FAILED;
					}

					hr = bitmapEncoder->Initialize(pIStream, WICBitmapEncoderNoCache);
					if (hr != S_OK)
					{
						pIStream->Release();
						bitmapEncoder->Release();
						goto FAILED;
					}

					{
						IWICBitmapFrameEncode* frameEncode = nullptr;
						hr = bitmapEncoder->CreateNewFrame(&frameEncode, NULL);
						if (frameEncode)
						{
							hr = frameEncode->Initialize(NULL);
							hr = frameEncode->WriteSource(frame->GetFrameBitmap(), NULL);
							hr = frameEncode->Commit();
							frameEncode->Release();
						}
					}

					hr = bitmapEncoder->Commit();
					bitmapEncoder->Release();
					MoveIStreamToStream(pIStream, imageStream);
					pIStream->Release();
				}
			FAILED:;
			}

/***********************************************************************
WindowsImageService
***********************************************************************/

			WindowsImageService::WindowsImageService()
			{
				IWICImagingFactory* factory=0;
				HRESULT hr = CoCreateInstance(
#if defined(WINCODEC_SDK_VERSION2)
					CLSID_WICImagingFactory1,
#else
					CLSID_WICImagingFactory,
#endif
					NULL,
					CLSCTX_INPROC_SERVER,
					IID_IWICImagingFactory,
					(LPVOID*)&factory
					);
				if(SUCCEEDED(hr))
				{
					imagingFactory=factory;
				}
			}

			WindowsImageService::~WindowsImageService()
			{
			}

			Ptr<INativeImage> WindowsImageService::CreateImageFromFile(const WString& path)
			{
				IWICBitmapDecoder* bitmapDecoder=0;
				HRESULT hr=imagingFactory->CreateDecoderFromFilename(
					path.Buffer(),
					NULL,
					GENERIC_READ,
					WICDecodeMetadataCacheOnDemand,
					&bitmapDecoder);
				if(SUCCEEDED(hr))
				{
					return Ptr(new WindowsImage(this, bitmapDecoder));
				}
				else
				{
					return nullptr;
				}
			}

			Ptr<INativeImage> WindowsImageService::CreateImageFromMemory(void* buffer, vint length)
			{
				Ptr<INativeImage> result;
				::IStream* stream=SHCreateMemStream((const BYTE*)buffer, (int)length);
				if(stream)
				{
					IWICBitmapDecoder* bitmapDecoder=0;
					HRESULT hr=imagingFactory->CreateDecoderFromStream(stream, NULL, WICDecodeMetadataCacheOnDemand, &bitmapDecoder);
					if(SUCCEEDED(hr))
					{
						result=Ptr(new WindowsImage(this, bitmapDecoder));
					}
					stream->Release();
				}
				return result;
			}

			Ptr<INativeImage> WindowsImageService::CreateImageFromStream(stream::IStream& imageStream)
			{
				stream::MemoryStream memoryStream;
				char buffer[65536];
				while (true)
				{
					vint length = imageStream.Read(buffer, sizeof(buffer));
					memoryStream.Write(buffer, length);
					if (length != sizeof(buffer))
					{
						break;
					}
				}
				return CreateImageFromMemory(memoryStream.GetInternalBuffer(), (vint)memoryStream.Size());
			}

			Ptr<INativeImage> WindowsImageService::CreateImageFromHBITMAP(HBITMAP handle)
			{
				IWICBitmap* bitmap=0;
				HRESULT hr=imagingFactory->CreateBitmapFromHBITMAP(handle, NULL, WICBitmapUseAlpha, &bitmap);
				if(SUCCEEDED(hr))
				{
					auto image=Ptr(new WindowsBitmapImage(this, bitmap, INativeImage::Bmp));
					bitmap->Release();
					return image;
				}
				else
				{
					return 0;
				}
			}

			Ptr<INativeImage> WindowsImageService::CreateImageFromHICON(HICON handle)
			{
				IWICBitmap* bitmap=0;
				HRESULT hr=imagingFactory->CreateBitmapFromHICON(handle, &bitmap);
				if(SUCCEEDED(hr))
				{
					auto image=Ptr(new WindowsBitmapImage(this, bitmap, INativeImage::Icon));
					bitmap->Release();
					return image;
				}
				else
				{
					return 0;
				}
			}

			IWICImagingFactory* WindowsImageService::GetImagingFactory()
			{
				return imagingFactory.Obj();
			}

/***********************************************************************
Helper Functions
***********************************************************************/

			WindowsImageService* GetWindowsImageService()
			{
				return  dynamic_cast<WindowsImageService*>(GetWindowsNativeController()->ImageService());
			}

			IWICImagingFactory* GetWICImagingFactory()
			{
				return GetWindowsImageService()->GetImagingFactory();
			}

			IWICBitmap* GetWICBitmap(INativeImageFrame* frame)
			{
				return dynamic_cast<WindowsImageFrame*>(frame)->GetFrameBitmap();
			}

			Ptr<INativeImage> CreateImageFromHBITMAP(HBITMAP handle)
			{
				return GetWindowsImageService()->CreateImageFromHBITMAP(handle);
			}

			Ptr<INativeImage> CreateImageFromHICON(HICON handle)
			{
				return GetWindowsImageService()->CreateImageFromHICON(handle);
			}
		}
	}
}

/***********************************************************************
.\SERVICESIMPL\WINDOWSINPUTSERVICE.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace windows
		{
			bool WinIsKeyPressing(VKEY code)
			{
				return (GetKeyState((int)code)&0xF0)!=0;
			}

			bool WinIsKeyToggled(VKEY code)
			{
				return (GetKeyState((int)code)&0x0F)!=0;
			}

/***********************************************************************
WindowsInputService
***********************************************************************/

			WString WindowsInputService::GetKeyNameInternal(VKEY code)
			{
				if ((vint)code < 8) return L"?";
				wchar_t name[256] = { 0 };
				vint scanCode = MapVirtualKey((int)code, MAPVK_VK_TO_VSC) << 16;
				switch ((vint)code)
				{
				case VK_INSERT:
				case VK_DELETE:
				case VK_HOME:
				case VK_END:
				case VK_PRIOR:
				case VK_NEXT:
				case VK_LEFT:
				case VK_RIGHT:
				case VK_UP:
				case VK_DOWN:
					scanCode |= 1 << 24;
					break;
				case VK_CLEAR:
				case VK_LSHIFT:
				case VK_RSHIFT:
				case VK_LCONTROL:
				case VK_RCONTROL:
				case VK_LMENU:
				case VK_RMENU:
					return L"?";
				}
				GetKeyNameText((int)scanCode, name, sizeof(name) / sizeof(*name));
				if (name[0])
				{
					WString result = name;
					vint index = predefinedKeys.Keys().IndexOf(result);
					if (index != -1 && predefinedKeys.Values()[index] == code)
					{
						return result;
					}
				}
				return WString::Unmanaged(L"?");
			}

			void WindowsInputService::InitializeKeyNames()
			{
#define INITIALIZE_KEY_NAME(NAME, TEXT)\
				keyNames[(vint)VKEY::KEY_ ## NAME] = WString::Unmanaged(TEXT);\
				if (!predefinedKeys.Keys().Contains(WString::Unmanaged(TEXT))) predefinedKeys.Add(WString::Unmanaged(TEXT), VKEY::KEY_ ## NAME);\

				GUI_DEFINE_KEYBOARD_WINDOWS_NAME(INITIALIZE_KEY_NAME)
#undef INITIALIZE_KEY_NAME

				for (vint i = 0; i < keyNames.Count(); i++)
				{
					keyNames[i] = GetKeyNameInternal((VKEY)i);
					if (keyNames[i] != L"?")
					{
						keys.Set(keyNames[i], (VKEY)i);
					}
				}
			}

			WindowsInputService::WindowsInputService()
				:ownerHandle(NULL)
				,isTimerEnabled(false)
				,keyNames((vint)VKEY::KEY_MAXIMUM)
			{
				InitializeKeyNames();
			}

			void WindowsInputService::SetOwnerHandle(HWND handle)
			{
				ownerHandle=handle;
			}

			void WindowsInputService::StartTimer()
			{
				if(!IsTimerEnabled())
				{
					SetTimer(ownerHandle, 1, 16, NULL);
					isTimerEnabled=true;
				}
			}

			void WindowsInputService::StopTimer()
			{
				if(IsTimerEnabled())
				{
					KillTimer(ownerHandle, 1);
					isTimerEnabled=false;
				}
			}

			bool WindowsInputService::IsTimerEnabled()
			{
				return isTimerEnabled;
			}
				
			bool WindowsInputService::IsKeyPressing(VKEY code)
			{
				return WinIsKeyPressing(code);
			}

			bool WindowsInputService::IsKeyToggled(VKEY code)
			{
				return WinIsKeyToggled(code);
			}

			WString WindowsInputService::GetKeyName(VKEY code)
			{
				if (0 <= (vint)code && (vint)code < keyNames.Count())
				{
					return keyNames[(vint)code];
				}
				else
				{
					return WString::Unmanaged(L"?");
				}
			}

			VKEY WindowsInputService::GetKey(const WString& name)
			{
				vint index = keys.Keys().IndexOf(name);
				return index == -1 ? VKEY::KEY_UNKNOWN : keys.Values()[index];
			}

			vint WindowsInputService::RegisterGlobalShortcutKey(bool ctrl, bool shift, bool alt, VKEY key)
			{
				UINT modifier = 0;
				if (ctrl) modifier |= MOD_CONTROL;
				if (shift) modifier |= MOD_SHIFT;
				if (alt) modifier |= MOD_ALT;

				vint id = ++usedHotKeys;
				BOOL result = RegisterHotKey(ownerHandle, (int)id, modifier, (UINT)key);
				if (result == 0) return (vint)NativeGlobalShortcutKeyResult::Occupied;
				return id;
			}

			bool WindowsInputService::UnregisterGlobalShortcutKey(vint id)
			{ 
				BOOL result = UnregisterHotKey(ownerHandle, (int)id);
				return result != 0;
			}
		}
	}
}

/***********************************************************************
.\SERVICESIMPL\WINDOWSRESOURCESERVICE.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace windows
		{

/***********************************************************************
WindowsCursor
***********************************************************************/

			WindowsCursor::WindowsCursor(HCURSOR _handle)
				: handle(_handle)
				, isSystemCursor(false)
				, systemCursorType(INativeCursor::Arrow)
			{
			}

			WindowsCursor::WindowsCursor(SystemCursorType type)
				:handle(NULL)
				, isSystemCursor(true)
				, systemCursorType(type)
			{
				LPWSTR id = NULL;
				switch (type)
				{
				case SmallWaiting:
					id = IDC_APPSTARTING;
					break;
				case LargeWaiting:
					id = IDC_WAIT;
					break;
				case Arrow:
					id = IDC_ARROW;
					break;
				case Cross:
					id = IDC_CROSS;
					break;
				case Hand:
					id = IDC_HAND;
					break;
				case Help:
					id = IDC_HELP;
					break;
				case IBeam:
					id = IDC_IBEAM;
					break;
				case SizeAll:
					id = IDC_SIZEALL;
					break;
				case SizeNESW:
					id = IDC_SIZENESW;
					break;
				case SizeNS:
					id = IDC_SIZENS;
					break;
				case SizeNWSE:
					id = IDC_SIZENWSE;
					break;
				case SizeWE:
					id = IDC_SIZEWE;
					break;
				}
				handle = (HCURSOR)LoadImage(NULL, id, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
			}
				
			bool WindowsCursor::IsSystemCursor()
			{
				return isSystemCursor;
			}

			INativeCursor::SystemCursorType WindowsCursor::GetSystemCursorType()
			{
				return systemCursorType;
			}

			HCURSOR WindowsCursor::GetCursorHandle()
			{
				return handle;
			}

/***********************************************************************
WindowsResourceService
***********************************************************************/

			WindowsResourceService::WindowsResourceService()
			{
				{
					systemCursors.Resize(INativeCursor::SystemCursorCount);
					for (vint i = 0; i < systemCursors.Count(); i++)
					{
						systemCursors[i] = Ptr(new WindowsCursor((INativeCursor::SystemCursorType)i));
					}
				}
				{
					NONCLIENTMETRICS metrics;
					metrics.cbSize = sizeof(NONCLIENTMETRICS);
					SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0);
					if (!*metrics.lfMessageFont.lfFaceName)
					{
						metrics.cbSize = sizeof(NONCLIENTMETRICS) - sizeof(metrics.iPaddedBorderWidth);
						SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0);
					}
					defaultFont.fontFamily = metrics.lfMessageFont.lfFaceName;
					defaultFont.size = metrics.lfMessageFont.lfHeight;
					if (defaultFont.size < 0)
					{
						defaultFont.size = -defaultFont.size;
					}
				}
			}

			INativeCursor* WindowsResourceService::GetSystemCursor(INativeCursor::SystemCursorType type)
			{
				vint index = (vint)type;
				if (0 <= index && index < systemCursors.Count())
				{
					return systemCursors[index].Obj();
				}
				else
				{
					return 0;
				}
			}

			INativeCursor* WindowsResourceService::GetDefaultSystemCursor()
			{
				return GetSystemCursor(INativeCursor::Arrow);
			}

			FontProperties WindowsResourceService::GetDefaultFont()
			{
				return defaultFont;
			}

			void WindowsResourceService::SetDefaultFont(const FontProperties& value)
			{
				defaultFont = value;
			}

			void WindowsResourceService::EnumerateFonts(collections::List<WString>& fonts)
			{
				auto proc = [](const LOGFONTW* lpelf, const TEXTMETRICW* lpntm, DWORD fontType, LPARAM lParam) -> int
				{
					auto&& fonts = *(collections::List<WString>*)lParam;
					fonts.Add(lpelf->lfFaceName);
					return 1;
				};

				HDC refHdc = GetDC(NULL);
				EnumFontFamilies(refHdc, NULL, proc, (LPARAM)&fonts);
				ReleaseDC(NULL, refHdc);
			}
		}
	}
}

/***********************************************************************
.\SERVICESIMPL\WINDOWSSCREENSERVICE.CPP
***********************************************************************/
#define _WINSOCKAPI_

namespace vl
{
	namespace presentation
	{
		namespace windows
		{

/***********************************************************************
WindowsScreen
***********************************************************************/

			WindowsScreen::WindowsScreen()
			{
				monitor=NULL;
			}

			NativeRect WindowsScreen::GetBounds()
			{
				MONITORINFOEX info;
				info.cbSize=sizeof(MONITORINFOEX);
				GetMonitorInfo(monitor, &info);
				return NativeRect(info.rcMonitor.left, info.rcMonitor.top, info.rcMonitor.right, info.rcMonitor.bottom);
			}

			NativeRect WindowsScreen::GetClientBounds()
			{
				MONITORINFOEX info;
				info.cbSize=sizeof(MONITORINFOEX);
				GetMonitorInfo(monitor, &info);
				return NativeRect(info.rcWork.left, info.rcWork.top, info.rcWork.right, info.rcWork.bottom);
			}

			WString WindowsScreen::GetName()
			{
				MONITORINFOEX info;
				info.cbSize=sizeof(MONITORINFOEX);
				GetMonitorInfo(monitor, &info);
					
				wchar_t buffer[sizeof(info.szDevice)/sizeof(*info.szDevice)+1];
				memset(buffer, 0, sizeof(buffer));
				memcpy(buffer, info.szDevice, sizeof(info.szDevice));
				return buffer;
			}

			bool WindowsScreen::IsPrimary()
			{
				MONITORINFOEX info;
				info.cbSize=sizeof(MONITORINFOEX);
				GetMonitorInfo(monitor, &info);
				return info.dwFlags==MONITORINFOF_PRIMARY;
			}

			double WindowsScreen::GetScalingX()
			{
				UINT x = 0, y = 0;
				DpiAwared_GetDpiForMonitor(monitor, &x, &y);
				return x / 96.0;
			}

			double WindowsScreen::GetScalingY()
			{
				UINT x = 0, y = 0;
				DpiAwared_GetDpiForMonitor(monitor, &x, &y);
				return y / 96.0;
			}

/***********************************************************************
WindowsScreenService
***********************************************************************/

			WindowsScreenService::WindowsScreenService(HandleRetriver _handleRetriver)
				:handleRetriver(_handleRetriver)
			{
			}

			BOOL CALLBACK WindowsScreenService::MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
			{
				MonitorEnumProcData* data=(MonitorEnumProcData*)dwData;
				if(data->currentScreen==data->screenService->screens.Count())
				{
					data->screenService->screens.Add(Ptr(new WindowsScreen));
				}
				data->screenService->screens[data->currentScreen]->monitor=hMonitor;
				data->currentScreen++;
				return TRUE;
			}

			void WindowsScreenService::RefreshScreenInformation()
			{
				// TODO: (enumerable) foreach
				for(vint i=0;i<screens.Count();i++)
				{
					screens[i]->monitor=NULL;
				}
				MonitorEnumProcData data;
				data.screenService=this;
				data.currentScreen=0;
				EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)(&data));
			}
				
			vint WindowsScreenService::GetScreenCount()
			{
				RefreshScreenInformation();
				return GetSystemMetrics(SM_CMONITORS);
			}

			INativeScreen* WindowsScreenService::GetScreen(vint index)
			{
				RefreshScreenInformation();
				return screens[index].Obj();
			}

			INativeScreen* WindowsScreenService::GetScreen(INativeWindow* window)
			{
				RefreshScreenInformation();
				HWND hwnd=handleRetriver(window);
				if(hwnd)
				{
					HMONITOR monitor=MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
					if(monitor!=NULL)
					{
						// TODO: (enumerable) foreach
						for(vint i=0;i<screens.Count();i++)
						{
							if(screens[i]->monitor==monitor)
							{
								return screens[i].Obj();
							}
						}
					}
				}
				return 0;
			}
		}
	}
}
