diff --git a/Demos/bdwallpaper/BDWallPaper.aps b/Demos/bdwallpaper/BDWallPaper.aps new file mode 100644 index 00000000..fc22b1cd Binary files /dev/null and b/Demos/bdwallpaper/BDWallPaper.aps differ diff --git a/Demos/bdwallpaper/BDWallPaper.cpp b/Demos/bdwallpaper/BDWallPaper.cpp new file mode 100644 index 00000000..c057c225 --- /dev/null +++ b/Demos/bdwallpaper/BDWallPaper.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "resource.h" +#include "MainWnd.h" + +static LPBYTE resource_zip_buffer_ = NULL; + +//#define USE_EMBEDED_RESOURCE + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow) +{ + CPaintManagerUI::SetInstance(hInstance); + + HRESULT Hr = ::CoInitialize(NULL); + if( FAILED(Hr) ) return 0; + + CMainWnd* pFrame = new CMainWnd(); + if( pFrame == NULL ) return 0; + pFrame->Create(NULL, _T("ѸϷ"), UI_WNDSTYLE_FRAME, 0L, 0, 0, 884, 652); + pFrame->CenterWindow(); + + CPaintManagerUI::MessageLoop(); + + ::CoUninitialize(); + return 0; +} \ No newline at end of file diff --git a/Demos/bdwallpaper/BDWallPaper.rc b/Demos/bdwallpaper/BDWallPaper.rc new file mode 100644 index 00000000..79de65ae Binary files /dev/null and b/Demos/bdwallpaper/BDWallPaper.rc differ diff --git a/Demos/bdwallpaper/BDWallPaper.vcxproj b/Demos/bdwallpaper/BDWallPaper.vcxproj new file mode 100644 index 00000000..a98bfd1e --- /dev/null +++ b/Demos/bdwallpaper/BDWallPaper.vcxproj @@ -0,0 +1,210 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + BDWallPaper + {565089A1-60C0-4281-99D7-A459E7354EEF} + 360 + + + + + + + + + + + + Application + false + Unicode + + + Application + false + Unicode + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)bin\ + $(SolutionDir)temp\BDWallPaper\$(Configuration)\ + true + $(SolutionDir)bin\ + $(SolutionDir)temp\BDWallPaper\$(Configuration)\ + false + AllRules.ruleset + + + AllRules.ruleset + + + $(ProjectName)_d + $(ProjectName) + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/BDWallPaper.tlb + + + + + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + Use + stdafx.h + true + Level3 + true + ProgramDatabase + helper;controlex;helper\jsoncpp; + + + _DEBUG;%(PreprocessorDefinitions) + 0x0406 + + + true + true + true + Windows + MachineX86 + + + true + .\Debug/BDWallPaper.bsc + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/BDWallPaper.tlb + + + + + MinSpace + OnlyExplicitInline + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreaded + true + Use + stdafx.h + Level3 + true + helper;controlex;helper\jsoncpp; + + + NDEBUG;%(PreprocessorDefinitions) + 0x0406 + + + true + Windows + MachineX86 + + + + + true + .\Release/BDWallPaper.bsc + + + + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + + + %(PreprocessorDefinitions) + Create + %(PreprocessorDefinitions) + Create + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + + + + + + + + + \ No newline at end of file diff --git a/Demos/bdwallpaper/BDWallPaper.vcxproj.filters b/Demos/bdwallpaper/BDWallPaper.vcxproj.filters new file mode 100644 index 00000000..f812d7bb --- /dev/null +++ b/Demos/bdwallpaper/BDWallPaper.vcxproj.filters @@ -0,0 +1,132 @@ + + + + + {c75b9d7c-452a-4867-a585-e8aee4a05f51} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {249f81db-516d-4a60-9882-d3fd674e412c} + h;hpp;hxx;hm;inl + + + {057557f5-517e-47cf-a695-8b482beaf54a} + + + {effdb570-d6be-45fe-9ac5-7dedf6a14a93} + + + {8b578ff8-5c34-471e-8ab6-0dccfac7dbb4} + + + {ff8c0161-870e-4f0d-abcb-a2e94b301694} + + + {ce067cf0-2c17-4761-9766-9a8cbab56a7c} + + + + + Source Files + + + Source Files + + + Source Files + + + ControlEx + + + Helper + + + Helper + + + Helper\jsoncpp + + + Helper\jsoncpp + + + Helper\jsoncpp + + + + + Header Files + + + Header Files + + + Header Files + + + ControlEx + + + Helper\jsoncpp + + + Helper\jsoncpp + + + Helper\jsoncpp + + + Helper\jsoncpp + + + Helper\jsoncpp + + + Helper\jsoncpp + + + Helper\jsoncpp + + + Helper\jsoncpp + + + Helper\jsoncpp + + + Header Files + + + ControlEx + + + ControlEx + + + ControlEx + + + ControlEx + + + Helper + + + + + Resources + + + Resources\skin + + + Helper\jsoncpp + + + + + Resources + + + \ No newline at end of file diff --git a/Demos/bdwallpaper/BDWallPaper.vcxproj.user b/Demos/bdwallpaper/BDWallPaper.vcxproj.user new file mode 100644 index 00000000..ace9a86a --- /dev/null +++ b/Demos/bdwallpaper/BDWallPaper.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Demos/bdwallpaper/DataMgr.h b/Demos/bdwallpaper/DataMgr.h new file mode 100644 index 00000000..d0206838 --- /dev/null +++ b/Demos/bdwallpaper/DataMgr.h @@ -0,0 +1,189 @@ +#include "StdAfx.h" +#include +#include +#pragma comment(lib, "WinInet.lib") +#include +using namespace std; +#include "json.h" +#include +#pragma comment(lib, "Urlmon.lib") + +#define WM_GETWPPAGE_OK WM_USER + 100 +#define WM_GETWPINFO_OK WM_USER + 101 + +struct WPInfoKey +{ + CString sType; + int nPage; +}; + +static bool operator == (const WPInfoKey& key1, const WPInfoKey& key2) +{ + return (key1.sType.CompareNoCase(key2.sType) == 0) && (key1.nPage == key2.nPage); +} + +static bool operator < (const WPInfoKey& key1, const WPInfoKey& key2) +{ + if(key1.sType.CompareNoCase(key2.sType) == 0) + { + return key1.nPage < key2.nPage; + } + else + { + return key1.sType < key2.sType; + } +} + +struct WPPage +{ + CString sType; + int nTotalPage; +}; + +struct WPInfo +{ + string flag; + string flag_desc; + string id; + string thumb_mid; + string thumb_nail; +}; + +class CDataMgr +{ +public: + CDataMgr() + { + m_hWnd = NULL; + } + +public: + void Init(HWND hWnd) + { + m_hWnd = hWnd; + } + + int LoadBizhi(CString sType, int nPage, int nCount = 10) + { + WPInfoKey* pKey = new WPInfoKey; + pKey->sType = sType; + pKey->nPage = nPage; + if(m_mapWPInfos.find(*pKey) != m_mapWPInfos.end()) + { + return 1; + } + + CString sUrl; + sUrl.Format(_T("http://bizhi.baidu.com/wallpaper/getList?type=%s&page=%d&count=%d&g=C_0-D_100825PBN40317ERB89E-M_206A8A1263C4-V_DE5571AC-T_20140714225619485&tn=bdwp&version=2.0.0.1160&_t=%d"), sType, nPage, nCount, GetTickCount()); + std::string data = WebReadFile(sUrl); + + Json::Reader reader; + Json::Value root; + if (!reader.parse(data.c_str(), root, false)) + { + return -1; + } + + string errormsg = root["errormsg"].asString(); + int errorno = root["errorno"].asInt(); + if(errorno != 0) return -1; + int ntotal = 0; + if(root["totalPage"].type() == Json::intValue) + { + ntotal = root["totalPage"].asInt(); + } + else + { + string totalpage = root["totalPage"].asString(); + ntotal = atoi(totalpage.c_str()); + } + // ҳ + WPPage *pPage = new WPPage; + pPage->sType = sType; + pPage->nTotalPage = ntotal; + m_mapWPPages[sType] = *pPage; + ::PostMessage(m_hWnd, WM_GETWPPAGE_OK, (WPARAM)pPage, 0); + + int page = root["page"].asInt(); + Json::Value urls = root["list"]; + for(int i = 0; i < urls.size(); ++i) + { + Json::Value url = urls[i]; + WPInfo Info; + Info.flag = url["flag"].asString(); + Info.flag_desc = url["flag_desc"].asString(); + Info.id = url["id"].asString(); + Info.thumb_mid = url["thumb_mid"].asString(); + Info.thumb_nail = url["thumbnail"].asString(); + m_mapWPInfos[*pKey].push_back(Info); + } + + ::PostMessage(m_hWnd, WM_GETWPINFO_OK, (WPARAM)pKey, 0); + return 0; + } + + bool GetBizhi(CString sType, int nPage, std::vector& vInfos) + { + WPInfoKey Key; + Key.sType = sType; + Key.nPage = nPage; + std::map>::iterator it = m_mapWPInfos.find(Key); + if(it != m_mapWPInfos.end()) + { + vInfos = it->second; + return true; + } + + return false; + } +private: + static string WebReadFile(CString sUrl) + { + string data; + // http + HINTERNET hConnect = InternetOpen(NULL, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + + if (hConnect) + { + DWORD dwTimeOut = 0; + InternetSetOption(hConnect, INTERNET_OPTION_CONNECT_TIMEOUT, &dwTimeOut, sizeof(dwTimeOut)); + + HINTERNET hSession = InternetOpenUrl(hConnect, sUrl, NULL, 0, INTERNET_FLAG_TRANSFER_BINARY | INTERNET_FLAG_PRAGMA_NOCACHE, 0); + if (hSession) + { + // ݻ + DWORD dwRead = 0; + DWORD dwBuffer = 1024 * 1024; + char *szBuffer = new char[dwBuffer]; + memset(szBuffer, 0, dwBuffer); + + if(InternetReadFile(hSession, szBuffer, dwBuffer, &dwRead) && (dwRead > 0)) + { + int nLen = dwRead; + char* pBuffer = new char[nLen + 1]; + memset(pBuffer, 0, nLen + 1); + memcpy(pBuffer, szBuffer, nLen); + if(pBuffer != NULL) + { + data = pBuffer; + } + } + + // ݻ + delete []szBuffer; + szBuffer = NULL; + + InternetCloseHandle(hSession); + } + InternetCloseHandle(hConnect); + } + + return data; + } + + +private: + HWND m_hWnd; + std::map m_mapWPPages; + std::map> m_mapWPInfos; +}; \ No newline at end of file diff --git a/Demos/bdwallpaper/Debug/BDWallPaper.bsc b/Demos/bdwallpaper/Debug/BDWallPaper.bsc new file mode 100644 index 00000000..ca3ba4a8 Binary files /dev/null and b/Demos/bdwallpaper/Debug/BDWallPaper.bsc differ diff --git a/Demos/bdwallpaper/MainWnd.cpp b/Demos/bdwallpaper/MainWnd.cpp new file mode 100644 index 00000000..3cc48717 --- /dev/null +++ b/Demos/bdwallpaper/MainWnd.cpp @@ -0,0 +1,242 @@ +#include "StdAfx.h" +#include "MainWnd.h" +#include +////////////////////////////////////////////////////////////////////////// +/// + +DUI_BEGIN_MESSAGE_MAP(CMainPage, CNotifyPump) + DUI_ON_MSGTYPE(DUI_MSGTYPE_CLICK,OnClick) + DUI_ON_MSGTYPE(DUI_MSGTYPE_SELECTCHANGED,OnSelectChanged) + DUI_ON_MSGTYPE(DUI_MSGTYPE_ITEMCLICK,OnItemClick) + DUI_END_MESSAGE_MAP() + + CMainPage::CMainPage() +{ + m_pPaintManager = NULL; +} + +void CMainPage::SetPaintMagager(CPaintManagerUI* pPaintMgr) +{ + m_pPaintManager = pPaintMgr; +} + +void CMainPage::OnClick(TNotifyUI& msg) +{ + +} + +void CMainPage::OnSelectChanged( TNotifyUI &msg ) +{ + +} + +void CMainPage::OnItemClick( TNotifyUI &msg ) +{ + +} + +////////////////////////////////////////////////////////////////////////// +/// + +DUI_BEGIN_MESSAGE_MAP(CMainWnd, WindowImplBase) + DUI_ON_MSGTYPE(DUI_MSGTYPE_CLICK,OnClick) + DUI_ON_MSGTYPE(DUI_MSGTYPE_SELECTCHANGED,OnSelectChanged) + DUI_ON_MSGTYPE(DUI_MSGTYPE_ITEMCLICK,OnItemClick) + DUI_END_MESSAGE_MAP() + + CMainWnd::CMainWnd(void) +{ + m_MainPage.SetPaintMagager(&m_PaintManager); + + AddVirtualWnd(_T("mainpage"),&m_MainPage); +} + +CMainWnd::~CMainWnd(void) +{ + RemoveVirtualWnd(_T("mainpage")); +} + +DuiLib::CDuiString CMainWnd::GetSkinFolder() +{ +#ifdef _DEBUG + return _T("skin\\BDWallPaper\\"); +#else + return _T("skin\\"); +#endif +} + +DuiLib::CDuiString CMainWnd::GetSkinFile() +{ + return _T("main.xml"); +} + +UILIB_RESOURCETYPE CMainWnd::GetResourceType() const +{ +#ifdef _DEBUG + return UILIB_FILE; +#else + return UILIB_ZIPRESOURCE; +#endif +} + +LPCTSTR CMainWnd::GetResourceID() const +{ + return _T("IDR_SKIN"); +} + +DuiLib::CDuiString CMainWnd::GetZIPFileName() const +{ + return _T("skin.zip"); +} + +LPCTSTR CMainWnd::GetWindowClassName( void ) const +{ + return _T("MainWnd"); +} + +CControlUI* CMainWnd::CreateControl(LPCTSTR pstrClass) +{ + return ::CreateControl(pstrClass); +} + +void CMainWnd::OnFinalMessage( HWND hWnd) +{ + __super::OnFinalMessage(hWnd); + delete this; +} + +struct TABBTN +{ + CString sType; + CString sText; +}; +void CMainWnd::InitWindow() +{ + // ʼݹ + m_Data.Init(m_hWnd); + // ఴť + CHorizontalLayoutUI* pTabBar = (CHorizontalLayoutUI*)m_PaintManager.FindControl(_T("tabbar")); + if(pTabBar) + { + TABBTN aTypes[] = { + {_T("8009|1003|1004|1007|1002|1005|1006|1001|1008"), _T("ѡ")}, + {_T("1003"), _T("羰")}, + {_T("1004"), _T("Ů")}, + {_T("1007"), _T("С")}, + {_T("1002"), _T("")}, + {_T("1005"), _T("ȳ")}, + {_T("1006"), _T("")}, + {_T("1001"), _T("")}, + {_T("1008"), _T("")}, + {_T("8009"), _T("ɫ")}, + }; + COptionUI* pFirst = NULL; + for(int i = 0; i < sizeof(aTypes) / sizeof(TABBTN); ++i) + { + TABBTN Tab = aTypes[i]; + COptionUI* pOption = new COptionUI(); + pOption->SetName(_T("wp_tabbtn")); + pOption->SetText(Tab.sText); + pOption->SetUserData(Tab.sType); + pOption->SetGroup(_T("tabbar")); + pOption->SetFixedHeight(25); + pOption->SetFixedWidth(65); + pOption->SetHotImage(_T("file='main/224.bmp' corner='4,4,4,4'")); + pOption->SetSelectedImage(_T("file='main/224.bmp' corner='4,4,4,4'")); + pTabBar->Add(pOption); + if(pFirst == NULL) pFirst = pOption; + } + + if(pFirst != NULL) pFirst->Selected(true); + } + + m_Data.LoadBizhi(_T("8009|1003|1004|1007|1002|1005|1006|1001|1008"), 1, 10); +} + +void CMainWnd::Notify( TNotifyUI &msg ) +{ + if(msg.sType.CompareNoCase(DUI_MSGTYPE_SCROLL) == 0) + { + CVerticalLayoutUI* pTabPanel = (CVerticalLayoutUI*)msg.pSender; + CString sName = msg.pSender->GetName(); + int nPage = (int)msg.wParam; + CString sTabPanel = sName; + int nIdx = sTabPanel.Find(_T("@")); + if(nIdx == -1) return; + CString sType = sTabPanel.Mid(nIdx + 1); + if(nPage < 1) nPage = 1; + if(nPage > 200) nPage = 200; + + int nScrollPos = (nPage - 1) * pTabPanel->GetVerticalScrollBar()->GetLineSize(); + SIZE szSize = {0, nScrollPos}; + pTabPanel->SetScrollPos(szSize, false); + + m_Data.LoadBizhi(sType, nPage, 10); + } + else if(msg.sType.CompareNoCase(DUI_MSGTYPE_SELECTCHANGED) == 0) + { + COptionUI* pTabBtn = (COptionUI*)msg.pSender; + CTabLayoutUI* pTabLayout = (CTabLayoutUI*)m_PaintManager.FindControl(_T("wp_tablayout")); + CString sName = pTabBtn->GetName(); + if(sName == _T("wp_tabbtn") && pTabLayout) + { + CString sType = pTabBtn->GetUserData(); + CString sTabPanel; + sTabPanel.Format(_T("wp_tabpanel@%s"), sType); + CVerticalLayoutUI* pTabPanel = (CVerticalLayoutUI*)m_PaintManager.FindControl(sTabPanel); + if(pTabPanel == NULL) + { + m_Data.LoadBizhi(sType, 1, 10); + } + else + { + pTabLayout->SelectItem(pTabPanel); + } + } + } + else if(msg.sType.CompareNoCase(DUI_MSGTYPE_CLICK_WPFAV) == 0) + { + MessageBox(NULL, _T("ղسɹ"), _T("ٶȱֽ - ʾ"), MB_OK); + } + else if(msg.sType.CompareNoCase(DUI_MSGTYPE_CLICK_WPSET) == 0) + { + CWPButton* pWPBtn = (CWPButton*)msg.pSender; + pWPBtn->SetWP(); + } + return WindowImplBase::Notify(msg); +} + +void CMainWnd::OnClick( TNotifyUI &msg ) +{ + CDuiString sCtrlName = msg.pSender->GetName(); + if( sCtrlName == _T("closebtn") ) + { + Close(); + return; + } + else if( sCtrlName == _T("minbtn")) + { + SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0); + return; + } + else if( sCtrlName == _T("maxbtn")) + { + SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, 0); + return; + } + else if( sCtrlName == _T("restorebtn")) + { + SendMessage(WM_SYSCOMMAND, SC_RESTORE, 0); + return; + } +} + +void CMainWnd::OnSelectChanged( TNotifyUI &msg ) +{ + +} + +void CMainWnd::OnItemClick( TNotifyUI &msg ) +{ + +} \ No newline at end of file diff --git a/Demos/bdwallpaper/MainWnd.h b/Demos/bdwallpaper/MainWnd.h new file mode 100644 index 00000000..cfe2a5fe --- /dev/null +++ b/Demos/bdwallpaper/MainWnd.h @@ -0,0 +1,165 @@ +#pragma once + +#include "Utils.h" +#include "DataMgr.h" +#include "ControlInclude.h" +////////////////////////////////////////////////////////////////////////// +/// + +class CMainPage : public CNotifyPump +{ +public: + CMainPage(); + +public: + void SetPaintMagager(CPaintManagerUI* pPaintMgr); + + DUI_DECLARE_MESSAGE_MAP() + virtual void OnClick(TNotifyUI& msg); + virtual void OnSelectChanged( TNotifyUI &msg ); + virtual void OnItemClick( TNotifyUI &msg ); + +private: + CPaintManagerUI* m_pPaintManager; +}; + +////////////////////////////////////////////////////////////////////////// +/// + +class CMainWnd : public WindowImplBase +{ +public: + CMainWnd(void); + ~CMainWnd(void); + +public: + virtual LPCTSTR GetWindowClassName( void ) const; + + virtual CDuiString GetSkinFolder(); + virtual CDuiString GetSkinFile(); + virtual UILIB_RESOURCETYPE GetResourceType() const; + virtual LPCTSTR GetResourceID() const; + virtual CDuiString GetZIPFileName() const; + + virtual CControlUI* CreateControl(LPCTSTR pstrClass); + virtual void OnFinalMessage( HWND ); + virtual void InitWindow(); + virtual LRESULT HandleCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + bHandled = FALSE; + + LRESULT lResult = 0; + + switch(uMsg) + { + case WM_GETWPPAGE_OK: + { + lResult = OnGetWPPageOK(uMsg, wParam, lParam, bHandled); + break; + } + case WM_GETWPINFO_OK: + { + lResult = OnGetWPInfoOK(uMsg, wParam, lParam, bHandled); + break; + } + default: break; + } + return lResult; + } +public: + virtual LRESULT OnClose(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) + { + PostQuitMessage(0); + return 0; + } + + virtual LRESULT OnGetWPPageOK(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) + { + WPPage* pPage = (WPPage*)wParam; + if(pPage != NULL) + { + CTabLayoutUI* pTabLayout = (CTabLayoutUI*)m_PaintManager.FindControl(_T("wp_tablayout")); + if(pTabLayout) + { + CString sTabPanel; + sTabPanel.Format(_T("wp_tabpanel@%s"), pPage->sType); + CVerticalLayoutUI* pTabPanel = (CVerticalLayoutUI*)m_PaintManager.FindControl(sTabPanel); + if(pTabPanel == NULL) + { + pTabPanel = new CVerticalLayoutUI(); + pTabPanel->SetName(sTabPanel); + pTabPanel->SetFixedHeight(pTabLayout->GetFixedHeight()); + pTabLayout->Add(pTabPanel); + pTabPanel->EnableScrollBar(true, false); + pTabPanel->SetScrollStepSize(pTabLayout->GetFixedHeight() - 12); + pTabPanel->GetVerticalScrollBar()->SetLineSize(pTabLayout->GetFixedHeight() - 12); + pTabLayout->SelectItem(pTabPanel); + for(int i = 1; i <= pPage->nTotalPage; i++) + { + CString sPanel; + sPanel.Format(_T("wp_panel_%s_%d"), pPage->sType, i); + CWPPanel* pPanel = new CWPPanel(); + pPanel->SetFixedHeight(pTabPanel->GetFixedHeight() - 12); + pPanel->SetName(sPanel); + pTabPanel->Add(pPanel); + DoEvents(); + } + } + } + + delete pPage; + pPage = NULL; + } + return 0; + } + + virtual LRESULT OnGetWPInfoOK(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) + { + WPInfoKey* pKey = (WPInfoKey*)wParam; + if(pKey != NULL) + { + CString sPanel; + sPanel.Format(_T("wp_panel_%s_%d"), pKey->sType, pKey->nPage); + CWPPanel* pPanel = (CWPPanel*)m_PaintManager.FindControl(sPanel); + if(pPanel != NULL) + { + std::vector vInfos; + if(m_Data.GetBizhi(pKey->sType, pKey->nPage, vInfos)) + { + for(int i = 0; i < vInfos.size(); i++) + { + WPInfo Info = vInfos[i]; + CString sID = Info.id.c_str(); + CString sThumbMid = Info.thumb_mid.c_str(); + CString sThumbNail = Info.thumb_nail.c_str(); + CString sButton; + sButton.Format(_T("wp_button_%s_%d"), pKey->sType, atoi(Info.id.c_str())); + CWPButton* pButton = new CWPButton(); + pButton->SetName(sButton); + pButton->SetWPImage(sThumbMid); + pButton->SetWPToolbarImage(_T("file='main/toolbar.png' corner='4,4,4,4'")); + pButton->SetWPSetImage(_T("file='main/set.png' dest='0,0,94,25'")); + pButton->SetWPFavImage(_T("file='main/fav.png' dest='0,0,94,25'")); + pButton->SetUserData(CString(Info.id.c_str())); + pPanel->Add(pButton); + DoEvents(); + } + } + } + delete pKey; + pKey = NULL; + } + return 0; + } + +public: + virtual void Notify( TNotifyUI &msg ); + DUI_DECLARE_MESSAGE_MAP() + virtual void OnClick(TNotifyUI& msg); + virtual void OnSelectChanged( TNotifyUI &msg ); + virtual void OnItemClick( TNotifyUI &msg ); + +private: + CMainPage m_MainPage; + CDataMgr m_Data; +}; diff --git a/Demos/bdwallpaper/Res/BDWallPaper.ico b/Demos/bdwallpaper/Res/BDWallPaper.ico new file mode 100644 index 00000000..c38850de Binary files /dev/null and b/Demos/bdwallpaper/Res/BDWallPaper.ico differ diff --git a/Demos/bdwallpaper/Res/bdwallpaper.zip b/Demos/bdwallpaper/Res/bdwallpaper.zip new file mode 100644 index 00000000..feaac8dd Binary files /dev/null and b/Demos/bdwallpaper/Res/bdwallpaper.zip differ diff --git a/Demos/bdwallpaper/StdAfx.cpp b/Demos/bdwallpaper/StdAfx.cpp new file mode 100644 index 00000000..79228f61 --- /dev/null +++ b/Demos/bdwallpaper/StdAfx.cpp @@ -0,0 +1,15 @@ +// stdafx.cpp : source file that includes just the standard includes +// App.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +#if defined _M_IX86 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_IA64 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_X64 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#else +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif diff --git a/Demos/bdwallpaper/StdAfx.h b/Demos/bdwallpaper/StdAfx.h new file mode 100644 index 00000000..6191eb85 --- /dev/null +++ b/Demos/bdwallpaper/StdAfx.h @@ -0,0 +1,36 @@ + +#if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) +#define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_ + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include +#include +#include + +#include "..\..\DuiLib\UIlib.h" +using namespace DuiLib; + +#ifdef _DEBUG +# ifdef _UNICODE +# pragma comment(lib, "..\\..\\lib\\DuiLib_d.lib") +# else +# pragma comment(lib, "..\\..\\lib\\DuiLib_d.lib") +# endif +#else +# ifdef _UNICODE +# pragma comment(lib, "..\\..\\lib\\DuiLib.lib") +# else +# pragma comment(lib, "..\\..\\lib\\DuiLib.lib") +# endif +#endif + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) diff --git a/Demos/bdwallpaper/controlex/AnimationHelper.cpp b/Demos/bdwallpaper/controlex/AnimationHelper.cpp new file mode 100644 index 00000000..25d2f1d8 --- /dev/null +++ b/Demos/bdwallpaper/controlex/AnimationHelper.cpp @@ -0,0 +1,1201 @@ +#include "stdafx.h" +#include "AnimationHelper.h" + +/////////////////////////////////////////////////////////////////////////////////////// +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened +typedef DWORD ZRESULT; +typedef struct +{ + int index; // index of this file within the zip + char name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; +typedef struct +{ + int index; // index of this file within the zip + TCHAR name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; +#define OpenZip OpenZipU +#define CloseZip(hz) CloseZipU(hz) +extern HZIP OpenZipU(void *z,unsigned int len,DWORD flags); +extern ZRESULT CloseZipU(HZIP hz); +#ifdef _UNICODE +#define ZIPENTRY ZIPENTRYW +#define GetZipItem GetZipItemW +#define FindZipItem FindZipItemW +#else +#define GetZipItem GetZipItemA +#define FindZipItem FindZipItemA +#endif +extern ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze); +extern ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *ze); +extern ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze); +extern ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *ze); +extern ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags); +/////////////////////////////////////////////////////////////////////////////////////// + +extern "C" +{ + extern unsigned char *stbi_load_from_memory(unsigned char const *buffer, int len, int *x, int *y, \ + int *comp, int req_comp); + extern void stbi_image_free(void *retval_from_stbi_load); + +}; + +namespace DuiLib +{ + /************************************************************************/ + /* CAnimation */ + /************************************************************************/ + + ////////////////////////////////////////////////////////////////////// + // Nested structures member functions + ////////////////////////////////////////////////////////////////////// + inline int CAnimation::TGIFControlExt::GetPackedValue(enum ControlExtValues Value) + { + int nRet = (int) m_cPacked; + switch(Value) + { + case GCX_PACKED_DISPOSAL: + nRet = (nRet & 28) >> 2; + break; + + case GCX_PACKED_USERINPUT: + nRet = (nRet & 2) >> 1; + break; + + case GCX_PACKED_TRANSPCOLOR: + nRet &= 1; + break; + }; + + return nRet; + } + + inline int CAnimation::TGIFLSDescriptor::GetPackedValue(enum LSDPackedValues Value) + { + int nRet = (int) m_cPacked; + + switch(Value) + { + case LSD_PACKED_GLOBALCT: + nRet = nRet >> 7; + break; + + case LSD_PACKED_CRESOLUTION: + nRet = ((nRet & 0x70) >> 4) + 1; + break; + + case LSD_PACKED_SORT: + nRet = (nRet & 8) >> 3; + break; + + case LSD_PACKED_GLOBALCTSIZE: + nRet &= 7; + break; + }; + + return nRet; + } + + inline int CAnimation::TGIFImageDescriptor::GetPackedValue(enum IDPackedValues Value) + { + int nRet = (int) m_cPacked; + + switch(Value) + { + case ID_PACKED_LOCALCT: + nRet >>= 7; + break; + + case ID_PACKED_INTERLACE: + nRet = ((nRet & 0x40) >> 6); + break; + + case ID_PACKED_SORT: + nRet = (nRet & 0x20) >> 5; + break; + + case ID_PACKED_LOCALCTSIZE: + nRet &= 7; + break; + }; + + return nRet; + } + + ////////////////////////////////////////////////////////////////////// + // ctor && dtor + ////////////////////////////////////////////////////////////////////// + CAnimation::CAnimation(IAnimationCallback* pCallback) : m_pCallback(pCallback) + { + // check structures size + assert(sizeof(TGIFImageDescriptor) == 10); + assert(sizeof(TGIFAppExtension) == 14); + assert(sizeof(TGIFPlainTextExt) == 15); + assert(sizeof(TGIFLSDescriptor) == 7); + assert(sizeof(TGIFControlExt) == 8); + assert(sizeof(TGIFCommentExt) == 2); + assert(sizeof(TGIFHeader) == 6); + + m_pRawData = NULL; + m_nDataSize = 0; + m_pGIFHeader = NULL; + m_pGIFLSDescriptor = NULL; + m_nGlobalCTSize = 0; + m_PictureSize.cx = m_PictureSize.cy = 0; + m_clrBackground = RGB(255, 255, 255); // white by default + m_nCurrFrame = 0; + m_nCurrOffset = 0; + m_hThread = NULL; + m_bExitThread = true; + + m_hDrawEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); + assert(m_hDrawEvent); + m_pvFrames = new VTFRAME; + assert(m_pvFrames); + } + + CAnimation::~CAnimation() + { + UnLoad(); + delete m_pvFrames; + m_pvFrames = NULL; + CloseHandle(m_hDrawEvent); + m_hDrawEvent = NULL; + } + + const TImageInfo* CAnimation::LoadGIF(LPCTSTR bitmap, LPCTSTR type/* = NULL*/, DWORD mask/* = 0*/) + { + LPBYTE pData = NULL; + DWORD dwSize = 0; + if( type != NULL && isdigit(*bitmap) ) + { + LPTSTR pstr = NULL; + int iIndex = _tcstol(bitmap, &pstr, 10); + HRSRC hPicture = ::FindResource(CPaintManagerUI::GetResourceDll(), MAKEINTRESOURCE(iIndex), type); + if (!hPicture) + return NULL; + HGLOBAL hResData; + if (!(hResData = ::LoadResource(CPaintManagerUI::GetResourceDll(), hPicture))) + { + ::FreeResource(hPicture); + return NULL; + }; + dwSize = ::SizeofResource(CPaintManagerUI::GetResourceDll(),hPicture); + if( dwSize < sizeof(TGIFHeader)) + { + ::FreeResource(hPicture); + return NULL; + } + pData = static_cast( malloc(dwSize*sizeof(BYTE)) ); + if (!pData) + { + ::FreeResource(hPicture); + return NULL; + } + LPBYTE pSrc = static_cast( ::LockResource(hResData) ); + if (!pSrc) + { + free(pData); + ::FreeResource(hPicture); + return NULL; + } + ::CopyMemory(pData, pSrc, dwSize); + ::FreeResource(hPicture); + } + else + { + CDuiString sFile = CPaintManagerUI::GetResourcePath(); + if( CPaintManagerUI::GetResourceZip().IsEmpty() ) { + sFile += bitmap; + HANDLE hFile = INVALID_HANDLE_VALUE; + DWORD dwRead=0; + hFile = ::CreateFile(sFile.GetData(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, \ + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if(INVALID_HANDLE_VALUE == hFile) + return NULL; + dwSize = ::GetFileSize(hFile, NULL); + if (dwSize < sizeof(TGIFHeader)) + { + CloseHandle(hFile); + return NULL; + } + pData = static_cast( malloc(dwSize*sizeof(BYTE)) ); + if (!pData) + { + CloseHandle(hFile); + return NULL; + } + ::ReadFile(hFile, pData, dwSize, &dwRead, NULL); + ::CloseHandle(hFile); + } + else + { + sFile += CPaintManagerUI::GetResourceZip(); + HZIP hz = NULL; + if( CPaintManagerUI::IsCachedResourceZip() ) hz = (HZIP)CPaintManagerUI::GetResourceZipHandle(); + else hz = OpenZip((void*)sFile.GetData(), 0, 2); + if( hz == NULL ) + return NULL; + ZIPENTRY ze; + int i; + if( FindZipItem(hz, bitmap, true, &i, &ze) != 0 ) + { + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + return NULL; + } + dwSize = ze.unc_size; + if (dwSize < sizeof(TGIFHeader)) + { + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + return NULL; + } + pData = static_cast( malloc(dwSize*sizeof(BYTE)) ); + if (!pData) + { + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + return NULL; + } + int res = UnzipItem(hz, i, pData, dwSize, 3); + if( res != 0x00000000 && res != 0x00000600) { + free(pData); + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + return NULL; + } + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + } + } + + const TImageInfo* data = LoadGIF(pData, dwSize); + free(pData); + return data; + } + + const TImageInfo* CAnimation::LoadGIF(const LPBYTE pData, DWORD dwSize, DWORD mask/* = 0*/) + { + if (!pData || dwSize==0) + return NULL; + + UnLoad(); + + if(!(m_pRawData = const_cast(pData))) + return NULL; + + m_nDataSize = dwSize; + m_pGIFHeader = (TGIFHeader*)m_pRawData; + + if((memcmp(&m_pGIFHeader->m_cSignature, "GIF", 3) != 0) + && ((memcmp(&m_pGIFHeader->m_cVersion, "87a", 3) != 0) + || (memcmp(&m_pGIFHeader->m_cVersion, "89a", 3) != 0))) + { + // it's neither GIF87a nor GIF89a + // do nothing + // clear GIF variables + m_pRawData = NULL; + m_pGIFHeader = NULL; + m_nDataSize = 0; + + return NULL; + } + + m_pGIFLSDescriptor = (TGIFLSDescriptor*)(m_pRawData + sizeof(TGIFHeader)); + if(m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT) == 1) + { + // calculate the globat color table size + m_nGlobalCTSize = static_cast(3 * (1 + << (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE) + + 1))); + // get the background color if GCT is present + unsigned char * pBkClr = m_pRawData + + sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + + 3 * m_pGIFLSDescriptor->m_cBkIndex; + m_clrBackground = RGB(pBkClr[0], pBkClr[1], pBkClr[2]); + }; + + // store the picture's size + m_PictureSize.cx = m_pGIFLSDescriptor->m_wWidth; + m_PictureSize.cy = m_pGIFLSDescriptor->m_wHeight; + + // determine frame count for this picture + UINT nFrameCount = 0; + ResetDataPointer(); + while(SkipNextGraphicBlock()) + nFrameCount++; + +#ifdef GIF_TRACING + OutputDebugString(_T(" -= GIF encountered\n" + "Logical Screen dimensions = %dx%d\n" + "Global color table = %d\n" + "Color depth = %d\n" + "Sort flag = %d\n" + "Size of Global Color Table = %d\n" + "Background color index = %d\n" + "Pixel aspect ratio = %d\n" + "Frame count = %d\n" + "Background color = %06Xh\n\n"), + m_pGIFLSDescriptor->m_wWidth, + m_pGIFLSDescriptor->m_wHeight, + m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT), + m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_CRESOLUTION), + m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_SORT), + m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE), + m_pGIFLSDescriptor->m_cBkIndex, + m_pGIFLSDescriptor->m_cPixelAspect, + nFrameCount, + m_clrBackground); + EnumGIFBlocks(); +#endif + + if(nFrameCount <= 1) + { + // now check the frame count + // if there's an empty GIF or only one frame, + // no need to animate this GIF + // therefore, treat it like any other pic + m_pRawData = NULL; + return NULL; + } + + // if, on the contrary, there are several frames + // then store separate frames in an array + TFrame frame; + UINT nBlockLen = 0; + LPBYTE pFrameData = NULL; + UINT nCurFrame = 0; + ////////////////////////////////////////////////////////////////////////// + // Before rendering a frame we should take care of what's + // behind that frame. TFrame::m_nDisposal will be our guide: + // 0 - no disposal specified (do nothing) + // 1 - do not dispose (again, do nothing) + // 2 - restore to background color (m_clrBackground) + // 3 - restore to previous + // background color for first frame + LPBYTE pDispData = static_cast(malloc(m_PictureSize.cx * m_PictureSize.cy * sizeof(COLORREF))); + if (!pDispData) + { + m_pRawData = NULL; + return NULL; + } + for (int n = 0; n < m_PictureSize.cx*m_PictureSize.cy; n++) + ((COLORREF*)pDispData)[n] = m_clrBackground; + + ResetDataPointer(); + while(pFrameData = GetNextGraphicBlock(&nBlockLen, + &frame.m_nDelay, + &frame.m_frameSize, + &frame.m_frameOffset, + &frame.m_nDisposal)) + { +#ifdef GIF_TRACING + ////////////////////////////////////////////// + // uncomment the following strings if you want + // to write separate frames on disk + // + // CString szName; + // szName.Format(_T("%.4d.gif"),nCurFrame); + // WriteDataOnDisk(szName,hFrameData,nBlockLen); + // nCurFrame++; +#endif // GIF_TRACING + if (!pFrameData) + continue; + + LPBYTE pImage = NULL; + int x,y,n; + pImage = stbi_load_from_memory(pFrameData, nBlockLen, &x, &y, &n, 4); + free(pFrameData); + pFrameData = NULL; + BITMAPINFO bmi; + ::ZeroMemory(&bmi, sizeof(BITMAPINFO)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = x; + bmi.bmiHeader.biHeight = -y; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = x * y * 4; + + bool bAlphaChannel = false; + LPBYTE pDest = NULL; + HBITMAP hBitmap = ::CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**)&pDest, NULL, 0); + if( !hBitmap ) + { + stbi_image_free(pImage); + continue; + } + + for( int i = 0; i < x * y; i++ ) + { + DWORD dwPixel = pImage[i*4+3]==BYTE(0) ? ((DWORD*)pDispData)[i] : ((DWORD*)pImage)[i];; + LPBYTE pPixel = (LPBYTE)(&dwPixel); + pDest[i*4 + 3] = pPixel[3]; + if( pDest[i*4 + 3] < 255 ) + { + pDest[i*4] = (BYTE)(DWORD(pPixel[2])*pPixel[3]/255); + pDest[i*4 + 1] = (BYTE)(DWORD(pPixel[1])*pPixel[3]/255); + pDest[i*4 + 2] = (BYTE)(DWORD(pPixel[0])*pPixel[3]/255); + bAlphaChannel = true; + } + else + { + pDest[i*4] = pPixel[2]; + pDest[i*4 + 1] = pPixel[1]; + pDest[i*4 + 2] = pPixel[0]; + } + + if( *(DWORD*)(&pDest[i*4]) == mask ) { + pDest[i*4] = (BYTE)0; + pDest[i*4 + 1] = (BYTE)0; + pDest[i*4 + 2] = (BYTE)0; + pDest[i*4 + 3] = (BYTE)0; + bAlphaChannel = true; + } + } + stbi_image_free(pImage); + + switch (frame.m_nDisposal) + { + case 0: + memset(pDispData, 0, m_PictureSize.cx*m_PictureSize.cy*sizeof(COLORREF)); + break; + case 1: + memcpy(pDispData, pDest, x * y * 4); + break; + case 2: + for (int n = 0; n < m_PictureSize.cx*m_PictureSize.cy; n++) + ((COLORREF*)pDispData)[n] = m_clrBackground; + break; + case 3: + break; + } + + frame.m_pImage = new TImageInfo; + frame.m_pImage->hBitmap = hBitmap; + frame.m_pImage->nX = x; + frame.m_pImage->nY = y; + frame.m_pImage->alphaChannel = bAlphaChannel; + + nCurFrame++; + // everything went well, add this frame + m_pvFrames->push_back(frame); + }; + + // clean after ourselves + free(pDispData); pDispData = NULL; + m_pRawData = NULL; + m_nDataSize = 0; + m_pGIFHeader = NULL; + m_pGIFLSDescriptor = NULL; + m_nGlobalCTSize = 0; + m_nCurrOffset = 0; + + return m_pvFrames->empty() ? NULL : ((*m_pvFrames)[0]).m_pImage; + } + + void CAnimation::UnLoad() + { + Stop(); + + std::vector::iterator it; + for(it = m_pvFrames->begin();it < m_pvFrames->end();it++) + { + if ((*it).m_pImage) + { + ::DeleteObject((*it).m_pImage->hBitmap); + delete (*it).m_pImage; + } + } + m_pvFrames->clear(); + m_PictureSize.cx = m_PictureSize.cy = 0; + m_clrBackground = RGB(255, 255, 255); // white by default + m_nCurrFrame = 0; + } + + bool CAnimation::Play() + { + if(!m_bExitThread) + return false; + + if (m_hThread != NULL) + { + ::WaitForSingleObject(m_hThread, 5000); + ::CloseHandle(m_hThread); + m_hThread = NULL; + } + + DWORD nDummy; + m_bExitThread = false; + m_hThread = (HANDLE) ::CreateThread(NULL, + 0, + _ThreadAnimation, + (void *)this, + CREATE_SUSPENDED, + &nDummy); + if(!m_hThread) + { + m_bExitThread = true; + return false; + } + else + ::ResumeThread(m_hThread); + + return true; + } + + void CAnimation::Stop() + { + m_bExitThread = true; + ::SetEvent(m_hDrawEvent); + // we'll wait for 5 seconds then continue execution + ::WaitForSingleObject(m_hThread, 5000); + ::CloseHandle(m_hThread); + m_hThread = NULL; + } + + bool CAnimation::IsPlaying() const + { + return !m_bExitThread; + } + + SIZE CAnimation::GetSize() const + { + return m_PictureSize; + } + + int CAnimation::GetFrameCount() const + { + if (m_pvFrames) + return m_pvFrames->size(); + else + return 0; + } + + COLORREF CAnimation::GetBkColor() const + { + return m_clrBackground; + } + + const TImageInfo* CAnimation::GetCurImage() + { + TImageInfo* data = ((*m_pvFrames)[m_nCurrFrame]).m_pImage; + if (!IsPlaying()) + Play(); + ::SetEvent(m_hDrawEvent); + return data; + } + + ////////////////////////////////////////////////////////////////////// + // protected methods + ////////////////////////////////////////////////////////////////////// + int CAnimation::GetNextBlockLen() const + { + GIFBlockTypes nBlock = GetNextBlock(); + + int nTmp; + + switch(nBlock) + { + case BLOCK_UNKNOWN: + return -1; + break; + + case BLOCK_TRAILER: + return 1; + break; + + case BLOCK_APPEXT: + nTmp = GetSubBlocksLen(m_nCurrOffset + sizeof(TGIFAppExtension)); + if(nTmp > 0) + return sizeof(TGIFAppExtension) + nTmp; + break; + + case BLOCK_COMMEXT: + nTmp = GetSubBlocksLen(m_nCurrOffset + sizeof(TGIFCommentExt)); + if(nTmp > 0) + return sizeof(TGIFCommentExt) + nTmp; + break; + + case BLOCK_CONTROLEXT: + return sizeof(TGIFControlExt); + break; + + case BLOCK_PLAINTEXT: + nTmp = GetSubBlocksLen(m_nCurrOffset + sizeof(TGIFPlainTextExt)); + if(nTmp > 0) + return sizeof(TGIFPlainTextExt) + nTmp; + break; + + case BLOCK_IMAGE: + TGIFImageDescriptor* pIDescr = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + int nLCTSize = (int) + (pIDescr->GetPackedValue(ID_PACKED_LOCALCT) * 3 * (1 + << (pIDescr->GetPackedValue(ID_PACKED_LOCALCTSIZE) + + 1))); + + int nTmp = GetSubBlocksLen(m_nCurrOffset + sizeof(TGIFImageDescriptor) + nLCTSize + 1); + if(nTmp > 0) + return sizeof(TGIFImageDescriptor) + nLCTSize + 1 + nTmp; + break; + }; + + return 0; + } + + BOOL CAnimation::SkipNextBlock() + { + if(!m_pRawData) + return FALSE; + + int nLen = GetNextBlockLen(); + if((nLen <= 0) || ((m_nCurrOffset + nLen) > m_nDataSize)) + return FALSE; + + m_nCurrOffset += nLen; + return TRUE; + } + + BOOL CAnimation::SkipNextGraphicBlock() + { + if(!m_pRawData) + return FALSE; + + // GIF header + LSDescriptor [+ GCT] [+ Control block] + Data + + enum GIFBlockTypes nBlock; + + nBlock = GetNextBlock(); + + while((nBlock != BLOCK_CONTROLEXT) + && (nBlock != BLOCK_IMAGE) + && (nBlock != BLOCK_PLAINTEXT) + && (nBlock != BLOCK_UNKNOWN) + && (nBlock != BLOCK_TRAILER)) + { + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + }; + + if((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER)) + return FALSE; + + // it's either a control ext.block, an image or a plain text + + if(GetNextBlockLen() <= 0) + return FALSE; + + if(nBlock == BLOCK_CONTROLEXT) + { + if(!SkipNextBlock()) + return FALSE; + nBlock = GetNextBlock(); + + // skip everything until we meet an image block or a plain-text block + while((nBlock != BLOCK_IMAGE) + && (nBlock != BLOCK_PLAINTEXT) + && (nBlock != BLOCK_UNKNOWN) + && (nBlock != BLOCK_TRAILER)) + { + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + }; + + if((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER)) + return FALSE; + }; + + // skip the found data block (image or plain-text) + if(!SkipNextBlock()) + return FALSE; + + return TRUE; + } + + void CAnimation::ResetDataPointer() + { + // skip header and logical screen descriptor + m_nCurrOffset = sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + m_nGlobalCTSize; + } + + enum CAnimation::GIFBlockTypes CAnimation::GetNextBlock() const + { + switch(m_pRawData[m_nCurrOffset]) + { + case 0x21: + // extension block + switch(m_pRawData[m_nCurrOffset + 1]) + { + case 0x01: + // plain text extension + return BLOCK_PLAINTEXT; + break; + + case 0xF9: + // graphic control extension + return BLOCK_CONTROLEXT; + break; + + case 0xFE: + // comment extension + return BLOCK_COMMEXT; + break; + + case 0xFF: + // application extension + return BLOCK_APPEXT; + break; + }; + break; + + case 0x3B: + // trailer + return BLOCK_TRAILER; + break; + + case 0x2C: + // image data + return BLOCK_IMAGE; + break; + }; + + return BLOCK_UNKNOWN; + } + + UINT CAnimation::GetSubBlocksLen(UINT nStartingOffset) const + { + UINT nRet = 0; + UINT nCurOffset = nStartingOffset; + + while(m_pRawData[nCurOffset] != 0 ) + { + nRet += m_pRawData[nCurOffset] + 1; + nCurOffset += m_pRawData[nCurOffset] + 1; + }; + + return nRet + 1; + } + + LPBYTE CAnimation::GetNextGraphicBlock(UINT* pBlockLen, UINT* pDelay, SIZE* pBlockSize, SIZE* pBlockOffset, UINT* pDisposal) + { + if(!m_pRawData) + return NULL; + + // GIF header + LSDescriptor [+ GCT] [+ Control block] + Data + + *pDisposal = 0; + enum GIFBlockTypes nBlock; + nBlock = GetNextBlock(); + + while((nBlock != BLOCK_CONTROLEXT) + && (nBlock != BLOCK_IMAGE) + && (nBlock != BLOCK_PLAINTEXT) + && (nBlock != BLOCK_UNKNOWN) + && (nBlock != BLOCK_TRAILER)) + { + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + }; + + if((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER)) + return NULL; + + // it's either a control ext.block, an image or a plain text + + int nStart = m_nCurrOffset; + int nBlockLen = GetNextBlockLen(); + + if(nBlockLen <= 0) + return NULL; + + if(nBlock == BLOCK_CONTROLEXT) + { + // get the following data + TGIFControlExt* pControl = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + // store delay time + *pDelay = pControl->m_wDelayTime; + // store disposal method + *pDisposal = pControl->GetPackedValue(GCX_PACKED_DISPOSAL); + + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + + // skip everything until we find data to display + // (image block or plain-text block) + + while((nBlock != BLOCK_IMAGE) + && (nBlock != BLOCK_PLAINTEXT) + && (nBlock != BLOCK_UNKNOWN) + && (nBlock != BLOCK_TRAILER)) + { + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + nBlockLen += GetNextBlockLen(); + }; + + if((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER)) + return NULL; + nBlockLen += GetNextBlockLen(); + } + else + *pDelay = -1; // to indicate that there was no delay value + + if(nBlock == BLOCK_IMAGE) + { + // store size and offsets + TGIFImageDescriptor* pImage = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + pBlockSize->cx = pImage->m_wWidth; + pBlockSize->cy = pImage->m_wHeight; + pBlockOffset->cx = pImage->m_wLeftPos; + pBlockOffset->cy = pImage->m_wTopPos; + }; + + if(!SkipNextBlock()) + return NULL; + + LPBYTE pData = static_cast(malloc(sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + m_nGlobalCTSize + nBlockLen + 1)); + if (!pData) + return NULL; + + int nOffset = 0; + + CopyMemory(pData, m_pRawData, sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + m_nGlobalCTSize); + nOffset += sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + m_nGlobalCTSize; + + CopyMemory(pData + nOffset, &m_pRawData[nStart], nBlockLen); + nOffset += nBlockLen; + + pData[nOffset] = 0x3B; // trailer + nOffset++; + + *pBlockLen = nOffset; + + return pData; + } + +#ifdef GIF_TRACING + void CAnimation::WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize) + { + CFile file; + + if(!file.Open(szFileName, CFile::modeCreate | CFile::modeWrite | CFile::shareDenyNone)) + { + OutputDebugString(_T("WriteData: Error creating file %s\n"), szFileName); + return; + }; + + char* pData = reinterpret_cast(GlobalLock(hData)); + if(!pData) + { + OutputDebugString(_T("WriteData: Error locking memory\n")); + return; + }; + + TRY + { + file.Write(pData, dwSize); + } + CATCH(CFileException, e); + { + OutputDebugString(_T("WriteData: An exception occured while writing to the file %s\n"), szFileName); + e->Delete(); + GlobalUnlock(hData); + file.Close(); + return; + } + END_CATCH GlobalUnlock(hData); + file.Close(); + } + + void CAnimation::EnumGIFBlocks() + { + enum GIFBlockTypes nBlock; + ResetDataPointer(); + while(m_nCurrOffset < m_nDataSize) + { + nBlock = GetNextBlock(); + switch(nBlock) + { + case BLOCK_UNKNOWN: + OutputDebugString(_T("- Unknown block\n")); + return; + break; + case BLOCK_TRAILER: + OutputDebugString(_T("- Trailer block\n")); + break; + case BLOCK_APPEXT: + OutputDebugString(_T("- Application extension block\n")); + break; + case BLOCK_COMMEXT: + OutputDebugString(_T("- Comment extension block\n")); + break; + case BLOCK_CONTROLEXT: + { + TGIFControlExt* pControl = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + OutputDebugString(_T("- Graphic control extension block (delay %d, disposal %d)\n"), + pControl->m_wDelayTime, + pControl->GetPackedValue(GCX_PACKED_DISPOSAL)); + }; + break; + case BLOCK_PLAINTEXT: + OutputDebugString(_T("- Plain text extension block\n")); + break; + case BLOCK_IMAGE: + TGIFImageDescriptor* pIDescr = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + OutputDebugString(_T("- Image data block (%dx%d %d,%d)\n"), + pIDescr->m_wWidth, + pIDescr->m_wHeight, + pIDescr->m_wLeftPos, + pIDescr->m_wTopPos); + break; + }; + SkipNextBlock(); + }; + OutputDebugString(_T("\n")); + } +#endif // GIF_TRACING + + DWORD CAnimation::ThreadAnimation() + { + while(!m_bExitThread) + { + if (m_pCallback) m_pCallback->OnFrame(); + // wait until currframe is drawn. if currframe is not drawn over 1min + // this ani may be no use in the future. so exit this thread to stop + // the animation playing by herself. + if (::WaitForSingleObject(m_hDrawEvent, 60*1000) != WAIT_OBJECT_0) + { + m_bExitThread = true; + continue; + } + + // if the delay time is too short (like in old GIFs), wait for 100ms + if((*m_pvFrames)[m_nCurrFrame].m_nDelay < 5) + Sleep(100); + else + Sleep(10 * (*m_pvFrames)[m_nCurrFrame].m_nDelay); + + ::InterlockedIncrement(&m_nCurrFrame); + if(m_nCurrFrame == m_pvFrames->size()) + ::InterlockedExchange(&m_nCurrFrame, (LONG)0); + } + + return 0; + } + + DWORD __stdcall CAnimation::_ThreadAnimation(LPVOID pParam) + { + assert(pParam); + CAnimation* pAni = static_cast(pParam); + if (!pAni) + return 1; + + return pAni->ThreadAnimation(); + } + + //************************************************************************/ + //* CEUIAniHelper */ + //************************************************************************/ + + + bool CEUIAniHelper::DrawAniImage(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, const CDuiString& sImageName, \ + const CDuiString& sImageResType, RECT rcItem, RECT rcBmpPart, RECT rcCorner, DWORD dwMask, BYTE bFade, \ + bool bHole, bool bTiledX, bool bTiledY) + { + const TImageInfo* data = NULL; + if ( !(data=GetAniImage((LPCTSTR)sImageName)) && !(data=pManager->GetImage((LPCTSTR)sImageName)) ) + { + // new Image + // try GIF first + data = AddAniImage(pManager->GetPaintDC(), (LPCTSTR)sImageName, (LPCTSTR)sImageResType, dwMask); + if (!data) + { + // may be not GIF + if( sImageResType.IsEmpty() ) { + data = pManager->GetImageEx((LPCTSTR)sImageName, NULL, dwMask); + } + else { + data = pManager->GetImageEx((LPCTSTR)sImageName, (LPCTSTR)sImageResType, dwMask); + } + } + } + + if (!data) + return false; + + if( rcBmpPart.left == 0 && rcBmpPart.right == 0 && rcBmpPart.top == 0 && rcBmpPart.bottom == 0 ) { + rcBmpPart.right = data->nX; + rcBmpPart.bottom = data->nY; + } + if (rcBmpPart.right > data->nX) rcBmpPart.right = data->nX; + if (rcBmpPart.bottom > data->nY) rcBmpPart.bottom = data->nY; + + RECT rcTemp; + if( !::IntersectRect(&rcTemp, &rcItem, &rc) ) return true; + if( !::IntersectRect(&rcTemp, &rcItem, &rcPaint) ) return true; + + CRenderEngine::DrawImage(hDC, data->hBitmap, rcItem, rcPaint, rcBmpPart, rcCorner, data->alphaChannel, bFade, bHole, bTiledX, bTiledY); + + return true; + } + + bool CEUIAniHelper::DrawImageEx(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, + LPCTSTR pStrImage, LPCTSTR pStrModify) + { + if ((pManager == NULL) || (hDC == NULL)) return false; + + // 1aaa.jpg + // 2file='aaa.jpg' res='' restype='0' dest='0,0,0,0' source='0,0,0,0' corner='0,0,0,0' + // mask='#FF0000' fade='255' hole='false' xtiled='false' ytiled='false' + + CDuiString sImageName = pStrImage; + CDuiString sImageResType; + RECT rcItem = rc; + RECT rcBmpPart = {0}; + RECT rcCorner = {0}; + DWORD dwMask = 0; + BYTE bFade = 0xFF; + bool bHole = false; + bool bTiledX = false; + bool bTiledY = false; + + int image_count = 0; + + CDuiString sItem; + CDuiString sValue; + LPTSTR pstr = NULL; + + for( int i = 0; i < 2; ++i,image_count = 0 ) { + if( i == 1) + pStrImage = pStrModify; + + if( !pStrImage ) continue; + + while( *pStrImage != _T('\0') ) { + sItem.Empty(); + sValue.Empty(); + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + while( *pStrImage != _T('\0') && *pStrImage != _T('=') && *pStrImage > _T(' ') ) { + LPTSTR pstrTemp = ::CharNext(pStrImage); + while( pStrImage < pstrTemp) { + sItem += *pStrImage++; + } + } + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + if( *pStrImage++ != _T('=') ) break; + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + if( *pStrImage++ != _T('\'') ) break; + while( *pStrImage != _T('\0') && *pStrImage != _T('\'') ) { + LPTSTR pstrTemp = ::CharNext(pStrImage); + while( pStrImage < pstrTemp) { + sValue += *pStrImage++; + } + } + if( *pStrImage++ != _T('\'') ) break; + if( !sValue.IsEmpty() ) { + if( sItem == _T("file") || sItem == _T("res") ) { + if( image_count > 0 ) + DrawAniImage(hDC, pManager, rc, rcPaint, sImageName, sImageResType, + rcItem, rcBmpPart, rcCorner, dwMask, bFade, bHole, bTiledX, bTiledY); + + sImageName = sValue; + if( sItem == _T("file") ) + ++image_count; + } + else if( sItem == _T("restype") ) { + if( image_count > 0 ) + DrawAniImage(hDC, pManager, rc, rcPaint, sImageName, sImageResType, + rcItem, rcBmpPart, rcCorner, dwMask, bFade, bHole, bTiledX, bTiledY); + + sImageResType = sValue; + ++image_count; + } + else if( sItem == _T("dest") ) { + rcItem.left = rc.left + _tcstol(sValue.GetData(), &pstr, 10); ASSERT(pstr); + rcItem.top = rc.top + _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcItem.right = rc.left + _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + if (rcItem.right > rc.right) rcItem.right = rc.right; + rcItem.bottom = rc.top + _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + if (rcItem.bottom > rc.bottom) rcItem.bottom = rc.bottom; + } + else if( sItem == _T("source") ) { + rcBmpPart.left = _tcstol(sValue.GetData(), &pstr, 10); ASSERT(pstr); + rcBmpPart.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcBmpPart.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcBmpPart.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + } + else if( sItem == _T("corner") ) { + rcCorner.left = _tcstol(sValue.GetData(), &pstr, 10); ASSERT(pstr); + rcCorner.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcCorner.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcCorner.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + } + else if( sItem == _T("mask") ) { + if( sValue[0] == _T('#')) dwMask = _tcstoul(sValue.GetData() + 1, &pstr, 16); + else dwMask = _tcstoul(sValue.GetData(), &pstr, 16); + } + else if( sItem == _T("fade") ) { + bFade = (BYTE)_tcstoul(sValue.GetData(), &pstr, 10); + } + else if( sItem == _T("hole") ) { + bHole = (_tcscmp(sValue.GetData(), _T("true")) == 0); + } + else if( sItem == _T("xtiled") ) { + bTiledX = (_tcscmp(sValue.GetData(), _T("true")) == 0); + } + else if( sItem == _T("ytiled") ) { + bTiledY = (_tcscmp(sValue.GetData(), _T("true")) == 0); + } + } + if( *pStrImage++ != _T(' ') ) break; + } + } + + DrawAniImage(hDC, pManager, rc, rcPaint, sImageName, sImageResType, + rcItem, rcBmpPart, rcCorner, dwMask, bFade, bHole, bTiledX, bTiledY); + + return true; + } + + const TImageInfo* CEUIAniHelper::GetAniImage(LPCTSTR bitmap) + { + CAnimation* data = static_cast(m_mAniHash.Find(bitmap)); + if (data) + return data->GetCurImage(); + else + return NULL; + } + + const TImageInfo* CEUIAniHelper::AddAniImage(HDC hDC, LPCTSTR bitmap, LPCTSTR type/* = NULL*/, DWORD mask/* = 0*/) + { + CAnimation* pAniImage = new CAnimation(this); + if (!pAniImage->LoadGIF(bitmap, type, mask)) + { + delete pAniImage; + return NULL; + } + if ( !m_mAniHash.Insert(bitmap, pAniImage) ) + { + delete pAniImage; + return NULL; + } + + pAniImage->Play(); + return pAniImage->GetCurImage(); + } +} diff --git a/Demos/bdwallpaper/controlex/AnimationHelper.h b/Demos/bdwallpaper/controlex/AnimationHelper.h new file mode 100644 index 00000000..a1bd8aa8 --- /dev/null +++ b/Demos/bdwallpaper/controlex/AnimationHelper.h @@ -0,0 +1,229 @@ +#ifndef _EUI_ANIMATION_HELPER__H__ +#define _EUI_ANIMATION_HELPER__H__ + +#include + +namespace DuiLib +{ + class IAnimationCallback + { + public: + virtual void OnFrame() = 0; + }; + + class CAnimation + { +#pragma pack(1) // turn byte alignment on + enum GIFBlockTypes + { + BLOCK_UNKNOWN, + BLOCK_APPEXT, + BLOCK_COMMEXT, + BLOCK_CONTROLEXT, + BLOCK_PLAINTEXT, + BLOCK_IMAGE, + BLOCK_TRAILER + }; + + enum ControlExtValues // graphic control extension packed field values + { + GCX_PACKED_DISPOSAL, // disposal method + GCX_PACKED_USERINPUT, + GCX_PACKED_TRANSPCOLOR + }; + + enum LSDPackedValues // logical screen descriptor packed field values + { + LSD_PACKED_GLOBALCT, + LSD_PACKED_CRESOLUTION, + LSD_PACKED_SORT, + LSD_PACKED_GLOBALCTSIZE + }; + + enum IDPackedValues // image descriptor packed field values + { + ID_PACKED_LOCALCT, + ID_PACKED_INTERLACE, + ID_PACKED_SORT, + ID_PACKED_LOCALCTSIZE + }; + + struct TGIFHeader // GIF header + { + char m_cSignature[3]; // Signature - Identifies the GIF Data Stream + // This field contains the fixed value 'GIF' + char m_cVersion[3]; // Version number. May be one of the following: + // "87a" or "89a" + }; + + struct TGIFLSDescriptor // Logical Screen Descriptor + { + WORD m_wWidth; // 2 bytes. Logical screen width + WORD m_wHeight; // 2 bytes. Logical screen height + unsigned char m_cPacked; // packed field + unsigned char m_cBkIndex; // 1 byte. Background color index + unsigned char m_cPixelAspect; // 1 byte. Pixel aspect ratio + inline int GetPackedValue(enum LSDPackedValues Value); + }; + + struct TGIFAppExtension // application extension block + { + unsigned char m_cExtIntroducer; // extension introducer (0x21) + unsigned char m_cExtLabel; // app. extension label (0xFF) + unsigned char m_cBlockSize; // fixed value of 11 + char m_cAppIdentifier[8]; // application identifier + char m_cAppAuth[3]; // application authentication code + }; + + struct TGIFControlExt // graphic control extension block + { + unsigned char m_cExtIntroducer; // extension introducer (0x21) + unsigned char m_cControlLabel; // control extension label (0xF9) + unsigned char m_cBlockSize; // fixed value of 4 + unsigned char m_cPacked; // packed field + WORD m_wDelayTime; // delay time + unsigned char m_cTColorIndex; // transparent color index + unsigned char m_cBlockTerm; // block terminator (0x00) + inline int GetPackedValue(enum ControlExtValues Value); + }; + + struct TGIFCommentExt // comment extension block + { + unsigned char m_cExtIntroducer; // extension introducer (0x21) + unsigned char m_cCommentLabel; // comment extension label (0xFE) + }; + + struct TGIFPlainTextExt // plain text extension block + { + unsigned char m_cExtIntroducer; // extension introducer (0x21) + unsigned char m_cPlainTextLabel; // text extension label (0x01) + unsigned char m_cBlockSize; // fixed value of 12 + WORD m_wLeftPos; // text grid left position + WORD m_wTopPos; // text grid top position + WORD m_wGridWidth; // text grid width + WORD m_wGridHeight; // text grid height + unsigned char m_cCellWidth; // character cell width + unsigned char m_cCellHeight; // character cell height + unsigned char m_cFgColor; // text foreground color index + unsigned char m_cBkColor; // text background color index + }; + + struct TGIFImageDescriptor // image descriptor block + { + unsigned char m_cImageSeparator; // image separator byte (0x2C) + WORD m_wLeftPos; // image left position + WORD m_wTopPos; // image top position + WORD m_wWidth; // image width + WORD m_wHeight; // image height + unsigned char m_cPacked; // packed field + inline int GetPackedValue(enum IDPackedValues Value); + }; +#pragma pack() // turn byte alignment off + struct TFrame // structure that keeps a single frame info + { + TImageInfo* m_pImage; // pointer to one frame image + SIZE m_frameSize; + SIZE m_frameOffset; + UINT m_nDelay; // delay (in 1/100s of a second) + UINT m_nDisposal; // disposal method + }; + typedef std::vector VTFRAME; + + public: + CAnimation(IAnimationCallback* pCallback); + ~CAnimation(); + + const TImageInfo* GetCurImage(); + // loads a picture from a file or a program resource + const TImageInfo* LoadGIF(LPCTSTR bitmap, LPCTSTR type = NULL, DWORD mask = 0); + // loads a picture from a memory block (allocated by malloc) + // Warning: this function DOES NOT free the memory, pointed to by pData + const TImageInfo* LoadGIF(const LPBYTE pData, DWORD dwSize, DWORD mask = 0); + SIZE GetSize() const; + int GetFrameCount() const; + COLORREF GetBkColor() const; + + bool Play(); // play the animation (just change m_nCurrFrame) + void Stop(); // stops animation + void UnLoad(); // stops animation plus releases all resources + bool IsPlaying() const; + + protected: + int GetNextBlockLen() const; + BOOL SkipNextBlock(); + BOOL SkipNextGraphicBlock(); + void ResetDataPointer(); + enum GIFBlockTypes GetNextBlock() const; + UINT GetSubBlocksLen(UINT nStartingOffset) const; + // create a single-frame GIF from one of frames, and put in a memeory block (allocated by malloc) + // Warning: we should free the memeory block, pointed to by return value + LPBYTE GetNextGraphicBlock(UINT* pBlockLen, UINT* pDelay, SIZE* pBlockSize, SIZE* pBlockOffset, UINT* pDisposal); +#ifdef GIF_TRACING + void EnumGIFBlocks(); + void WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize); +#endif // GIF_TRACING + + private: + unsigned char* m_pRawData; // ʱݴ涯ļ Ч + UINT m_nDataSize; // GIFļС + TGIFHeader* m_pGIFHeader; // GIFļͷ + TGIFLSDescriptor* m_pGIFLSDescriptor; // ߼Ļʶ + UINT m_nGlobalCTSize; // ȫɫбС + SIZE m_PictureSize; // ͼߴ + COLORREF m_clrBackground; // ɫ + volatile long m_nCurrFrame; // ǰֵ֡ + UINT m_nCurrOffset; // ȡƫ + VTFRAME* m_pvFrames; // ֡ + + HANDLE m_hThread; + HANDLE m_hDrawEvent; // ͼ¼ ǰ֡Ѿȡڻ ֹȾٶ֡ + volatile bool m_bExitThread; + IAnimationCallback* m_pCallback; + + DWORD ThreadAnimation(); + static DWORD __stdcall _ThreadAnimation(LPVOID pParam); + }; + + class CEUIAniHelper : public IAnimationCallback + { + public: + ~CEUIAniHelper() + { + CAnimation* data = NULL; + while (m_mAniHash.GetSize() > 0) + { + if(LPCTSTR key = m_mAniHash.GetAt(0)) { + data = static_cast(m_mAniHash.Find(key)); + if (data) + { + delete data; + data = NULL; + } + m_mAniHash.Remove(key); + } + } + } + /** + * ͼǿ ԭCControlUI::DrawImage CRenderEngine::DrawImageString + * Note: PaintXXImage + */ + bool DrawImageEx(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, + LPCTSTR pStrImage, LPCTSTR pStrModify = NULL); + /** + * ͼǿ ԭDuiLib::DrawImage + * Note: жļ͡ʼȡGIFݵ + */ + bool DrawAniImage(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, const CDuiString& sImageName, \ + const CDuiString& sImageResType, RECT rcItem, RECT rcBmpPart, RECT rcCorner, DWORD dwMask, BYTE bFade, \ + bool bHole, bool bTiledX, bool bTiledY); + + protected: + const TImageInfo* GetAniImage(LPCTSTR bitmap); + const TImageInfo* AddAniImage(HDC hDC, LPCTSTR bitmap, LPCTSTR type = NULL, DWORD mask = 0); + + protected: + CStdStringPtrMap m_mAniHash; + }; +} + +#endif diff --git a/Demos/bdwallpaper/controlex/ControlInclude.h b/Demos/bdwallpaper/controlex/ControlInclude.h new file mode 100644 index 00000000..0c84b69d --- /dev/null +++ b/Demos/bdwallpaper/controlex/ControlInclude.h @@ -0,0 +1,32 @@ +#ifndef __CONTROL_INCLUDE_H__ +#define __CONTROL_INCLUDE_H__ + +#include +#include +#include "UILableEx.h" +#include "UIButtonEx.h" +#include "UIWPPanel.h" + +static CControlUI* CreateControl(LPCTSTR pstrClass) +{ + if( _tcsicmp(pstrClass, _T("LabelEx")) == 0 ) return new CLabelEx; + else if( _tcsicmp(pstrClass, _T("LabelMutiline")) == 0 ) return new CLabelMutiline; + else if( _tcsicmp(pstrClass, _T("WPButton")) == 0 ) return new CWPButton; + else if( _tcsicmp(pstrClass, _T("WPPanel")) == 0 ) return new CWPPanel; + return NULL; +} + +class CDialogBuilderCallbackEx : public IDialogBuilderCallback +{ +public: + CControlUI* CreateControl(LPCTSTR pstrClass) + { + if( _tcsicmp(pstrClass, _T("LabelEx")) == 0 ) return new CLabelEx; + else if( _tcsicmp(pstrClass, _T("LabelMutiline")) == 0 ) return new CLabelMutiline; + else if( _tcsicmp(pstrClass, _T("WPButton")) == 0 ) return new CWPButton; + else if( _tcsicmp(pstrClass, _T("WPPanel")) == 0 ) return new CWPPanel; + return NULL; + } +}; + +#endif __CONTROL_INCLUDE_H__ diff --git a/Demos/bdwallpaper/controlex/UIButtonEx.h b/Demos/bdwallpaper/controlex/UIButtonEx.h new file mode 100644 index 00000000..c5bb1823 --- /dev/null +++ b/Demos/bdwallpaper/controlex/UIButtonEx.h @@ -0,0 +1,283 @@ +#ifndef __BUTTONEX_H__ +#define __BUTTONEX_H__ +#include +namespace DuiLib +{ + #define DUI_MSGTYPE_CLICK_WPFAV (_T("click_wpfav")) + #define DUI_MSGTYPE_CLICK_WPSET (_T("click_wpset")) + + class CWPButton : public CButtonUI + { + public: + CWPButton() + { + } + ~CWPButton(void) + { + } + + public: + virtual void PaintStatusImage(HDC hDC) + { + // ؼλ + RECT rcItem = m_rcItem; + // ƱֽͼƬ + CRenderEngine::DrawImageString(hDC, m_pManager, rcItem, m_rcPaint, PrepareImage(), NULL); + // ״̬ + CButtonUI::PaintStatusImage(hDC); + if(m_uButtonState & UISTATE_HOT) + { + // λ + RECT rcToolBar = rcItem; + RECT rcFav = rcItem; + RECT rcSet = rcItem; + rcToolBar.top = rcToolBar.bottom - 25; + rcFav.top = rcFav.bottom - 25; + rcFav.right = rcFav.left + 94; + rcSet.top = rcSet.bottom - 25; + rcSet.left = rcSet.right - 94; + // + CRenderEngine::DrawImageString(hDC, m_pManager, rcToolBar, m_rcPaint, m_sWPToolbarImage, NULL); + CRenderEngine::DrawImageString(hDC, m_pManager, rcFav, m_rcPaint, m_sWPFavImage, NULL); + CRenderEngine::DrawImageString(hDC, m_pManager, rcSet, m_rcPaint, m_sWPSetImage, NULL); + } + } + + void DoEvent(TEventUI& event) + { + if( event.Type == UIEVENT_BUTTONUP ) + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { + // λ + RECT rcFav = m_rcItem; + RECT rcSet = m_rcItem; + rcFav.top = rcFav.bottom - 25; + rcFav.right = rcFav.left + 94; + rcSet.top = rcSet.bottom - 25; + rcSet.left = rcSet.right - 94; + if( ::PtInRect(&rcFav, event.ptMouse) ) { + if( m_pManager != NULL ) m_pManager->SendNotify(this, DUI_MSGTYPE_CLICK_WPFAV); + } + else if( ::PtInRect(&rcSet, event.ptMouse) ) { + if( m_pManager != NULL ) m_pManager->SendNotify(this, DUI_MSGTYPE_CLICK_WPSET); + } + else if( ::PtInRect(&m_rcItem, event.ptMouse) ) Activate(); + m_uButtonState &= ~(UISTATE_PUSHED | UISTATE_CAPTURED); + Invalidate(); + } + return; + } + + return CButtonUI::DoEvent(event); + } + + CString PrepareImage() + { + CString sAppPath = CPaintManagerUI::GetInstancePath().GetData(); + CString sImageName = CrackUrl(m_sWPImage.GetData()); + sImageName.Replace(_T("/"), _T("\\")); + TCHAR szFile[MAX_PATH] = {0}; + lstrcpy(szFile, sAppPath); + ::PathAppend(szFile, sImageName); + if(!::PathFileExists(szFile)) + { + CreateDirectory(szFile, NULL); + ::URLDownloadToFile(NULL, m_sWPImage, szFile, 0, NULL); + } + return szFile; + + } + + CString PrepareWPImage() + { + int cx = GetSystemMetrics(SM_CXSCREEN); + int cy = GetSystemMetrics(SM_CYSCREEN); + CString sAppPath = CPaintManagerUI::GetInstancePath().GetData(); + CString sUrl; + sUrl.Format(_T("http://bizhi.baidu.com/wallpaper/getWallpaperById?t=%d&name=baiduwp&v=2.0.0.1160&g=C_0-D_100825PBN40317ERB89E-M_206A8A1263C4-V_DE5571AC-T_20140714225619485&x=%d&y=%d&tn=bdwp&dtn=bdwp&uinf=8704-0-8&id=%s&resolution=%d_%d"), GetTickCount(), cx, cy, GetUserData().GetData(), cx, cy); + CString sRealUrl = RealWebFile(sUrl); + CString sImageName = CrackUrl(sRealUrl); + sImageName.Replace(_T("/"), _T("\\")); + TCHAR szFile[MAX_PATH] = {0}; + lstrcpy(szFile, sAppPath); + ::PathAppend(szFile, sImageName); + if(!::PathFileExists(szFile)) + { + CreateDirectory(szFile, NULL); + ::URLDownloadToFile(NULL, sRealUrl, szFile, 0, NULL); + } + return szFile; + + } + + BOOL CreateDirectory(LPCTSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes) + { + TCHAR cPath[MAX_PATH] = {0}; + TCHAR cTmpPath[MAX_PATH] = {0}; + TCHAR * lpPos = NULL; + TCHAR cTmp = _T('\0'); + + if (NULL == lpPathName || NULL == *lpPathName) + return FALSE; + + _tcsncpy(cPath, lpPathName, MAX_PATH); + + for (int i = 0; i < (int)_tcslen(cPath); i++) + { + if (_T('\\') == cPath[i]) + cPath[i] = _T('/'); + } + + lpPos = _tcschr(cPath, _T('/')); + while (lpPos != NULL) + { + if (lpPos == cPath) + { + lpPos++; + } + else + { + cTmp = *lpPos; + *lpPos = _T('\0'); + _tcsncpy(cTmpPath, cPath, MAX_PATH); + ::CreateDirectory(cTmpPath, lpSecurityAttributes); + *lpPos = cTmp; + lpPos++; + } + lpPos = _tcschr(lpPos, _T('/')); + } + + return TRUE; + } + + CString QueryInfo(HINTERNET hRequest, DWORD dwInfoLevel) + { + CString sInfo; + DWORD dwLen = 0; + if (!HttpQueryInfo (hRequest, dwInfoLevel, NULL, &dwLen, 0) && dwLen) + { + void * buf = malloc(dwLen + 2); + ZeroMemory (buf, dwLen + 2); + ::HttpQueryInfo (hRequest, dwInfoLevel, buf, &dwLen, 0); + sInfo = (LPCTSTR)buf; + free(buf); + } + return sInfo; + } + + CString RealWebFile(CString sUrl) + { + CString sRealUrl = sUrl; + // http + HINTERNET hConnect = InternetOpen(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); + + if (hConnect) + { + DWORD dwTimeOut = 0; + InternetSetOption(hConnect, INTERNET_OPTION_CONNECT_TIMEOUT, &dwTimeOut, sizeof(dwTimeOut)); + + HINTERNET hSession = InternetOpenUrl(hConnect, sUrl, NULL, 0, INTERNET_FLAG_NO_AUTO_REDIRECT | INTERNET_FLAG_RELOAD, 0); + if (hSession) + { + DWORD dwStatus = 0; + DWORD dwBuffLen = sizeof(dwStatus); + BOOL bSuccess = HttpQueryInfo(hSession, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, &dwStatus, &dwBuffLen, 0); + if(bSuccess) { + sRealUrl = QueryInfo(hSession, HTTP_QUERY_LOCATION); + } + + InternetCloseHandle(hSession); + } + InternetCloseHandle(hConnect); + } + + return sRealUrl; + } + + CString CrackUrl(CString sUrl) + { + URL_COMPONENTS uc; + TCHAR Scheme[1000]; + TCHAR HostName[1000]; + TCHAR UserName[1000]; + TCHAR Password[1000]; + TCHAR UrlPath[1000]; + TCHAR ExtraInfo[1000]; + + uc.dwStructSize = sizeof(uc); + uc.lpszScheme = Scheme; + uc.lpszHostName = HostName; + uc.lpszUserName = UserName; + uc.lpszPassword = Password; + uc.lpszUrlPath = UrlPath; + uc.lpszExtraInfo = ExtraInfo; + + uc.dwSchemeLength = 1000; + uc.dwHostNameLength = 1000; + uc.dwUserNameLength = 1000; + uc.dwPasswordLength = 1000; + uc.dwUrlPathLength = 1000; + uc.dwExtraInfoLength = 1000; + + InternetCrackUrl(sUrl, 0, 0, &uc); + return UrlPath; + } + + public: + void SetWP() + { + CString sWPImage = PrepareWPImage(); + HRESULT hr = S_OK; + IActiveDesktop *pIAD = NULL; + hr = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER, IID_IActiveDesktop, (void**)&pIAD); + if( !SUCCEEDED(hr) ) return; + + hr = pIAD->SetWallpaper(sWPImage, 0); + if( !SUCCEEDED(hr) ) return; + + WALLPAPEROPT wp = {0}; + wp.dwSize = sizeof(WALLPAPEROPT); + wp.dwStyle |= 2; + hr = pIAD->SetWallpaperOptions(&wp, 0); + if( !SUCCEEDED(hr) ) return; + + hr = pIAD->ApplyChanges(AD_APPLY_ALL); + if( !SUCCEEDED(hr) ) return; + + pIAD->Release(); + } + + void SetWPImage(LPCTSTR pstrImage) + { + m_sWPImage = pstrImage; + Invalidate(); + } + void SetWPToolbarImage(LPCTSTR pstrImage) + { + m_sWPToolbarImage = pstrImage; + } + void SetWPFavImage(LPCTSTR pstrImage) + { + m_sWPFavImage = pstrImage; + } + void SetWPSetImage(LPCTSTR pstrImage) + { + m_sWPSetImage = pstrImage; + } + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if( _tcsicmp(pstrName, _T("wpimage")) == 0 ) SetWPImage(pstrValue); + else if( _tcsicmp(pstrName, _T("wptoolbarimage")) == 0 ) SetWPToolbarImage(pstrValue); + else if( _tcsicmp(pstrName, _T("wpfavimage")) == 0 ) SetWPFavImage(pstrValue); + else if( _tcsicmp(pstrName, _T("wpsetimage")) == 0 ) SetWPSetImage(pstrValue); + else CButtonUI::SetAttribute(pstrName, pstrValue); + } + private: + CDuiString m_sWPImage; + CDuiString m_sWPToolbarImage; + CDuiString m_sWPFavImage; + CDuiString m_sWPSetImage; + }; +} + +#endif __BUTTONEX_H__ diff --git a/Demos/bdwallpaper/controlex/UILableEx.h b/Demos/bdwallpaper/controlex/UILableEx.h new file mode 100644 index 00000000..1f2ab3d0 --- /dev/null +++ b/Demos/bdwallpaper/controlex/UILableEx.h @@ -0,0 +1,130 @@ +#ifndef __LABELEX_H__ +#define __LABELEX_H__ +#include "AnimationHelper.h" + +namespace DuiLib +{ + class CLabelEx : public CLabelUI, public CEUIAniHelper + { + public: + void CLabelEx::PaintBkImage(HDC hDC) + { + if( m_sBkImage.IsEmpty() ) + return; + if( !DrawImageEx(hDC, m_pManager, m_rcItem, m_rcPaint, (LPCTSTR)m_sBkImage) ) + m_sBkImage.Empty(); + } + + void CLabelEx::OnFrame() + { + Invalidate(); + } + + void PaintText(HDC hDC) + { + if( m_dwTextColor == 0 ) m_dwTextColor = m_pManager->GetDefaultFontColor(); + if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor(); + + if( m_sText.IsEmpty() ) return; + int nLinks = 0; + RECT rc = m_rcItem; + rc.left += m_rcTextPadding.left; + rc.right -= m_rcTextPadding.right; + rc.top += m_rcTextPadding.top; + rc.bottom -= m_rcTextPadding.bottom; + if( IsEnabled() ) { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + NULL, NULL, nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + m_iFont, m_uTextStyle); + } + else { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + NULL, NULL, nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + m_iFont, m_uTextStyle); + } + } + }; + + class CLabelIcon : public CLabelUI + { + public: + CLabelIcon() + { + m_hIcon = NULL; + } + ~CLabelIcon(void) + { + if(m_hIcon != NULL) + ::DestroyIcon(m_hIcon); + } + + public: + virtual void PaintStatusImage(HDC hDC) + { + CLabelUI::PaintStatusImage(hDC); + if(m_hIcon != NULL) + { + ICONINFO ii; + if(::GetIconInfo(m_hIcon, &ii)) + { + BITMAP bmp; + if(::GetObject(ii.hbmColor, sizeof(bmp), &bmp)) + ::DrawIcon(hDC, m_rcItem.left+(m_rcItem.right-m_rcItem.left-bmp.bmWidth)/2, m_rcItem.top+4, m_hIcon); + ::DeleteObject(ii.hbmColor); + ::DeleteObject(ii.hbmMask); + } + } + } + + public: + void SetIcon(HICON hIcon) + { + m_hIcon = hIcon; + } + + private: + HICON m_hIcon; + }; + + class CLabelMutiline: public CLabelUI + { + public: + void PaintText(HDC hDC) + { + if( m_dwTextColor == 0 ) m_dwTextColor = m_pManager->GetDefaultFontColor(); + if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor(); + + if( m_sText.IsEmpty() ) return; + int nLinks = 0; + RECT rc = m_rcItem; + rc.left += m_rcTextPadding.left; + rc.right -= m_rcTextPadding.right; + rc.top += m_rcTextPadding.top; + rc.bottom -= m_rcTextPadding.bottom; + if( IsEnabled() ) { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + NULL, NULL, nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + m_iFont, m_uTextStyle); + } + else { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + NULL, NULL, nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + m_iFont, m_uTextStyle); + } + } + }; +} + +#endif __LABELEX_H__ diff --git a/Demos/bdwallpaper/controlex/UIWPPanel.h b/Demos/bdwallpaper/controlex/UIWPPanel.h new file mode 100644 index 00000000..db948e5b --- /dev/null +++ b/Demos/bdwallpaper/controlex/UIWPPanel.h @@ -0,0 +1,81 @@ +#ifndef __WPPANEL_H__ +#define __WPPANEL_H__ +#include +#include + +namespace DuiLib +{ + class CWPPanel : public CContainerUI + { + public: + CWPPanel() + { + // ּ + int aaLayouts[4][4] = {{4,1,1,4},{1,4,4,1},{4,4,1,1},{1,1,4,4}}; + srand(time(NULL)); + int nLayout = rand() % 4; + for(int i = 0; i < 4; i++) + { + m_aLayouts[i] = aaLayouts[nLayout][i]; + } + } + ~CWPPanel(void) + { + } + + public: + void SetPos(RECT rc) + { + CControlUI::SetPos(rc); + RECT rcItem = m_rcItem; + + rcItem.left += m_rcInset.left; + rcItem.top += m_rcInset.top; + rcItem.right -= m_rcInset.right; + rcItem.bottom -= m_rcInset.bottom; + + int nWidth = rcItem.right - rcItem.left; + int nHeight = rcItem.bottom - rcItem.top; + int nBlockWidth = nWidth / 2; + int nBlockHeight = nHeight / 2; + int nSBlockWidth = nBlockWidth / 2; + int nSBlockHeight = nBlockHeight / 2; + + int it = 0; + for (int it1 = 0; it1 < 4; ++it1) { + int nX = it1 % 2 * nBlockWidth; + int nY = it1 / 2 * nBlockHeight; + RECT rcBlock = {rcItem.left + nX, rcItem.top + nY, rcItem.left + nX + nBlockWidth, rcItem.top + nY + nBlockHeight}; + + int nBlocks = m_aLayouts[it1]; + if(nBlocks == 1) { + CControlUI* pBlock = (CControlUI*)m_items.GetAt(it); + if(pBlock != NULL) { + pBlock->SetPos(rcBlock); + } + } + else if(nBlocks == 4) { + for(int it2 = 0; it2 < nBlocks; ++it2) { + int nSX = it2 % 2 * nSBlockWidth; + int nSY = it2 / 2 * nSBlockHeight; + RECT rcSBlock = {rcBlock.left + nSX, rcBlock.top + nSY, rcBlock.left + nSX + nSBlockWidth, rcBlock.top + nSY + nSBlockHeight}; + CControlUI* pBlock = (CControlUI*)m_items.GetAt(it + it2); + if(pBlock != NULL) { + pBlock->SetPos(rcSBlock); + } + } + } + it += nBlocks; + } + } + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + CContainerUI::SetAttribute(pstrName, pstrValue); + } + + private: + int m_aLayouts[4]; + }; +} + +#endif __WPPANEL_H__ diff --git a/Demos/bdwallpaper/helper/Utils.h b/Demos/bdwallpaper/helper/Utils.h new file mode 100644 index 00000000..ea886ab7 --- /dev/null +++ b/Demos/bdwallpaper/helper/Utils.h @@ -0,0 +1,17 @@ +#pragma once + +static BOOL DoEvents() +{ + MSG msg = {0}; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + if (msg.message == WM_QUIT) + { + PostQuitMessage(msg.wParam); + return FALSE; + } + TranslateMessage(&msg); + DispatchMessage( & msg); + } + return TRUE; +} diff --git a/Demos/bdwallpaper/helper/XUnzip.cpp b/Demos/bdwallpaper/helper/XUnzip.cpp new file mode 100644 index 00000000..2f7b40a8 --- /dev/null +++ b/Demos/bdwallpaper/helper/XUnzip.cpp @@ -0,0 +1,4445 @@ +// XUnzip.cpp Version 1.3 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich@gmail.com +// +// Version 1.3: - Corrected size bug introduced by 1.2 +// +// Version 1.2: - Many bug fixes. See CodeProject article for list. +// +// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd() +// - Changed file names to avoid conflicts with Lucian's files +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by Info-ZIP. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + + +//#define _USE_32BIT_TIME_T //+++1.2 + + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include + +#pragma warning(disable : 4996) // disable bogus deprecation warning + +// THIS FILE is almost entirely based upon code by Jean-loup Gailly +// and Mark Adler. It has been modified by Lucian Wischik. +// The original code may be found at http://www.gzip.org/zlib/ +// The original copyright text follows. +// +// +// +// zlib.h -- interface of the 'zlib' general purpose compression library +// version 1.1.3, July 9th, 1998 +// +// Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Jean-loup Gailly Mark Adler +// jloup@gzip.org madler@alumni.caltech.edu +// +// +// The data format used by the zlib library is described by RFCs (Request for +// Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt +// (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +// +// +// The 'zlib' compression library provides in-memory compression and +// decompression functions, including integrity checks of the uncompressed +// data. This version of the library supports only one compression method +// (deflation) but other algorithms will be added later and will have the same +// stream interface. +// +// Compression can be done in a single step if the buffers are large +// enough (for example if an input file is mmap'ed), or can be done by +// repeated calls of the compression function. In the latter case, the +// application must provide more input and/or consume the output +// (providing more output space) before each call. +// +// The library also supports reading and writing files in gzip (.gz) format +// with an interface similar to that of stdio. +// +// The library does not install any signal handler. The decoder checks +// the consistency of the compressed data, so the library should never +// crash even in case of corrupted input. +// +// for more info about .ZIP format, see ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip +// PkWare has also a specification at ftp://ftp.pkware.com/probdesc.zip + +#define zmalloc(len) malloc(len) + +#define zfree(p) free(p) + +/* +void *zmalloc(unsigned int len) +{ char *buf = new char[len+32]; + for (int i=0; i<16; i++) + { buf[i]=i; + buf[len+31-i]=i; + } + *((unsigned int*)buf) = len; + char c[1000]; wsprintf(c,"malloc 0x%lx - %lu",buf+16,len); + OutputDebugString(c); + return buf+16; +} + +void zfree(void *buf) +{ char c[1000]; wsprintf(c,"free 0x%lx",buf); + OutputDebugString(c); + char *p = ((char*)buf)-16; + unsigned int len = *((unsigned int*)p); + bool blown=false; + for (int i=0; i<16; i++) + { char lo = p[i]; + char hi = p[len+31-i]; + if (hi!=i || (lo!=i && i>4)) blown=true; + } + if (blown) + { OutputDebugString("BLOWN!!!"); + } + delete[] p; +} +*/ + +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened + +typedef DWORD ZRESULT; +// return codes from any of the zip functions. Listed later. + +#define ZIP_HANDLE 1 +#define ZIP_FILENAME 2 +#define ZIP_MEMORY 3 + +typedef struct +{ int index; // index of this file within the zip +char name[MAX_PATH]; // filename within the zip +DWORD attr; // attributes, as in GetFileAttributes. +FILETIME atime,ctime,mtime;// access, create, modify filetimes +long comp_size; // sizes of item, compressed and uncompressed. These +long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; + +typedef struct +{ int index; // index of this file within the zip +TCHAR name[MAX_PATH]; // filename within the zip +DWORD attr; // attributes, as in GetFileAttributes. +FILETIME atime,ctime,mtime;// access, create, modify filetimes +long comp_size; // sizes of item, compressed and uncompressed. These +long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; + +// These are the result codes: +#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, +#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. +// The following come from general system stuff (e.g. files not openable) +#define ZR_GENMASK 0x0000FF00 +#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle +#define ZR_NOFILE 0x00000200 // couldn't create/open the file +#define ZR_NOALLOC 0x00000300 // failed to allocate some resource +#define ZR_WRITE 0x00000400 // a general error writing to the file +#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip +#define ZR_MORE 0x00000600 // there's still more data to be unzipped +#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile +#define ZR_READ 0x00000800 // a general error reading the file +// The following come from mistakes on the part of the caller +#define ZR_CALLERMASK 0x00FF0000 +#define ZR_ARGS 0x00010000 // general mistake with the arguments +#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't +#define ZR_MEMSIZE 0x00030000 // the memory size is too small +#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function +#define ZR_ENDED 0x00050000 // the zip creation has already been closed +#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken +#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped +#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip +// The following come from bugs within the zip library itself +#define ZR_BUGMASK 0xFF000000 +#define ZR_NOTINITED 0x01000000 // initialisation didn't work +#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file +#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed +#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code + +#pragma warning(disable : 4702) // unreachable code + +static ZRESULT zopenerror = ZR_OK; //+++1.2 + +typedef struct tm_unz_s +{ unsigned int tm_sec; // seconds after the minute - [0,59] + unsigned int tm_min; // minutes after the hour - [0,59] + unsigned int tm_hour; // hours since midnight - [0,23] + unsigned int tm_mday; // day of the month - [1,31] + unsigned int tm_mon; // months since January - [0,11] + unsigned int tm_year; // years - [1980..2044] +} tm_unz; + + +// unz_global_info structure contain global data about the ZIPfile +typedef struct unz_global_info_s +{ unsigned long number_entry; // total number of entries in the central dir on this disk + unsigned long size_comment; // size of the global comment of the zipfile +} unz_global_info; + +// unz_file_info contain information about a file in the zipfile +typedef struct unz_file_info_s +{ unsigned long version; // version made by 2 bytes + unsigned long version_needed; // version needed to extract 2 bytes + unsigned long flag; // general purpose bit flag 2 bytes + unsigned long compression_method; // compression method 2 bytes + unsigned long dosDate; // last mod file date in Dos fmt 4 bytes + unsigned long crc; // crc-32 4 bytes + unsigned long compressed_size; // compressed size 4 bytes + unsigned long uncompressed_size; // uncompressed size 4 bytes + unsigned long size_filename; // filename length 2 bytes + unsigned long size_file_extra; // extra field length 2 bytes + unsigned long size_file_comment; // file comment length 2 bytes + unsigned long disk_num_start; // disk number start 2 bytes + unsigned long internal_fa; // internal file attributes 2 bytes + unsigned long external_fa; // external file attributes 4 bytes + tm_unz tmu_date; +} unz_file_info; + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + + + + + + + +#define ZLIB_VERSION "1.1.3" + + +// Allowed flush values; see deflate() for details +#define Z_NO_FLUSH 0 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 + + +// compression levels +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) + +// compression strategy; see deflateInit2() for details +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +// Possible values of the data_type field +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 + +// The deflate compression method (the only one supported in this version) +#define Z_DEFLATED 8 + +// for initializing zalloc, zfree, opaque +#define Z_NULL 0 + +// case sensitivity when searching for filenames +#define CASE_SENSITIVE 1 +#define CASE_INSENSITIVE 2 + + +// Return codes for the compression/decompression functions. Negative +// values are errors, positive values are used for special but normal events. +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) + + + +// Basic data types +typedef unsigned char Byte; // 8 bits +typedef unsigned int uInt; // 16 bits or more +typedef unsigned long uLong; // 32 bits or more +typedef void *voidpf; +typedef void *voidp; +typedef long z_off_t; + + + + + + + + + + + + +typedef voidpf (*alloc_func) (voidpf opaque, uInt items, uInt size); +typedef void (*free_func) (voidpf opaque, voidpf address); + +struct internal_state; + +typedef struct z_stream_s { + Byte *next_in; // next input byte + uInt avail_in; // number of bytes available at next_in + uLong total_in; // total nb of input bytes read so far + + Byte *next_out; // next output byte should be put there + uInt avail_out; // remaining free space at next_out + uLong total_out; // total nb of bytes output so far + + char *msg; // last error message, NULL if no error + struct internal_state *state; // not visible by applications + + alloc_func zalloc; // used to allocate the internal state + free_func zfree; // used to free the internal state + voidpf opaque; // private data object passed to zalloc and zfree + + int data_type; // best guess about the data type: ascii or binary + uLong adler; // adler32 value of the uncompressed data + uLong reserved; // reserved for future use +} z_stream; + +typedef z_stream *z_streamp; + + +// The application must update next_in and avail_in when avail_in has +// dropped to zero. It must update next_out and avail_out when avail_out +// has dropped to zero. The application must initialize zalloc, zfree and +// opaque before calling the init function. All other fields are set by the +// compression library and must not be updated by the application. +// +// The opaque value provided by the application will be passed as the first +// parameter for calls of zalloc and zfree. This can be useful for custom +// memory management. The compression library attaches no meaning to the +// opaque value. +// +// zalloc must return Z_NULL if there is not enough memory for the object. +// If zlib is used in a multi-threaded application, zalloc and zfree must be +// thread safe. +// +// The fields total_in and total_out can be used for statistics or +// progress reports. After compression, total_in holds the total size of +// the uncompressed data and may be saved for use in the decompressor +// (particularly if the decompressor wants to decompress everything in +// a single step). +// + + +// basic functions + +const char *zlibVersion (); +// The application can compare zlibVersion and ZLIB_VERSION for consistency. +// If the first character differs, the library code actually used is +// not compatible with the zlib.h header file used by the application. +// This check is automatically made by inflateInit. + + + + + + +int inflate (z_streamp strm, int flush); +// +// inflate decompresses as much data as possible, and stops when the input +// buffer becomes empty or the output buffer becomes full. It may some +// introduce some output latency (reading input without producing any output) +// except when forced to flush. +// +// The detailed semantics are as follows. inflate performs one or both of the +// following actions: +// +// - Decompress more input starting at next_in and update next_in and avail_in +// accordingly. If not all input can be processed (because there is not +// enough room in the output buffer), next_in is updated and processing +// will resume at this point for the next call of inflate(). +// +// - Provide more output starting at next_out and update next_out and avail_out +// accordingly. inflate() provides as much output as possible, until there +// is no more input data or no more space in the output buffer (see below +// about the flush parameter). +// +// Before the call of inflate(), the application should ensure that at least +// one of the actions is possible, by providing more input and/or consuming +// more output, and updating the next_* and avail_* values accordingly. +// The application can consume the uncompressed output when it wants, for +// example when the output buffer is full (avail_out == 0), or after each +// call of inflate(). If inflate returns Z_OK and with zero avail_out, it +// must be called again after making room in the output buffer because there +// might be more output pending. +// +// If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much +// output as possible to the output buffer. The flushing behavior of inflate is +// not specified for values of the flush parameter other than Z_SYNC_FLUSH +// and Z_FINISH, but the current implementation actually flushes as much output +// as possible anyway. +// +// inflate() should normally be called until it returns Z_STREAM_END or an +// error. However if all decompression is to be performed in a single step +// (a single call of inflate), the parameter flush should be set to +// Z_FINISH. In this case all pending input is processed and all pending +// output is flushed; avail_out must be large enough to hold all the +// uncompressed data. (The size of the uncompressed data may have been saved +// by the compressor for this purpose.) The next operation on this stream must +// be inflateEnd to deallocate the decompression state. The use of Z_FINISH +// is never required, but can be used to inform inflate that a faster routine +// may be used for the single inflate() call. +// +// If a preset dictionary is needed at this point (see inflateSetDictionary +// below), inflate sets strm-adler to the adler32 checksum of the +// dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise +// it sets strm->adler to the adler32 checksum of all output produced +// so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or +// an error code as described below. At the end of the stream, inflate() +// checks that its computed adler32 checksum is equal to that saved by the +// compressor and returns Z_STREAM_END only if the checksum is correct. +// +// inflate() returns Z_OK if some progress has been made (more input processed +// or more output produced), Z_STREAM_END if the end of the compressed data has +// been reached and all uncompressed output has been produced, Z_NEED_DICT if a +// preset dictionary is needed at this point, Z_DATA_ERROR if the input data was +// corrupted (input stream not conforming to the zlib format or incorrect +// adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent +// (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not +// enough memory, Z_BUF_ERROR if no progress is possible or if there was not +// enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR +// case, the application may then call inflateSync to look for a good +// compression block. +// + + +int inflateEnd (z_streamp strm); +// +// All dynamically allocated data structures for this stream are freed. +// This function discards any unprocessed input and does not flush any +// pending output. +// +// inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state +// was inconsistent. In the error case, msg may be set but then points to a +// static string (which must not be deallocated). + + // Advanced functions + +// The following functions are needed only in some special applications. + + + + + +int inflateSetDictionary (z_streamp strm, + const Byte *dictionary, + uInt dictLength); +// +// Initializes the decompression dictionary from the given uncompressed byte +// sequence. This function must be called immediately after a call of inflate +// if this call returned Z_NEED_DICT. The dictionary chosen by the compressor +// can be determined from the Adler32 value returned by this call of +// inflate. The compressor and decompressor must use exactly the same +// dictionary. +// +// inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a +// parameter is invalid (such as NULL dictionary) or the stream state is +// inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the +// expected one (incorrect Adler32 value). inflateSetDictionary does not +// perform any decompression: this will be done by subsequent calls of +// inflate(). + + +int inflateSync (z_streamp strm); +// +// Skips invalid compressed data until a full flush point can be found, or until all +// available input is skipped. No output is provided. +// +// inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR +// if no more input was provided, Z_DATA_ERROR if no flush point has been found, +// or Z_STREAM_ERROR if the stream structure was inconsistent. In the success +// case, the application may save the current current value of total_in which +// indicates where valid compressed data was found. In the error case, the +// application may repeatedly call inflateSync, providing more input each time, +// until success or end of the input data. + + +int inflateReset (z_streamp strm); +// This function is equivalent to inflateEnd followed by inflateInit, +// but does not free and reallocate all the internal decompression state. +// The stream will keep attributes that may have been set by inflateInit2. +// +// inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source +// stream state was inconsistent (such as zalloc or state being NULL). +// + + + +// checksum functions +// These functions are not related to compression but are exported +// anyway because they might be useful in applications using the +// compression library. + +uLong adler32 (uLong adler, const Byte *buf, uInt len); +// Update a running Adler-32 checksum with the bytes buf[0..len-1] and +// return the updated checksum. If buf is NULL, this function returns +// the required initial value for the checksum. +// An Adler-32 checksum is almost as reliable as a CRC32 but can be computed +// much faster. Usage example: +// +// uLong adler = adler32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// adler = adler32(adler, buffer, length); +// } +// if (adler != original_adler) error(); + +uLong ucrc32 (uLong crc, const Byte *buf, uInt len); +// Update a running crc with the bytes buf[0..len-1] and return the updated +// crc. If buf is NULL, this function returns the required initial value +// for the crc. Pre- and post-conditioning (one's complement) is performed +// within this function so it shouldn't be done by the application. +// Usage example: +// +// uLong crc = crc32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// crc = crc32(crc, buffer, length); +// } +// if (crc != original_crc) error(); + + + + +const char *zError (int err); +int inflateSyncPoint (z_streamp z); +const uLong *get_crc_table (void); + + + +typedef unsigned char uch; +typedef uch uchf; +typedef unsigned short ush; +typedef ush ushf; +typedef unsigned long ulg; + + + +const char * const z_errmsg[10] = { // indexed by 2-zlib_error +"need dictionary", // Z_NEED_DICT 2 +"stream end", // Z_STREAM_END 1 +"", // Z_OK 0 +"file error", // Z_ERRNO (-1) +"stream error", // Z_STREAM_ERROR (-2) +"data error", // Z_DATA_ERROR (-3) +"insufficient memory", // Z_MEM_ERROR (-4) +"buffer error", // Z_BUF_ERROR (-5) +"incompatible version",// Z_VERSION_ERROR (-6) +""}; + + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +// To be used only when the state is known to be valid + + // common constants + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + +#define PRESET_DICT 0x20 // preset dictionary flag in zlib header + + // target dependencies + +#define OS_CODE 0x0b // Window 95 & Windows NT + + + + // functions + +#define zmemzero(dest, len) memset(dest, 0, len) + +// Diagnostic functions +#undef Assert +#undef Trace +#undef Tracev +#undef Tracevv +#undef Tracec +#undef Tracecv + +#ifdef DEBUG + + int z_verbose = 0; + void z_error (char *m) {fprintf(stderr, "%s\n", m); exit(1);} + +#define Assert(cond,msg) {if(!(cond)) z_error(msg);} +#define Trace(x) {if (z_verbose>=0) fprintf x ;} +#define Tracev(x) {if (z_verbose>0) fprintf x ;} +#define Tracevv(x) {if (z_verbose>1) fprintf x ;} +#define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +#define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} + +#else + +#ifndef __noop +#if _MSC_VER < 1300 +#define __noop ((void)0) +#endif +#endif + +#define Assert(cond,msg) __noop +#define Trace(x) __noop +#define Tracev(x) __noop +#define Tracevv(x) __noop +#define Tracec(c,x) __noop +#define Tracecv(c,x) __noop + +#endif + + +typedef uLong (*check_func) (uLong check, const Byte *buf, uInt len); +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size); +void zcfree (voidpf opaque, voidpf ptr); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) + +//void ZFREE(z_streamp strm,voidpf addr) +//{ *((strm)->zfree))((strm)->opaque, addr); +//} + +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + + + + +// Huffman code lookup table entry--this entry is four bytes for machines +// that have 16-bit pointers (e.g. PC's in the small or medium model). + + +typedef struct inflate_huft_s inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; // number of extra bits or operation + Byte Bits; // number of bits in this code or subcode + } what; + uInt pad; // pad structure to a power of 2 (4 bytes for + } word; // 16-bit, 8 bytes for 32-bit int's) + uInt base; // literal, length base, distance base, or table offset +}; + +// Maximum size of dynamic tree. The maximum found in a long but non- +// exhaustive search was 1004 huft structures (850 for length/literals +// and 154 for distances, the latter actually the result of an +// exhaustive search). The actual maximum is not known, but the +// value below is more than safe. +#define MANY 1440 + +int inflate_trees_bits ( + uInt *, // 19 code lengths + uInt *, // bits tree desired/actual depth + inflate_huft * *, // bits tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_dynamic ( + uInt, // number of literal/length codes + uInt, // number of distance codes + uInt *, // that many (total) code lengths + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + inflate_huft * *, // literal/length tree result + inflate_huft * *, // distance tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_fixed ( + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + const inflate_huft * *, // literal/length tree result + const inflate_huft * *, // distance tree result + z_streamp); // for memory allocation + + + + + +struct inflate_blocks_state; +typedef struct inflate_blocks_state inflate_blocks_statef; + +inflate_blocks_statef * inflate_blocks_new ( + z_streamp z, + check_func c, // check function + uInt w); // window size + +int inflate_blocks ( + inflate_blocks_statef *, + z_streamp , + int); // initial return code + +void inflate_blocks_reset ( + inflate_blocks_statef *, + z_streamp , + uLong *); // check value on output + +int inflate_blocks_free ( + inflate_blocks_statef *, + z_streamp); + +void inflate_set_dictionary ( + inflate_blocks_statef *s, + const Byte *d, // dictionary + uInt n); // dictionary length + +int inflate_blocks_sync_point ( + inflate_blocks_statef *s); + + + + +struct inflate_codes_state; +typedef struct inflate_codes_state inflate_codes_statef; + +inflate_codes_statef *inflate_codes_new ( + uInt, uInt, + const inflate_huft *, const inflate_huft *, + z_streamp ); + +int inflate_codes ( + inflate_blocks_statef *, + z_streamp , + int); + +void inflate_codes_free ( + inflate_codes_statef *, + z_streamp ); + + + + +typedef enum { + IBM_TYPE, // get type bits (3, including end bit) + IBM_LENS, // get lengths for stored + IBM_STORED, // processing stored block + IBM_TABLE, // get table lengths + IBM_BTREE, // get bit lengths tree for a dynamic block + IBM_DTREE, // get length, distance trees for a dynamic block + IBM_CODES, // processing fixed or dynamic block + IBM_DRY, // output remaining window bytes + IBM_DONE, // finished last block, done + IBM_BAD} // got a data error--stuck here +inflate_block_mode; + +// inflate blocks semi-private state +struct inflate_blocks_state { + + // mode + inflate_block_mode mode; // current inflate_block mode + + // mode dependent information + union { + uInt left; // if STORED, bytes left to copy + struct { + uInt table; // table lengths (14 bits) + uInt index; // index into blens (or border) + uInt *blens; // bit lengths of codes + uInt bb; // bit length tree depth + inflate_huft *tb; // bit length decoding tree + } trees; // if DTREE, decoding info for trees + struct { + inflate_codes_statef + *codes; + } decode; // if CODES, current state + } sub; // submode + uInt last; // true if this block is the last block + + // mode independent information + uInt bitk; // bits in bit buffer + uLong bitb; // bit buffer + inflate_huft *hufts; // single malloc for tree space + Byte *window; // sliding window + Byte *end; // one byte after sliding window + Byte *read; // window read pointer + Byte *write; // window write pointer + check_func checkfn; // check function + uLong check; // check on output + +}; + + +// defines for inflate input/output +// update pointers and return +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=(uLong)(p-z->next_in);z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +// get bytes and bits +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +// output bytes +#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;m;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +// load local pointers +#define LOAD {LOADIN LOADOUT} + +// masks for lower bits (size given to avoid silly warnings with Visual C++) +// And'ing with mask[n] masks the lower n bits +const uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +// copy as much as possible from the sliding window to the output area +int inflate_flush (inflate_blocks_statef *, z_streamp, int); + +int inflate_fast (uInt, uInt, const inflate_huft *, const inflate_huft *, inflate_blocks_statef *, z_streamp ); + + + +const uInt fixed_bl = 9; +const uInt fixed_bd = 5; +const inflate_huft fixed_tl[] = { + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} + }; +const inflate_huft fixed_td[] = { + {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, + {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, + {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, + {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, + {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, + {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, + {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, + {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} + }; + + + + + + + +// copy as much as possible from the sliding window to the output area +int inflate_flush(inflate_blocks_statef *s,z_streamp z,int r) +{ + uInt n; + Byte *p; + Byte *q; + + // local copies of source and destination pointers + p = z->next_out; + q = s->read; + + // compute number of bytes to copy as far as end of window + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy as far as end of window + if (n!=0) // check for n!=0 to avoid waking up CodeGuard + { memcpy(p, q, n); + p += n; + q += n; + } + + // see if more to copy at beginning of window + if (q == s->end) + { + // wrap pointers + q = s->window; + if (s->write == s->end) + s->write = s->window; + + // compute bytes to copy + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy + memcpy(p, q, n); + p += n; + q += n; + } + + // update pointers + z->next_out = p; + s->read = q; + + // done + return r; +} + + + + + + +// simplify the use of the inflate_huft type with some defines +#define exop word.what.Exop +#define bits word.what.Bits + +typedef enum { // waiting for "i:"=input, "o:"=output, "x:"=nothing + START, // x: set up for LEN + LEN, // i: get length/literal/eob next + LENEXT, // i: getting length extra (have base) + DIST, // i: get distance next + DISTEXT, // i: getting distance extra + COPY, // o: copying bytes in window, waiting for space + LIT, // o: got literal, waiting for output space + WASH, // o: got eob, possibly still output waiting + END, // x: got eob and all data flushed + BADCODE} // x: got error +inflate_codes_mode; + +// inflate codes private state +struct inflate_codes_state { + + // mode + inflate_codes_mode mode; // current inflate_codes mode + + // mode dependent information + uInt len; + union { + struct { + const inflate_huft *tree; // pointer into tree + uInt need; // bits needed + } code; // if LEN or DIST, where in tree + uInt lit; // if LIT, literal + struct { + uInt get; // bits to get for extra + uInt dist; // distance back to copy from + } copy; // if EXT or COPY, where and how much + } sub; // submode + + // mode independent information + Byte lbits; // ltree bits decoded per branch + Byte dbits; // dtree bits decoder per branch + const inflate_huft *ltree; // literal/length/eob tree + const inflate_huft *dtree; // distance tree + +}; + + +inflate_codes_statef *inflate_codes_new( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +z_streamp z) +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt j; // temporary storage + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + Byte *f; // pointer to copy strings from + inflate_codes_statef *c = s->sub.decode.codes; // codes state + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input and output based on current state + for(;;) switch (c->mode) + { // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif // !SLOW + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: // i: get length/literal/eob next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) // literal + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) // length + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + if (e & 32) // end of block + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: // i: getting length extra (have base) + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: // i: get distance next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) // distance + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: // i: getting distance extra + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: // o: copying bytes in window, waiting for space + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: // o: got literal, waiting for output space + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: // o: got eob, possibly more output + if (k > 7) // return unused byte, if any + { + Assert(k < 16, "inflate_codes grabbed too many bytes"); + k -= 8; + n++; + p--; // can always return one + } + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: // x: got error + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +void inflate_codes_free(inflate_codes_statef *c,z_streamp z) +{ ZFREE(z, c); + Tracev((stderr, "inflate: codes free\n")); +} + + + +// infblock.c -- interpret and process block types to last block +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + + +// Table for deflate from PKZIP's appnote.txt. +const uInt border[] = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +// +// Notes beyond the 1.93a appnote.txt: +// +// 1. Distance pointers never point before the beginning of the output stream. +// 2. Distance pointers can point back across blocks, up to 32k away. +// 3. There is an implied maximum of 7 bits for the bit length table and +// 15 bits for the actual data. +// 4. If only one code exists, then it is encoded using one bit. (Zero +// would be more efficient, but perhaps a little confusing.) If two +// codes exist, they are coded using one bit each (0 and 1). +// 5. There is no way of sending zero distance codes--a dummy must be +// sent if there are none. (History: a pre 2.0 version of PKZIP would +// store blocks with no distance codes, but this was discovered to be +// too harsh a criterion.) Valid only for 1.93a. 2.04c does allow +// zero distance codes, which is sent as one code of zero bits in +// length. +// 6. There are up to 286 literal/length codes. Code 256 represents the +// end-of-block. Note however that the static length tree defines +// 288 codes just to fill out the Huffman codes. Codes 286 and 287 +// cannot be used though, since there is no length base or extra bits +// defined for them. Similarily, there are up to 30 distance codes. +// However, static trees define 32 codes (all 5 bits) to fill out the +// Huffman codes, but the last two had better not show up in the data. +// 7. Unzip can check dynamic Huffman blocks for complete code sets. +// The exception is that a single code would not be complete (see #4). +// 8. The five bits following the block type is really the number of +// literal codes sent minus 257. +// 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits +// (1+6+6). Therefore, to output three times the length, you output +// three codes (1+1+1), whereas to output four times the same length, +// you only need two codes (1+3). Hmm. +//10. In the tree reconstruction algorithm, Code = Code + Increment +// only if BitLength(i) is not zero. (Pretty obvious.) +//11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) +//12. Note: length code 284 can represent 227-258, but length code 285 +// really is 258. The last length deserves its own, short code +// since it gets used a lot in very redundant files. The length +// 258 is special since 258 - 3 (the min match length) is 255. +//13. The literal/length and distance code bit lengths are read as a +// single stream of lengths. It is possible (and advantageous) for +// a repeat code (16, 17, or 18) to go across the boundary between +// the two sets of lengths. + + +void inflate_blocks_reset(inflate_blocks_statef *s, z_streamp z, uLong *c) +{ + if (c != Z_NULL) + *c = s->check; + if (s->mode == IBM_BTREE || s->mode == IBM_DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == IBM_CODES) + inflate_codes_free(s->sub.decode.codes, z); + s->mode = IBM_TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, (const Byte *)Z_NULL, 0); + Tracev((stderr, "inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z_streamp z, check_func c, uInt w) +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->hufts = + (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + if ((s->window = (Byte *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s->hufts); + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = IBM_TYPE; + Tracev((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, Z_NULL); + return s; +} + + +int inflate_blocks(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt t; // temporary storage + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input based on current state + for(;;) switch (s->mode) + { + case IBM_TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: // stored + Tracev((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; // go to byte boundary + DUMPBITS(t) + s->mode = IBM_LENS; // get length of stored block + break; + case 1: // fixed + Tracev((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + const inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td, z); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + } + DUMPBITS(3) + s->mode = IBM_CODES; + break; + case 2: // dynamic + Tracev((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = IBM_TABLE; + break; + case 3: // illegal + DUMPBITS(3) + s->mode = IBM_BAD; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case IBM_LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = IBM_BAD; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; // dump bits + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? IBM_STORED : (s->last ? IBM_DRY : IBM_TYPE); + break; + case IBM_STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + memcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? IBM_DRY : IBM_TYPE; + break; + case IBM_TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; + // remove this section to workaround bug in pkzip + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = IBM_BAD; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } + // end remove + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if ((s->sub.trees.blens = (uInt*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = IBM_BTREE; + case IBM_BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, s->hufts, z); + if (t != Z_OK) + { + ZFREE(z, s->sub.trees.blens); + r = t; + if (r == Z_DATA_ERROR) + s->mode = IBM_BAD; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = IBM_DTREE; + case IBM_DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->bits; + c = h->base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else // c == 16..18 + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + ZFREE(z, s->sub.trees.blens); + s->mode = IBM_BAD; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; // must be <= 9 for lookahead assumptions + bd = 6; // must be <= 9 for lookahead assumptions + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, + s->hufts, z); + ZFREE(z, s->sub.trees.blens); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = IBM_BAD; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + } + s->mode = IBM_CODES; + case IBM_CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = IBM_TYPE; + break; + } + s->mode = IBM_DRY; + case IBM_DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = IBM_DONE; + case IBM_DONE: + r = Z_STREAM_END; + LEAVE + case IBM_BAD: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(inflate_blocks_statef *s, z_streamp z) +{ + inflate_blocks_reset(s, z, Z_NULL); + ZFREE(z, s->window); + ZFREE(z, s->hufts); + ZFREE(z, s); + Tracev((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + + +// inftrees.c -- generate Huffman trees for efficient decoding +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + + +extern const char inflate_copyright[] = + " ";//inflate 1.1.3 Copyright 1995-1998 Mark Adler "; +// If you use the zlib library in a product, an acknowledgment is welcome +// in the documentation of your product. If for some reason you cannot +// include such an acknowledgment, I would appreciate that you keep this +// copyright string in the executable of your product. + + + +int huft_build ( + uInt *, // code lengths in bits + uInt, // number of codes + uInt, // number of "simple" codes + const uInt *, // list of base values for non-simple codes + const uInt *, // list of extra bits for non-simple codes + inflate_huft **,// result: starting table + uInt *, // maximum lookup bits (returns actual) + inflate_huft *, // space for trees + uInt *, // hufts used in space + uInt * ); // space for values + +// Tables for deflate from PKZIP's appnote.txt. +const uInt cplens[31] = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + // see note #13 above about 258 +const uInt cplext[31] = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; // 112==invalid +const uInt cpdist[30] = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +const uInt cpdext[30] = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +// +// Huffman code decoding is performed using a multi-level table lookup. +// The fastest way to decode is to simply build a lookup table whose +// size is determined by the longest code. However, the time it takes +// to build this table can also be a factor if the data being decoded +// is not very long. The most common codes are necessarily the +// shortest codes, so those codes dominate the decoding time, and hence +// the speed. The idea is you can have a shorter table that decodes the +// shorter, more probable codes, and then point to subsidiary tables for +// the longer codes. The time it costs to decode the longer codes is +// then traded against the time it takes to make longer tables. +// +// This results of this trade are in the variables lbits and dbits +// below. lbits is the number of bits the first level table for literal/ +// length codes can decode in one step, and dbits is the same thing for +// the distance codes. Subsequent tables are also less than or equal to +// those sizes. These values may be adjusted either when all of the +// codes are shorter than that, in which case the longest code length in +// bits is used, or when the shortest code is *longer* than the requested +// table size, in which case the length of the shortest code in bits is +// used. +// +// There are two different values for the two tables, since they code a +// different number of possibilities each. The literal/length table +// codes 286 possible values, or in a flat code, a little over eight +// bits. The distance table codes 30 possible values, or a little less +// than five bits, flat. The optimum values for speed end up being +// about one bit more than those, so lbits is 8+1 and dbits is 5+1. +// The optimum values may differ though from machine to machine, and +// possibly even between compilers. Your mileage may vary. +// + + +// If BMAX needs to be larger than 16, then h and x[] should be uLong. +#define BMAX 15 // maximum bit length of any code + +int huft_build( +uInt *b, // code lengths in bits (all assumed <= BMAX) +uInt n, // number of codes (assumed <= 288) +uInt s, // number of simple-valued codes (0..s-1) +const uInt *d, // list of base values for non-simple codes +const uInt *e, // list of extra bits for non-simple codes +inflate_huft * *t, // result: starting table +uInt *m, // maximum lookup bits, returns actual +inflate_huft *hp, // space for trees +uInt *hn, // hufts used in space +uInt *v) // working area: values in order of bit length +// Given a list of code lengths and a maximum table size, make a set of +// tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR +// if the given code set is incomplete (the tables are still built in this +// case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of +// lengths), or Z_MEM_ERROR if not enough memory. +{ + + uInt a; // counter for codes of length k + uInt c[BMAX+1]; // bit length count table + uInt f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + register uInt i; // counter, current code + register uInt j; // counter + register int k; // number of bits in current code + int l; // bits per table (returned in m) + uInt mask; // (1 << w) - 1, to avoid cc -O bug on HP + register uInt *p; // pointer into c[], b[], or v[] + inflate_huft *q; // points to current table + struct inflate_huft_s r; // table entry for structure assignment + inflate_huft *u[BMAX]; // table stack + register int w; // bits before this table == (l * h) + uInt x[BMAX+1]; // bit offsets, then code stack + uInt *xp; // pointer into x + int y; // number of dummy codes added + uInt z; // number of entries in current table + + + // Generate counts for each bit length + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4; p; // clear c[]--assume BMAX+1 is 16 + p = b; i = n; + do { + c[*p++]++; // assume all entries <= BMAX + } while (--i); + if (c[0] == n) // null input--all zero length codes + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + // Find minimum and maximum length, bound *m by those + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; // minimum code length + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; // maximum code length + if ((uInt)l > i) + l = i; + *m = l; + + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { // note that i == g from above + *xp++ = (j += *p++); + } + + + // Make a table of values in order of bit lengths + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; // set n to length of v + + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = v; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = (inflate_huft *)Z_NULL; // just to keep compilers happy + q = (inflate_huft *)Z_NULL; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + + // compute minimum size table less than or equal to l bits + z = g - w; + z = z > (uInt)l ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) // try a k-w bit table + { // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = c + k; + if (j < z) + while (++j < z) // try smaller tables up to z bits + { + if ((f <<= 1) <= *++xp) + break; // enough codes to use up j bits + f -= *xp; // else deduct codes from patterns + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (*hn + z > MANY) // (note: doesn't matter for fixed) + return Z_MEM_ERROR; // not enough memory + u[h] = q = hp + *hn; + *hn += z; + + // connect to last table, if there is one + if (h) + { + x[h] = i; // save pattern for backing up + r.bits = (Byte)l; // bits to dump before this table + r.exop = (Byte)j; // bits in this table + j = i >> (w - l); + r.base = (uInt)(q - u[h-1] - j); // offset to this table + u[h-1][j] = r; // connect to last table + } + else + *t = q; // first table is returned result + } + + // set up table entry in r + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; // out of values--invalid code + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); // 256 is end-of-block + r.base = *p++; // simple code is just the value + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);// non-simple--look up in lists + r.base = d[*p++ - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + + + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits( +uInt *c, // 19 code lengths +uInt *bb, // bits tree desired/actual depth +inflate_huft * *tb, // bits tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + if ((v = (uInt*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + r = huft_build(c, 19, 19, (uInt*)Z_NULL, (uInt*)Z_NULL, + tb, bb, hp, &hn, v); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +} + + +int inflate_trees_dynamic( +uInt nl, // number of literal/length codes +uInt nd, // number of distance codes +uInt *c, // that many (total) code lengths +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +inflate_huft * *tl, // literal/length tree result +inflate_huft * *td, // distance tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + // allocate work area + if ((v = (uInt*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + + // build literal/length tree + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // build distance tree + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // done + ZFREE(z, v); + return Z_OK; +} + + + + + +int inflate_trees_fixed( +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +const inflate_huft * * tl, // literal/length tree result +const inflate_huft * *td, // distance tree result +z_streamp ) // for memory allocation +{ + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +// inffast.c -- process literals and length/distance pairs fast +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + +// macros for bit input with no checking and for returning unused bytes +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} + +// Called with number of bytes left to write in window at least 258 +// (the maximum string length) and number of input bytes available +// at least ten. The ten bytes are six bytes for the longest length/ +// distance pair plus four bytes for overloading the bit buffer. + +int inflate_fast( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +inflate_blocks_statef *s, +z_streamp z) +{ + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + uInt ml; // mask for literal/length tree + uInt md; // mask for distance tree + uInt c; // bytes to copy + uInt d; // distance back to copy from + Byte *r; // copy source pointer + + // load input, output, bit values + LOAD + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do { // assume called with m >= 258 && n >= 10 + // get literal/length code + GRABBITS(20) // max bits for literal/length code + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits for length + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + // decode distance base of block to copy + GRABBITS(15); // max bits for distance code + e = (t = td + ((uInt)b & md))->exop; + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits to add to distance base + e &= 15; + GRABBITS(e) // get extra bits (up to 13) + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + // do the copy + m -= c; + if ((uInt)(q - s->window) >= d) // offset before dest + { // just copy + r = q - d; + *q++ = *r++; c--; // minimum count is three, + *q++ = *r++; c--; // so unroll loop a little + } + else // else offset after destination + { + e = d - (uInt)(q - s->window); // bytes from offset to end + r = s->end - e; // pointer to offset + if (c > e) // if source crosses, + { + c -= e; // copy to end of window + do { + *q++ = *r++; + } while (--e); + r = s->window; // copy rest from start of window + } + } + do { // copy all or what's left + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + { + t += t->base; + e = (t += ((uInt)b & inflate_mask[e]))->exop; + } + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + break; + } + if ((e & 64) == 0) + { + t += t->base; + if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + } while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + UNGRAB + UPDATE + return Z_OK; +} + + + + + + +// crc32.c -- compute the CRC-32 of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + + + + + +// Table of CRC-32's of all single-byte values (made by make_crc_table) +const uLong crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +const uLong * get_crc_table() +{ return (const uLong *)crc_table; +} + +#define CRC_DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define CRC_DO2(buf) CRC_DO1(buf); CRC_DO1(buf); +#define CRC_DO4(buf) CRC_DO2(buf); CRC_DO2(buf); +#define CRC_DO8(buf) CRC_DO4(buf); CRC_DO4(buf); + +uLong ucrc32(uLong crc, const Byte *buf, uInt len) +{ if (buf == Z_NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) {CRC_DO8(buf); len -= 8;} + if (len) do {CRC_DO1(buf);} while (--len); + return crc ^ 0xffffffffL; +} + + +// adler32.c -- compute the Adler-32 checksum of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + +#define BASE 65521L // largest prime smaller than 65536 +#define NMAX 5552 +// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + +#define AD_DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define AD_DO2(buf,i) AD_DO1(buf,i); AD_DO1(buf,i+1); +#define AD_DO4(buf,i) AD_DO2(buf,i); AD_DO2(buf,i+2); +#define AD_DO8(buf,i) AD_DO4(buf,i); AD_DO4(buf,i+4); +#define AD_DO16(buf) AD_DO8(buf,0); AD_DO8(buf,8); + +// ========================================================================= +uLong adler32(uLong adler, const Byte *buf, uInt len) +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + AD_DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} + + + +// zutil.c -- target dependent utility functions for the compression library +// Copyright (C) 1995-1998 Jean-loup Gailly. +// For conditions of distribution and use, see copyright notice in zlib.h +// @(#) $Id$ + + + + + + +const char * zlibVersion() +{ + return ZLIB_VERSION; +} + +// exported to allow conversion of error code to string for compress() and +// uncompress() +const char * zError(int err) +{ return ERR_MSG(err); +} + + + + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) items += size - size; // make compiler happy + return (voidpf)calloc(items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + zfree(ptr); + if (opaque) return; // make compiler happy +} + + + +// inflate.c -- zlib interface to inflate modules +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_blocks_state {int dummy;}; // for buggy compilers + +typedef enum { + IM_METHOD, // waiting for method byte + IM_FLAG, // waiting for flag byte + IM_DICT4, // four dictionary check bytes to go + IM_DICT3, // three dictionary check bytes to go + IM_DICT2, // two dictionary check bytes to go + IM_DICT1, // one dictionary check byte to go + IM_DICT0, // waiting for inflateSetDictionary + IM_BLOCKS, // decompressing blocks + IM_CHECK4, // four check bytes to go + IM_CHECK3, // three check bytes to go + IM_CHECK2, // two check bytes to go + IM_CHECK1, // one check byte to go + IM_DONE, // finished check, done + IM_BAD} // got an error--stay here +inflate_mode; + +// inflate private state +struct internal_state { + + // mode + inflate_mode mode; // current inflate mode + + // mode dependent information + union { + uInt method; // if IM_FLAGS, method byte + struct { + uLong was; // computed check value + uLong need; // stream check value + } check; // if CHECK, check values to compare + uInt marker; // if IM_BAD, inflateSync's marker bytes count + } sub; // submode + + // mode independent information + int nowrap; // flag for no wrapper + uInt wbits; // log2(window size) (8..15, defaults to 15) + inflate_blocks_statef + *blocks; // current inflate_blocks state + +}; + +int inflateReset(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? IM_BLOCKS : IM_METHOD; + inflate_blocks_reset(z->state->blocks, z, Z_NULL); + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int inflateEnd(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z); + ZFREE(z, z->state); + z->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z_streamp z) +{ const char *version = ZLIB_VERSION; int stream_size = sizeof(z_stream); + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != sizeof(z_stream)) return Z_VERSION_ERROR; + + int w = -15; // MAX_WBITS: 32K LZ77 window. + // Warning: reducing MAX_WBITS makes minigzip unable to extract .gz files created by gzip. + // The memory requirements for deflate are (in bytes): + // (1 << (windowBits+2)) + (1 << (memLevel+9)) + // that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + // plus a few kilobytes for small objects. For example, if you want to reduce + // the default memory requirements from 256K to 128K, compile with + // make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + // Of course this will generally degrade compression (there's no free lunch). + // + // The memory requirements for inflate are (in bytes) 1 << windowBits + // that is, 32K for windowBits=15 (default value) plus a few kilobytes + // for small objects. + + // initialize state + if (z == Z_NULL) return Z_STREAM_ERROR; + z->msg = Z_NULL; + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; + if ((z->state = (struct internal_state *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + // handle undocumented nowrap option (no zlib header or check) + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + // create inflate_blocks state + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Tracev((stderr, "inflate: allocated\n")); + + // reset state + inflateReset(z); + return Z_OK; +} + + + +#define IM_NEEDBYTE {if(z->avail_in==0)return r;r=f;} +#define IM_NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z_streamp z, int f) +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + for (;;) switch (z->state->mode) + { + case IM_METHOD: + IM_NEEDBYTE + if (((z->state->sub.method = IM_NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = IM_BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = IM_BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + z->state->mode = IM_FLAG; + case IM_FLAG: + IM_NEEDBYTE + b = IM_NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = IM_BLOCKS; + break; + } + z->state->mode = IM_DICT4; + case IM_DICT4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_DICT3; + case IM_DICT3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_DICT2; + case IM_DICT2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_DICT1; + case IM_DICT1: + IM_NEEDBYTE; r; + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = IM_DICT0; + return Z_NEED_DICT; + case IM_DICT0: + z->state->mode = IM_BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case IM_BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (r == Z_DATA_ERROR) + { + z->state->mode = IM_BAD; + z->state->sub.marker = 0; // can try inflateSync + break; + } + if (r == Z_OK) + r = f; + if (r != Z_STREAM_END) + return r; + r = f; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = IM_DONE; + break; + } + z->state->mode = IM_CHECK4; + case IM_CHECK4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_CHECK3; + case IM_CHECK3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_CHECK2; + case IM_CHECK2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_CHECK1; + case IM_CHECK1: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib check ok\n")); + z->state->mode = IM_DONE; + case IM_DONE: + return Z_STREAM_END; + case IM_BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } +} + + + +#ifdef _UNICODE + +static int GetAnsiFileName(LPCWSTR name, char * buf, int nBufSize) +{ + memset(buf, 0, nBufSize); + + int n = WideCharToMultiByte(CP_ACP, // code page + 0, // performance and mapping flags + name, // wide-character string + -1, // number of chars in string + buf, // buffer for new string + nBufSize, // size of buffer + NULL, // default for unmappable chars + NULL); // set when default char used + return n; +} + +static int GetUnicodeFileName(const char * name, LPWSTR buf, int nBufSize) +{ + memset(buf, 0, nBufSize*sizeof(TCHAR)); + + int n = MultiByteToWideChar(CP_ACP, // code page + 0, // character-type options + name, // string to map + -1, // number of bytes in string + buf, // wide-character buffer + nBufSize); // size of buffer + + return n; +} + +#endif + + +// unzip.c -- IO on .zip files using zlib +// Version 0.15 beta, Mar 19th, 1998, +// Read unzip.h for more info + + + + +#define UNZ_BUFSIZE (16384) +#define UNZ_MAXFILENAMEINZIP (256) +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = " ";//unzip 0.15 Copyright 1998 Gilles Vollant "; + +// unz_file_info_interntal contain internal info about a file in zipfile +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;// relative offset of local header 4 bytes +} unz_file_info_internal; + + +typedef struct +{ bool is_handle; // either a handle or memory + bool canseek; + // for handles: + HANDLE h; bool herr; unsigned long initial_offset; + // for memory: + void *buf; unsigned int len,pos; // if it's a memory block +} LUFILE; + + +LUFILE *lufopen(void *z,unsigned int len,DWORD flags,ZRESULT *err) +{ + if (flags!=ZIP_HANDLE && flags!=ZIP_FILENAME && flags!=ZIP_MEMORY) + { + *err=ZR_ARGS; + return NULL; + } + // + HANDLE h=0; bool canseek=false; *err=ZR_OK; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + if (flags==ZIP_HANDLE) + { + HANDLE hf = z; + + BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&h,0,FALSE,DUPLICATE_SAME_ACCESS); + + if (!res) + { + *err=ZR_NODUPH; + return NULL; + } + } + else + { + h = CreateFile((const TCHAR *)z, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) + { + *err = ZR_NOFILE; + return NULL; + } + } + DWORD type = GetFileType(h); + canseek = (type==FILE_TYPE_DISK); + } + LUFILE *lf = new LUFILE; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + lf->is_handle=true; + lf->canseek=canseek; + lf->h=h; lf->herr=false; + lf->initial_offset=0; + if (canseek) + lf->initial_offset = SetFilePointer(h,0,NULL,FILE_CURRENT); + } + else + { + lf->is_handle=false; + lf->canseek=true; + lf->buf=z; + lf->len=len; + lf->pos=0; + lf->initial_offset=0; + } + *err=ZR_OK; + return lf; +} + + +int lufclose(LUFILE *stream) +{ if (stream==NULL) return EOF; + if (stream->is_handle) CloseHandle(stream->h); + delete stream; + return 0; +} + +int luferror(LUFILE *stream) +{ if (stream->is_handle && stream->herr) return 1; + else return 0; +} + +long int luftell(LUFILE *stream) +{ if (stream->is_handle && stream->canseek) return SetFilePointer(stream->h,0,NULL,FILE_CURRENT)-stream->initial_offset; + else if (stream->is_handle) return 0; + else return stream->pos; +} + +int lufseek(LUFILE *stream, long offset, int whence) +{ if (stream->is_handle && stream->canseek) + { if (whence==SEEK_SET) SetFilePointer(stream->h,stream->initial_offset+offset,0,FILE_BEGIN); + else if (whence==SEEK_CUR) SetFilePointer(stream->h,offset,NULL,FILE_CURRENT); + else if (whence==SEEK_END) SetFilePointer(stream->h,offset,NULL,FILE_END); + else return 19; // EINVAL + return 0; + } + else if (stream->is_handle) return 29; // ESPIPE + else + { if (whence==SEEK_SET) stream->pos=offset; + else if (whence==SEEK_CUR) stream->pos+=offset; + else if (whence==SEEK_END) stream->pos=stream->len+offset; + return 0; + } +} + + +size_t lufread(void *ptr,size_t size,size_t n,LUFILE *stream) +{ unsigned int toread = (unsigned int)(size*n); + if (stream->is_handle) + { DWORD red; BOOL res = ReadFile(stream->h,ptr,toread,&red,NULL); + if (!res) stream->herr=true; + return red/size; + } + if (stream->pos+toread > stream->len) toread = stream->len-stream->pos; + memcpy(ptr, (char*)stream->buf + stream->pos, toread); DWORD red = toread; + stream->pos += red; + return red/size; +} + + + + +// file_in_zip_read_info_s contain internal information about a file in zipfile, +// when reading and decompress it +typedef struct +{ + char *read_buffer; // internal buffer for compressed data + z_stream stream; // zLib stream structure for inflate + + uLong pos_in_zipfile; // position in byte on the zipfile, for fseek + uLong stream_initialised; // flag set if stream structure is initialised + + uLong offset_local_extrafield;// offset of the local extra field + uInt size_local_extrafield;// size of the local extra field + uLong pos_local_extrafield; // position in the local extra field in read + + uLong crc32; // crc32 of all data uncompressed + uLong crc32_wait; // crc32 we must obtain after decompress all + uLong rest_read_compressed; // number of byte to be decompressed + uLong rest_read_uncompressed;//number of byte to be obtained after decomp + LUFILE* file; // io structore of the zipfile + uLong compression_method; // compression method (0==store) + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) +} file_in_zip_read_info_s; + + +// unz_s contain internal information about the zipfile +typedef struct +{ + LUFILE* file; // io structore of the zipfile + unz_global_info gi; // public global information + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) + uLong num_file; // number of the current file in the zipfile + uLong pos_in_central_dir; // pos of the current file in the central dir + uLong current_file_ok; // flag about the usability of the current file + uLong central_pos; // position of the beginning of the central dir + + uLong size_central_dir; // size of the central directory + uLong offset_central_dir; // offset of start of central directory with respect to the starting disk number + + unz_file_info cur_file_info; // public info about the current file in zip + unz_file_info_internal cur_file_info_internal; // private info about it + file_in_zip_read_info_s* pfile_in_zip_read; // structure about the current file if we are decompressing it +} unz_s, *unzFile; + + +int unzStringFileNameCompare (const char* fileName1,const char* fileName2,int iCaseSensitivity); +// Compare two filename (fileName1,fileName2). + +z_off_t unztell (unzFile file); +// Give the current position in uncompressed data + +int unzeof (unzFile file); +// return 1 if the end of file was reached, 0 elsewhere + +int unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len); +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// +// if buf==NULL, it return the size of the local extra field +// +// if buf!=NULL, len is the size of the buffer, the extra header is copied in +// buf. +// the return value is the number of bytes copied in buf, or (if <0) +// the error code + + + +// =========================================================================== +// Read a byte from a gz_stream; update next_in and avail_in. Return EOF +// for end of file. +// IN assertion: the stream s has been sucessfully opened for reading. + +int unzlocal_getByte(LUFILE *fin,int *pi) +{ unsigned char c; + int err = (int)lufread(&c, 1, 1, fin); + if (err==1) + { *pi = (int)c; + return UNZ_OK; + } + else + { if (luferror(fin)) return UNZ_ERRNO; + else return UNZ_EOF; + } +} + + +// =========================================================================== +// Reads a long in LSB order from the given gz_stream. Sets +int unzlocal_getShort (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +int unzlocal_getLong (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +// My own strcmpi / strcasecmp +int strcmpcasenosensitive_internal (const char* fileName1,const char *fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= (char)0x20; + if ((c2>='a') && (c2<='z')) + c2 -= (char)0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + + + +// +// Compare two filename (fileName1,fileName2). +// If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) +// If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) +// +int unzStringFileNameCompare (const char*fileName1,const char*fileName2,int iCaseSensitivity) +{ if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); + else return strcmpcasenosensitive_internal(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + + +// Locate the Central directory of a zipfile (at the end, just before +// the global comment) +uLong unzlocal_SearchCentralDir(LUFILE *fin) +{ if (lufseek(fin,0,SEEK_END) != 0) return 0; + uLong uSizeFile = luftell(fin); + + uLong uMaxBack=0xffff; // maximum size of global comment + if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; + + unsigned char *buf = (unsigned char*)zmalloc(BUFREADCOMMENT+4); + if (buf==NULL) return 0; + uLong uPosFound=0; + + uLong uBackRead = 4; + while (uBackReaduMaxBack) uBackRead = uMaxBack; + else uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (lufseek(fin,uReadPos,SEEK_SET)!=0) break; + if (lufread(buf,(uInt)uReadSize,1,fin)!=1) break; + for (i=(int)uReadSize-3; (i--)>0;) + { if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { uPosFound = uReadPos+i; break; + } + } + if (uPosFound!=0) break; + } + if (buf) zfree(buf); + return uPosFound; +} + + +int unzGoToFirstFile (unzFile file); +int unzCloseCurrentFile (unzFile file); + +// Open a Zip file. +// If the zipfile cannot be opened (file don't exist or in not valid), return NULL. +// Otherwise, the return value is a unzFile Handle, usable with other unzip functions +unzFile unzOpenInternal(LUFILE *fin) +{ + zopenerror = ZR_OK; //+++1.2 + if (fin==NULL) { zopenerror = ZR_ARGS; return NULL; } //+++1.2 + if (unz_copyright[0]!=' ') {lufclose(fin); zopenerror = ZR_CORRUPT; return NULL; } //+++1.2 + + int err=UNZ_OK; + unz_s us; + uLong central_pos,uL; + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) err=UNZ_ERRNO; + if (lufseek(fin,central_pos,SEEK_SET)!=0) err=UNZ_ERRNO; + // the signature, already checked + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) err=UNZ_ERRNO; + // number of this disk + uLong number_disk; // number of the current dist, used for spanning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; + // number of the disk with the start of the central directory + uLong number_disk_with_CD; // number the the disk with central dir, used for spaning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir on this disk + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir + uLong number_entry_CD; // total number of entries in the central dir (same than number_entry on nospan) + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; + if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; + // size of the central directory + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // offset of start of central directory with respect to the starting disk number + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // zipfile comment length + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; + if ((central_pos+fin->initial_offsetinitial_offset - (us.offset_central_dir+us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + fin->initial_offset = 0; // since the zipfile itself is expected to handle this + + unz_s *s = (unz_s*)zmalloc(sizeof(unz_s)); + *s=us; + unzGoToFirstFile((unzFile)s); + return (unzFile)s; +} + + + +// Close a ZipFile opened with unzipOpen. +// If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), +// these files MUST be closed with unzipCloseCurrentFile before call unzipClose. +// return UNZ_OK if there is no problem. +int unzClose (unzFile file) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + lufclose(s->file); + if (s) zfree(s); // unused s=0; + return UNZ_OK; +} + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +// Translate date/time from Dos format to tm_unz (readable more easilty) +void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +// Get Info about the current file in the zipfile, with internal only info +int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize); + +int unzlocal_GetCurrentFileInfoInternal (unzFile file, unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, char *szFileName, + uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (lufseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + // we check the magic + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (lufread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (lufread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + {} // unused lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (lufread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + //unused lSeek+=file_info.size_file_comment - uSizeRead; + } + else {} //unused lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, + char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, szComment,commentBufferSize); +} + + +// Set the current file of the zipfile to the first file. +// return UNZ_OK if there is no problem +int unzGoToFirstFile (unzFile file) +{ + int err; + unz_s* s; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Set the current file of the zipfile to the next file. +// return UNZ_OK if there is no problem +// return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +int unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Try locate the file szFileName in the zipfile. +// For the iCaseSensitivity signification, see unzStringFileNameCompare +// return value : +// UNZ_OK if the file is found. It becomes the current file. +// UNZ_END_OF_LIST_OF_FILE if the file is not found +int unzLocateFile (unzFile file, const TCHAR *szFileName, int iCaseSensitivity) +{ + unz_s* s; + int err; + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + if (file==NULL) + return UNZ_PARAMERROR; + + if (_tcslen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + char szFileNameA[MAX_PATH]; + +#ifdef _UNICODE + GetAnsiFileName(szFileName, szFileNameA, MAX_PATH-1); +#else + strcpy(szFileNameA, szFileName); +#endif + + // support Windows subdirectory by:daviyang35 + int iLen=strlen(szFileNameA); + for (int i=0;icurrent_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName,szFileNameA,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +// Read the local header of the current zipfile +// Check the coherency of the local header and info in the end of central +// directory about this file +// store in *piSizeVar the size of extra info in local header +// (filename and size of extra field data) +int unzlocal_CheckCurrentFileCoherencyHeader (unz_s *s,uInt *piSizeVar, + uLong *poffset_local_extrafield, uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (lufseek(s->file,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +// else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) +// err=UNZ_BADZIPFILE; + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // date/time + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // crc + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size compr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size uncompr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + + + + + +// Open for reading data the current file in the zipfile. +// If there is no error and the file is opened, the return value is UNZ_OK. +int unzOpenCurrentFile (unzFile file) +{ + int err; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; // offset of the local extra field + uInt size_local_extrafield; // size of the local extra field + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*)zmalloc(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)zmalloc(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); //unused pfile_in_zip_read_info=0; + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) + { // unused err=UNZ_BADZIPFILE; + } + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + // windowBits is passed < 0 to tell that there is no zlib header. + // Note that in this case inflate *requires* an extra "dummy" byte + // after the compressed stream in order to complete decompression and + // return Z_STREAM_END. + // In unzip, i don't wait absolutely Z_STREAM_END because I known the + // size of both compressed and uncompressed data + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +// Read bytes from the current file. +// buf contain buffer where data must be copied +// len the size of buf. +// return the number of byte copied if somes bytes are copied +// return 0 if the end of file was reached +// return <0 with error code if there is an error +// (UNZ_ERRNO for IO error, or zLib error for uncompress error) +int unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ int err=UNZ_OK; + uInt iRead = 0; + + unz_s *s = (unz_s*)file; + if (s==NULL) return UNZ_PARAMERROR; + + file_in_zip_read_info_s* pfile_in_zip_read_info = s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; + if ((pfile_in_zip_read_info->read_buffer == NULL)) return UNZ_END_OF_LIST_OF_FILE; + if (len==0) return 0; + + pfile_in_zip_read_info->stream.next_out = (Byte*)buf; + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + { pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + } + + while (pfile_in_zip_read_info->stream.avail_out>0) + { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) + { uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) return UNZ_EOF; + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) return UNZ_ERRNO; + if (lufread(pfile_in_zip_read_info->read_buffer,uReadThis,1,pfile_in_zip_read_info->file)!=1) return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + pfile_in_zip_read_info->stream.next_in = (Byte*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) + { uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + } + else + { uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + } + for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); + } + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,pfile_in_zip_read_info->stream.next_out,uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { uLong uTotalOutBefore,uTotalOutAfter; + const Byte *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + err=inflate(&pfile_in_zip_read_info->stream,flush); + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,bufBefore,(uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; //+++1.3 + //if (err==Z_STREAM_END) return (iRead==len) ? UNZ_EOF : iRead; //+++1.2 + + if (err != Z_OK) break; + } + } + + if (err==Z_OK) return iRead; + + return iRead; +} + + +// Give the current position in uncompressed data +z_off_t unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +// return 1 if the end of file was reached, 0 elsewhere +int unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// if buf==NULL, it return the size of the local extra field that can be read +// if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. +// the return value is the number of bytes copied in buf, or (if <0) the error code +int unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (lufread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +// Close the file in zip opened with unzipOpenCurrentFile +// Return UNZ_CRCERROR if all the file was read but the CRC is not good +int unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + if (pfile_in_zip_read_info->read_buffer!=0) + { void *buf = pfile_in_zip_read_info->read_buffer; + zfree(buf); + pfile_in_zip_read_info->read_buffer=0; + } + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); // unused pfile_in_zip_read_info=0; + + s->pfile_in_zip_read=NULL; + + return err; +} + + +// Get the global comment string of the ZipFile, in the szComment buffer. +// uSizeBuf is the size of the szComment buffer. +// return the number of byte copied or an error code <0 +int unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) +{ //int err=UNZ_OK; + unz_s* s; + uLong uReadThis ; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; + if (lufseek(s->file,s->central_pos+22,SEEK_SET)!=0) return UNZ_ERRNO; + if (uReadThis>0) + { *szComment='\0'; + if (lufread(szComment,(uInt)uReadThis,1,s->file)!=1) return UNZ_ERRNO; + } + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + + + + + +int unzOpenCurrentFile (unzFile file); +int unzReadCurrentFile (unzFile file, void *buf, unsigned len); +int unzCloseCurrentFile (unzFile file); + + +FILETIME timet2filetime(time_t timer) +{ + struct tm *tm = gmtime(&timer); + SYSTEMTIME st; + st.wYear = (WORD)(tm->tm_year+1900); + st.wMonth = (WORD)(tm->tm_mon+1); + st.wDay = (WORD)(tm->tm_mday); + st.wHour = (WORD)(tm->tm_hour); + st.wMinute = (WORD)(tm->tm_min); + st.wSecond = (WORD)(tm->tm_sec); + st.wMilliseconds=0; + FILETIME ft; + SystemTimeToFileTime(&st,&ft); + return ft; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class TUnzip +{ public: + TUnzip() : uf(0), currentfile(-1), czei(-1) {} + + unzFile uf; int currentfile; ZIPENTRY cze; int czei; + TCHAR rootdir[MAX_PATH]; + + ZRESULT Open(void *z,unsigned int len,DWORD flags); + ZRESULT Get(int index,ZIPENTRY *ze); + ZRESULT Find(const TCHAR *name,bool ic,int *index,ZIPENTRY *ze); + ZRESULT Unzip(int index,void *dst,unsigned int len,DWORD flags); + ZRESULT Close(); +}; + + +ZRESULT TUnzip::Open(void *z,unsigned int len,DWORD flags) +{ + if (uf!=0 || currentfile!=-1) + return ZR_NOTINITED; + GetCurrentDirectory(MAX_PATH,rootdir); + _tcscat(rootdir,_T("\\")); + if (flags==ZIP_HANDLE) + { + DWORD type = GetFileType(z); + if (type!=FILE_TYPE_DISK) + return ZR_SEEK; + } + ZRESULT e; + LUFILE *f = lufopen(z,len,flags,&e); + if (f==NULL) + return e; + uf = unzOpenInternal(f); + //return ZR_OK; + return zopenerror; //+++1.2 +} + +ZRESULT TUnzip::Get(int index,ZIPENTRY *ze) +{ if (index<-1 || index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index==czei && index!=-1) {memcpy(ze,&cze,sizeof(ZIPENTRY)); return ZR_OK;} + if (index==-1) + { ze->index = uf->gi.number_entry; + ze->name[0]=0; + ze->attr=0; + ze->atime.dwLowDateTime=0; ze->atime.dwHighDateTime=0; + ze->ctime.dwLowDateTime=0; ze->ctime.dwHighDateTime=0; + ze->mtime.dwLowDateTime=0; ze->mtime.dwHighDateTime=0; + ze->comp_size=0; + ze->unc_size=0; + return ZR_OK; + } + if (index<(int)uf->num_file) unzGoToFirstFile(uf); + while ((int)uf->num_filefile,offset,SEEK_SET)!=0) return ZR_READ; + char *extra = new char[extralen]; + if (lufread(extra,1,(uInt)extralen,uf->file)!=extralen) {delete[] extra; return ZR_READ;} + // + ze->index=uf->num_file; + strcpy(ze->name,fn); + // zip has an 'attribute' 32bit value. Its lower half is windows stuff + // its upper half is standard unix attr. + unsigned long a = ufi.external_fa; + bool uisdir = (a&0x40000000)!=0; + //bool uwriteable= (a&0x08000000)!=0; + bool uwriteable= (a&0x00800000)!=0; // ***hd*** + //bool ureadable= (a&0x01000000)!=0; + //bool uexecutable=(a&0x00400000)!=0; + bool wreadonly= (a&0x00000001)!=0; + bool whidden= (a&0x00000002)!=0; + bool wsystem= (a&0x00000004)!=0; + bool wisdir= (a&0x00000010)!=0; + bool warchive= (a&0x00000020)!=0; + ze->attr=FILE_ATTRIBUTE_NORMAL; + if (uisdir || wisdir) ze->attr |= FILE_ATTRIBUTE_DIRECTORY; + if (warchive) ze->attr|=FILE_ATTRIBUTE_ARCHIVE; + if (whidden) ze->attr|=FILE_ATTRIBUTE_HIDDEN; + if (!uwriteable||wreadonly) ze->attr|=FILE_ATTRIBUTE_READONLY; + if (wsystem) ze->attr|=FILE_ATTRIBUTE_SYSTEM; + ze->comp_size = ufi.compressed_size; + ze->unc_size = ufi.uncompressed_size; + // + WORD dostime = (WORD)(ufi.dosDate&0xFFFF); + WORD dosdate = (WORD)((ufi.dosDate>>16)&0xFFFF); + FILETIME ft; + DosDateTimeToFileTime(dosdate,dostime,&ft); + ze->atime=ft; ze->ctime=ft; ze->mtime=ft; + // the zip will always have at least that dostime. But if it also has + // an extra header, then we'll instead get the info from that. + unsigned int epos=0; + while (epos+4mtime = timet2filetime(mtime); + } + if (hasatime) + { time_t atime = *(__time32_t*)(extra+epos); epos+=4; + ze->atime = timet2filetime(atime); + } + if (hasctime) + { time_t ctime = *(__time32_t*)(extra+epos); + ze->ctime = timet2filetime(ctime); + } + break; + } + // + if (extra!=0) delete[] extra; + memcpy(&cze,ze,sizeof(ZIPENTRY)); czei=index; + return ZR_OK; +} + +ZRESULT TUnzip::Find(const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + int res = unzLocateFile(uf,name,ic?CASE_INSENSITIVE:CASE_SENSITIVE); + if (res!=UNZ_OK) + { + if (index!=0) + *index=-1; + if (ze!=NULL) + { + ZeroMemory(ze,sizeof(ZIPENTRY)); ze->index=-1; + } + return ZR_NOTFOUND; + } + if (currentfile!=-1) + unzCloseCurrentFile(uf); currentfile=-1; + int i = (int)uf->num_file; + if (index!=NULL) + *index=i; + if (ze!=NULL) + { + ZRESULT zres = Get(i,ze); + if (zres!=ZR_OK) + return zres; + } + return ZR_OK; +} + +void EnsureDirectory(const TCHAR *rootdir, const TCHAR *dir) +{ + if (dir==NULL || dir[0] == _T('\0')) + return; + const TCHAR *lastslash = dir, *c = lastslash; + while (*c != _T('\0')) + { + if (*c==_T('/') || *c==_T('\\')) + lastslash=c; + c++; + } + const TCHAR *name=lastslash; + if (lastslash!=dir) + { + TCHAR tmp[MAX_PATH]; + _tcsncpy(tmp, dir, lastslash-dir); + tmp[lastslash-dir] = _T('\0'); + EnsureDirectory(rootdir,tmp); + name++; + } + TCHAR cd[MAX_PATH]; + _tcscpy(cd,rootdir); + //_tcscat(cd,name); + _tcscat(cd,dir); //+++1.2 + CreateDirectory(cd,NULL); +} + +ZRESULT TUnzip::Unzip(int index,void *dst,unsigned int len,DWORD flags) +{ + if (flags!=ZIP_MEMORY && flags!=ZIP_FILENAME && flags!=ZIP_HANDLE) + return ZR_ARGS; + if (flags==ZIP_MEMORY) + { + if (index!=currentfile) + { + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (index<(int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_file0) + return ZR_MORE; + unzCloseCurrentFile(uf); + currentfile=-1; + if (res==0) + return ZR_OK; + else + return ZR_FLATE; + } + + // otherwise we're writing to a handle or a file + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index >= (int)uf->gi.number_entry) + return ZR_ARGS; + if (index < (int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_filelen) n=len-1; + strncpy(buf,msg,n); buf[n]=0; + return mlen; +} + + +typedef struct +{ DWORD flag; + TUnzip *unz; +} TUnzipHandleData; + +HZIP OpenZipU(void *z,unsigned int len,DWORD flags) +{ + TUnzip *unz = new TUnzip(); + lasterrorU = unz->Open(z,len,flags); + if (lasterrorU!=ZR_OK) + { + delete unz; + return 0; + } + TUnzipHandleData *han = new TUnzipHandleData; + han->flag=1; + han->unz=unz; + return (HZIP)han; +} + +ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Get(index,ze); + return lasterrorU; +} + +ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Get(index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + return lasterrorU; +} + +ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Find(name,ic,index,ze); + return lasterrorU; +} + +ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Find(name,ic,index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + + return lasterrorU; +} + +ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Unzip(index,dst,len,flags); + return lasterrorU; +} + +ZRESULT CloseZipU(HZIP hz) +{ if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} + TUnzip *unz = han->unz; + lasterrorU = unz->Close(); + delete unz; + delete han; + return lasterrorU; +} + +bool IsZipHandleU(HZIP hz) +{ if (hz==0) return true; + TUnzipHandleData *han = (TUnzipHandleData*)hz; + return (han->flag==1); +} + + diff --git a/Demos/bdwallpaper/helper/jsoncpp/autolink.h b/Demos/bdwallpaper/helper/jsoncpp/autolink.h new file mode 100644 index 00000000..37c9258e --- /dev/null +++ b/Demos/bdwallpaper/helper/jsoncpp/autolink.h @@ -0,0 +1,19 @@ +#ifndef JSON_AUTOLINK_H_INCLUDED +# define JSON_AUTOLINK_H_INCLUDED + +# include "config.h" + +# ifdef JSON_IN_CPPTL +# include +# endif + +# if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && !defined(JSON_IN_CPPTL) +# define CPPTL_AUTOLINK_NAME "json" +# undef CPPTL_AUTOLINK_DLL +# ifdef JSON_DLL +# define CPPTL_AUTOLINK_DLL +# endif +# include "autolink.h" +# endif + +#endif // JSON_AUTOLINK_H_INCLUDED diff --git a/Demos/bdwallpaper/helper/jsoncpp/config.h b/Demos/bdwallpaper/helper/jsoncpp/config.h new file mode 100644 index 00000000..5d334cbc --- /dev/null +++ b/Demos/bdwallpaper/helper/jsoncpp/config.h @@ -0,0 +1,43 @@ +#ifndef JSON_CONFIG_H_INCLUDED +# define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 +/// If defined, indicates that Json specific container should be used +/// (hash table & simple deque container with customizable allocator). +/// THIS FEATURE IS STILL EXPERIMENTAL! +//# define JSON_VALUE_USE_INTERNAL_MAP 1 +/// Force usage of standard new/malloc based allocator instead of memory pool based allocator. +/// The memory pools allocator used optimization (initializing Value and ValueInternalLink +/// as if it was a POD) that may cause some validation tool to report errors. +/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. +//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 + +/// If defined, indicates that Json use exception to report invalid type manipulation +/// instead of C assert macro. +# define JSON_USE_EXCEPTION 1 + +# ifdef JSON_IN_CPPTL +# include +# ifndef JSON_USE_CPPTL +# define JSON_USE_CPPTL 1 +# endif +# endif + +# ifdef JSON_IN_CPPTL +# define JSON_API CPPTL_API +# elif defined(JSON_DLL_BUILD) +# define JSON_API __declspec(dllexport) +# elif defined(JSON_DLL) +# define JSON_API __declspec(dllimport) +# else +# define JSON_API +# endif + +#endif // JSON_CONFIG_H_INCLUDED diff --git a/Demos/bdwallpaper/helper/jsoncpp/features.h b/Demos/bdwallpaper/helper/jsoncpp/features.h new file mode 100644 index 00000000..5a9adec1 --- /dev/null +++ b/Demos/bdwallpaper/helper/jsoncpp/features.h @@ -0,0 +1,42 @@ +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +# define CPPTL_JSON_FEATURES_H_INCLUDED + +# include "forwards.h" + +namespace Json { + + /** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ + class JSON_API Features + { + public: + /** \brief A configuration that allows all features and assumes all strings are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c false. + bool strictRoot_; + }; + +} // namespace Json + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED diff --git a/Demos/bdwallpaper/helper/jsoncpp/forwards.h b/Demos/bdwallpaper/helper/jsoncpp/forwards.h new file mode 100644 index 00000000..d0ce8300 --- /dev/null +++ b/Demos/bdwallpaper/helper/jsoncpp/forwards.h @@ -0,0 +1,39 @@ +#ifndef JSON_FORWARDS_H_INCLUDED +# define JSON_FORWARDS_H_INCLUDED + +# include "config.h" + +namespace Json { + + // writer.h + class FastWriter; + class StyledWriter; + + // reader.h + class Reader; + + // features.h + class Features; + + // value.h + typedef int Int; + typedef unsigned int UInt; + class StaticString; + class Path; + class PathArgument; + class Value; + class ValueIteratorBase; + class ValueIterator; + class ValueConstIterator; +#ifdef JSON_VALUE_USE_INTERNAL_MAP + class ValueAllocator; + class ValueMapAllocator; + class ValueInternalLink; + class ValueInternalArray; + class ValueInternalMap; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + +} // namespace Json + + +#endif // JSON_FORWARDS_H_INCLUDED diff --git a/Demos/bdwallpaper/helper/jsoncpp/json.h b/Demos/bdwallpaper/helper/jsoncpp/json.h new file mode 100644 index 00000000..c71ed65a --- /dev/null +++ b/Demos/bdwallpaper/helper/jsoncpp/json.h @@ -0,0 +1,10 @@ +#ifndef JSON_JSON_H_INCLUDED +# define JSON_JSON_H_INCLUDED + +# include "autolink.h" +# include "value.h" +# include "reader.h" +# include "writer.h" +# include "features.h" + +#endif // JSON_JSON_H_INCLUDED diff --git a/Demos/bdwallpaper/helper/jsoncpp/json_batchallocator.h b/Demos/bdwallpaper/helper/jsoncpp/json_batchallocator.h new file mode 100644 index 00000000..87ea5ed8 --- /dev/null +++ b/Demos/bdwallpaper/helper/jsoncpp/json_batchallocator.h @@ -0,0 +1,125 @@ +#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED +# define JSONCPP_BATCHALLOCATOR_H_INCLUDED + +# include +# include + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +namespace Json { + +/* Fast memory allocator. + * + * This memory allocator allocates memory for a batch of object (specified by + * the page size, the number of object in each page). + * + * It does not allow the destruction of a single object. All the allocated objects + * can be destroyed at once. The memory can be either released or reused for future + * allocation. + * + * The in-place new operator must be used to construct the object using the pointer + * returned by allocate. + */ +template +class BatchAllocator +{ +public: + typedef AllocatedType Type; + + BatchAllocator( unsigned int objectsPerPage = 255 ) + : freeHead_( 0 ) + , objectsPerPage_( objectsPerPage ) + { +// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); + assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space. + assert( objectsPerPage >= 16 ); + batches_ = allocateBatch( 0 ); // allocated a dummy page + currentBatch_ = batches_; + } + + ~BatchAllocator() + { + for ( BatchInfo *batch = batches_; batch; ) + { + BatchInfo *nextBatch = batch->next_; + free( batch ); + batch = nextBatch; + } + } + + /// allocate space for an array of objectPerAllocation object. + /// @warning it is the responsability of the caller to call objects constructors. + AllocatedType *allocate() + { + if ( freeHead_ ) // returns node from free list. + { + AllocatedType *object = freeHead_; + freeHead_ = *(AllocatedType **)object; + return object; + } + if ( currentBatch_->used_ == currentBatch_->end_ ) + { + currentBatch_ = currentBatch_->next_; + while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ ) + currentBatch_ = currentBatch_->next_; + + if ( !currentBatch_ ) // no free batch found, allocate a new one + { + currentBatch_ = allocateBatch( objectsPerPage_ ); + currentBatch_->next_ = batches_; // insert at the head of the list + batches_ = currentBatch_; + } + } + AllocatedType *allocated = currentBatch_->used_; + currentBatch_->used_ += objectPerAllocation; + return allocated; + } + + /// Release the object. + /// @warning it is the responsability of the caller to actually destruct the object. + void release( AllocatedType *object ) + { + assert( object != 0 ); + *(AllocatedType **)object = freeHead_; + freeHead_ = object; + } + +private: + struct BatchInfo + { + BatchInfo *next_; + AllocatedType *used_; + AllocatedType *end_; + AllocatedType buffer_[objectPerAllocation]; + }; + + // disabled copy constructor and assignement operator. + BatchAllocator( const BatchAllocator & ); + void operator =( const BatchAllocator &); + + static BatchInfo *allocateBatch( unsigned int objectsPerPage ) + { + const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation + + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; + BatchInfo *batch = static_cast( malloc( mallocSize ) ); + batch->next_ = 0; + batch->used_ = batch->buffer_; + batch->end_ = batch->buffer_ + objectsPerPage; + return batch; + } + + BatchInfo *batches_; + BatchInfo *currentBatch_; + /// Head of a single linked list within the allocated space of freeed object + AllocatedType *freeHead_; + unsigned int objectsPerPage_; +}; + + +} // namespace Json + +# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION + +#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED + diff --git a/Demos/bdwallpaper/helper/jsoncpp/json_reader.cpp b/Demos/bdwallpaper/helper/jsoncpp/json_reader.cpp new file mode 100644 index 00000000..3623e71d --- /dev/null +++ b/Demos/bdwallpaper/helper/jsoncpp/json_reader.cpp @@ -0,0 +1,885 @@ +#include "reader.h" +#include "value.h" +#include +#include +#include +#include +#include +#include + +#if _MSC_VER >= 1400 // VC++ 8.0 +#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. +#endif + +namespace Json { + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() + : allowComments_( true ) + , strictRoot_( false ) +{ +} + + +Features +Features::all() +{ + return Features(); +} + + +Features +Features::strictMode() +{ + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + + +static inline bool +in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) +{ + return c == c1 || c == c2 || c == c3 || c == c4; +} + +static inline bool +in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) +{ + return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; +} + + +static bool +containsNewLine( Reader::Location begin, + Reader::Location end ) +{ + for ( ;begin < end; ++begin ) + if ( *begin == '\n' || *begin == '\r' ) + return true; + return false; +} + +static std::string codePointToUTF8(unsigned int cp) +{ + std::string result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) + { + result.resize(1); + result[0] = static_cast(cp); + } + else if (cp <= 0x7FF) + { + result.resize(2); + result[1] = static_cast(0x80 | (0x3f & cp)); + result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } + else if (cp <= 0xFFFF) + { + result.resize(3); + result[2] = static_cast(0x80 | (0x3f & cp)); + result[1] = 0x80 | static_cast((0x3f & (cp >> 6))); + result[0] = 0xE0 | static_cast((0xf & (cp >> 12))); + } + else if (cp <= 0x10FFFF) + { + result.resize(4); + result[3] = static_cast(0x80 | (0x3f & cp)); + result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() + : features_( Features::all() ) +{ +} + + +Reader::Reader( const Features &features ) + : features_( features ) +{ +} + + +bool +Reader::parse( const std::string &document, + Value &root, + bool collectComments ) +{ + document_ = document; + const char *begin = document_.c_str(); + const char *end = begin + document_.length(); + return parse( begin, end, root, collectComments ); +} + + +bool +Reader::parse( std::istream& sin, + Value &root, + bool collectComments ) +{ + //std::istream_iterator begin(sin); + //std::istream_iterator end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since std::string is reference-counted, this at least does not + // create an extra copy. + std::string doc; + std::getline(sin, doc, (char)EOF); + return parse( doc, root, collectComments ); +} + +bool +Reader::parse( const char *beginDoc, const char *endDoc, + Value &root, + bool collectComments ) +{ + if ( !features_.allowComments_ ) + { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while ( !nodes_.empty() ) + nodes_.pop(); + nodes_.push( &root ); + + bool successful = readValue(); + Token token; + skipCommentTokens( token ); + if ( collectComments_ && !commentsBefore_.empty() ) + root.setComment( commentsBefore_, commentAfter ); + if ( features_.strictRoot_ ) + { + if ( !root.isArray() && !root.isObject() ) + { + // Set error location to start of doc, ideally should be first token found in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( "A valid JSON document must be either an array or an object value.", + token ); + return false; + } + } + return successful; +} + + +bool +Reader::readValue() +{ + Token token; + skipCommentTokens( token ); + bool successful = true; + + if ( collectComments_ && !commentsBefore_.empty() ) + { + currentValue().setComment( commentsBefore_, commentBefore ); + commentsBefore_ = ""; + } + + + switch ( token.type_ ) + { + case tokenObjectBegin: + successful = readObject( token ); + break; + case tokenArrayBegin: + successful = readArray( token ); + break; + case tokenNumber: + successful = decodeNumber( token ); + break; + case tokenString: + successful = decodeString( token ); + break; + case tokenTrue: + currentValue() = true; + break; + case tokenFalse: + currentValue() = false; + break; + case tokenNull: + currentValue() = Value(); + break; + default: + return addError( "Syntax error: value, object or array expected.", token ); + } + + if ( collectComments_ ) + { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + + +void +Reader::skipCommentTokens( Token &token ) +{ + if ( features_.allowComments_ ) + { + do + { + readToken( token ); + } + while ( token.type_ == tokenComment ); + } + else + { + readToken( token ); + } +} + + +bool +Reader::expectToken( TokenType type, Token &token, const char *message ) +{ + readToken( token ); + if ( token.type_ != type ) + return addError( message, token ); + return true; +} + + +bool +Reader::readToken( Token &token ) +{ + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch ( c ) + { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match( "rue", 3 ); + break; + case 'f': + token.type_ = tokenFalse; + ok = match( "alse", 4 ); + break; + case 'n': + token.type_ = tokenNull; + ok = match( "ull", 3 ); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if ( !ok ) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + + +void +Reader::skipSpaces() +{ + while ( current_ != end_ ) + { + Char c = *current_; + if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) + ++current_; + else + break; + } +} + + +bool +Reader::match( Location pattern, + int patternLength ) +{ + if ( end_ - current_ < patternLength ) + return false; + int index = patternLength; + while ( index-- ) + if ( current_[index] != pattern[index] ) + return false; + current_ += patternLength; + return true; +} + + +bool +Reader::readComment() +{ + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if ( c == '*' ) + successful = readCStyleComment(); + else if ( c == '/' ) + successful = readCppStyleComment(); + if ( !successful ) + return false; + + if ( collectComments_ ) + { + CommentPlacement placement = commentBefore; + if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) + { + if ( c != '*' || !containsNewLine( commentBegin, current_ ) ) + placement = commentAfterOnSameLine; + } + + addComment( commentBegin, current_, placement ); + } + return true; +} + + +void +Reader::addComment( Location begin, + Location end, + CommentPlacement placement ) +{ + assert( collectComments_ ); + if ( placement == commentAfterOnSameLine ) + { + assert( lastValue_ != 0 ); + lastValue_->setComment( std::string( begin, end ), placement ); + } + else + { + if ( !commentsBefore_.empty() ) + commentsBefore_ += "\n"; + commentsBefore_ += std::string( begin, end ); + } +} + + +bool +Reader::readCStyleComment() +{ + while ( current_ != end_ ) + { + Char c = getNextChar(); + if ( c == '*' && *current_ == '/' ) + break; + } + return getNextChar() == '/'; +} + + +bool +Reader::readCppStyleComment() +{ + while ( current_ != end_ ) + { + Char c = getNextChar(); + if ( c == '\r' || c == '\n' ) + break; + } + return true; +} + + +void +Reader::readNumber() +{ + while ( current_ != end_ ) + { + if ( !(*current_ >= '0' && *current_ <= '9') && + !in( *current_, '.', 'e', 'E', '+', '-' ) ) + break; + ++current_; + } +} + +bool +Reader::readString() +{ + Char c = 0; + while ( current_ != end_ ) + { + c = getNextChar(); + if ( c == '\\' ) + getNextChar(); + else if ( c == '"' ) + break; + } + return c == '"'; +} + + +bool +Reader::readObject( Token &tokenStart ) +{ + Token tokenName; + std::string name; + currentValue() = Value( objectValue ); + while ( readToken( tokenName ) ) + { + bool initialTokenOk = true; + while ( tokenName.type_ == tokenComment && initialTokenOk ) + initialTokenOk = readToken( tokenName ); + if ( !initialTokenOk ) + break; + if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object + return true; + if ( tokenName.type_ != tokenString ) + break; + + name = ""; + if ( !decodeString( tokenName, name ) ) + return recoverFromError( tokenObjectEnd ); + + Token colon; + if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) + { + return addErrorAndRecover( "Missing ':' after object member name", + colon, + tokenObjectEnd ); + } + Value &value = currentValue()[ name ]; + nodes_.push( &value ); + bool ok = readValue(); + nodes_.pop(); + if ( !ok ) // error already set + return recoverFromError( tokenObjectEnd ); + + Token comma; + if ( !readToken( comma ) + || ( comma.type_ != tokenObjectEnd && + comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment ) ) + { + return addErrorAndRecover( "Missing ',' or '}' in object declaration", + comma, + tokenObjectEnd ); + } + bool finalizeTokenOk = true; + while ( comma.type_ == tokenComment && + finalizeTokenOk ) + finalizeTokenOk = readToken( comma ); + if ( comma.type_ == tokenObjectEnd ) + return true; + } + return addErrorAndRecover( "Missing '}' or object member name", + tokenName, + tokenObjectEnd ); +} + + +bool +Reader::readArray( Token &tokenStart ) +{ + currentValue() = Value( arrayValue ); + skipSpaces(); + if ( *current_ == ']' ) // empty array + { + Token endArray; + readToken( endArray ); + return true; + } + int index = 0; + while ( true ) + { + Value &value = currentValue()[ index++ ]; + nodes_.push( &value ); + bool ok = readValue(); + nodes_.pop(); + if ( !ok ) // error already set + return recoverFromError( tokenArrayEnd ); + + Token token; + // Accept Comment after last item in the array. + ok = readToken( token ); + while ( token.type_ == tokenComment && ok ) + { + ok = readToken( token ); + } + bool badTokenType = ( token.type_ == tokenArraySeparator && + token.type_ == tokenArrayEnd ); + if ( !ok || badTokenType ) + { + return addErrorAndRecover( "Missing ',' or ']' in array declaration", + token, + tokenArrayEnd ); + } + if ( token.type_ == tokenArrayEnd ) + break; + } + return true; +} + + +bool +Reader::decodeNumber( Token &token ) +{ + bool isDouble = false; + for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) + { + isDouble = isDouble + || in( *inspect, '.', 'e', 'E', '+' ) + || ( *inspect == '-' && inspect != token.start_ ); + } + if ( isDouble ) + return decodeDouble( token ); + Location current = token.start_; + bool isNegative = *current == '-'; + if ( isNegative ) + ++current; + Value::UInt threshold = (isNegative ? Value::UInt(-Value::minInt) + : Value::maxUInt) / 10; + Value::UInt value = 0; + while ( current < token.end_ ) + { + Char c = *current++; + if ( c < '0' || c > '9' ) + return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); + if ( value >= threshold ) + return decodeDouble( token ); + value = value * 10 + Value::UInt(c - '0'); + } + if ( isNegative ) + currentValue() = -Value::Int( value ); + else if ( value <= Value::UInt(Value::maxInt) ) + currentValue() = Value::Int( value ); + else + currentValue() = value; + return true; +} + + +bool +Reader::decodeDouble( Token &token ) +{ + double value = 0; + const int bufferSize = 32; + int count; + int length = int(token.end_ - token.start_); + if ( length <= bufferSize ) + { + Char buffer[bufferSize]; + memcpy( buffer, token.start_, length ); + buffer[length] = 0; + count = sscanf( buffer, "%lf", &value ); + } + else + { + std::string buffer( token.start_, token.end_ ); + count = sscanf( buffer.c_str(), "%lf", &value ); + } + + if ( count != 1 ) + return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); + currentValue() = value; + return true; +} + + +bool +Reader::decodeString( Token &token ) +{ + std::string decoded; + if ( !decodeString( token, decoded ) ) + return false; + currentValue() = decoded; + return true; +} + + +bool +Reader::decodeString( Token &token, std::string &decoded ) +{ + decoded.reserve( token.end_ - token.start_ - 2 ); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while ( current != end ) + { + Char c = *current++; + if ( c == '"' ) + break; + else if ( c == '\\' ) + { + if ( current == end ) + return addError( "Empty escape sequence in string", token, current ); + Char escape = *current++; + switch ( escape ) + { + case '"': decoded += '"'; break; + case '/': decoded += '/'; break; + case '\\': decoded += '\\'; break; + case 'b': decoded += '\b'; break; + case 'f': decoded += '\f'; break; + case 'n': decoded += '\n'; break; + case 'r': decoded += '\r'; break; + case 't': decoded += '\t'; break; + case 'u': + { + unsigned int unicode; + if ( !decodeUnicodeCodePoint( token, current, end, unicode ) ) + return false; + decoded += codePointToUTF8(unicode); + } + break; + default: + return addError( "Bad escape sequence in string", token, current ); + } + } + else + { + decoded += c; + } + } + return true; +} + +bool +Reader::decodeUnicodeCodePoint( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ) +{ + + if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) ) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) + { + // surrogate pairs + if (end - current < 6) + return addError( "additional six characters expected to parse unicode surrogate pair.", token, current ); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++)== 'u') + { + if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair )) + { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } + else + return false; + } + else + return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); + } + return true; +} + +bool +Reader::decodeUnicodeEscapeSequence( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ) +{ + if ( end - current < 4 ) + return addError( "Bad unicode escape sequence in string: four digits expected.", token, current ); + unicode = 0; + for ( int index =0; index < 4; ++index ) + { + Char c = *current++; + unicode *= 16; + if ( c >= '0' && c <= '9' ) + unicode += c - '0'; + else if ( c >= 'a' && c <= 'f' ) + unicode += c - 'a' + 10; + else if ( c >= 'A' && c <= 'F' ) + unicode += c - 'A' + 10; + else + return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current ); + } + return true; +} + + +bool +Reader::addError( const std::string &message, + Token &token, + Location extra ) +{ + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back( info ); + return false; +} + + +bool +Reader::recoverFromError( TokenType skipUntilToken ) +{ + int errorCount = int(errors_.size()); + Token skip; + while ( true ) + { + if ( !readToken(skip) ) + errors_.resize( errorCount ); // discard errors caused by recovery + if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream ) + break; + } + errors_.resize( errorCount ); + return false; +} + + +bool +Reader::addErrorAndRecover( const std::string &message, + Token &token, + TokenType skipUntilToken ) +{ + addError( message, token ); + return recoverFromError( skipUntilToken ); +} + + +Value & +Reader::currentValue() +{ + return *(nodes_.top()); +} + + +Reader::Char +Reader::getNextChar() +{ + if ( current_ == end_ ) + return 0; + return *current_++; +} + + +void +Reader::getLocationLineAndColumn( Location location, + int &line, + int &column ) const +{ + Location current = begin_; + Location lastLineStart = current; + line = 0; + while ( current < location && current != end_ ) + { + Char c = *current++; + if ( c == '\r' ) + { + if ( *current == '\n' ) + ++current; + lastLineStart = current; + ++line; + } + else if ( c == '\n' ) + { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + + +std::string +Reader::getLocationLineAndColumn( Location location ) const +{ + int line, column; + getLocationLineAndColumn( location, line, column ); + char buffer[18+16+16+1]; + sprintf( buffer, "Line %d, Column %d", line, column ); + return buffer; +} + + +std::string +Reader::getFormatedErrorMessages() const +{ + std::string formattedMessage; + for ( Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError ) + { + const ErrorInfo &error = *itError; + formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if ( error.extra_ ) + formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; + } + return formattedMessage; +} + + +std::istream& operator>>( std::istream &sin, Value &root ) +{ + Json::Reader reader; + bool ok = reader.parse(sin, root, true); + //JSON_ASSERT( ok ); + if (!ok) throw std::runtime_error(reader.getFormatedErrorMessages()); + return sin; +} + + +} // namespace Json diff --git a/Demos/bdwallpaper/helper/jsoncpp/json_value.cpp b/Demos/bdwallpaper/helper/jsoncpp/json_value.cpp new file mode 100644 index 00000000..7e555be0 --- /dev/null +++ b/Demos/bdwallpaper/helper/jsoncpp/json_value.cpp @@ -0,0 +1,1718 @@ +#include +#include "value.h" +#include "writer.h" +#include +#include +#include +#include +#ifdef JSON_USE_CPPTL +# include +#endif +#include // size_t +#ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +# include "json_batchallocator.h" +#endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR + +#define JSON_ASSERT_UNREACHABLE assert( false ) +#define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw +#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) throw std::runtime_error( message ); + +namespace Json { + +const Value Value::null; +const Int Value::minInt = Int( ~(UInt(-1)/2) ); +const Int Value::maxInt = Int( UInt(-1)/2 ); +const UInt Value::maxUInt = UInt(-1); + +// A "safe" implementation of strdup. Allow null pointer to be passed. +// Also avoid warning on msvc80. +// +//inline char *safeStringDup( const char *czstring ) +//{ +// if ( czstring ) +// { +// const size_t length = (unsigned int)( strlen(czstring) + 1 ); +// char *newString = static_cast( malloc( length ) ); +// memcpy( newString, czstring, length ); +// return newString; +// } +// return 0; +//} +// +//inline char *safeStringDup( const std::string &str ) +//{ +// if ( !str.empty() ) +// { +// const size_t length = str.length(); +// char *newString = static_cast( malloc( length + 1 ) ); +// memcpy( newString, str.c_str(), length ); +// newString[length] = 0; +// return newString; +// } +// return 0; +//} + +ValueAllocator::~ValueAllocator() +{ +} + +class DefaultValueAllocator : public ValueAllocator +{ +public: + virtual ~DefaultValueAllocator() + { + } + + virtual char *makeMemberName( const char *memberName ) + { + return duplicateStringValue( memberName ); + } + + virtual void releaseMemberName( char *memberName ) + { + releaseStringValue( memberName ); + } + + virtual char *duplicateStringValue( const char *value, + unsigned int length = unknown ) + { + //@todo invesgate this old optimization + //if ( !value || value[0] == 0 ) + // return 0; + + if ( length == unknown ) + length = (unsigned int)strlen(value); + char *newString = static_cast( malloc( length + 1 ) ); + memcpy( newString, value, length ); + newString[length] = 0; + return newString; + } + + virtual void releaseStringValue( char *value ) + { + if ( value ) + free( value ); + } +}; + +static ValueAllocator *&valueAllocator() +{ + static DefaultValueAllocator defaultAllocator; + static ValueAllocator *valueAllocator = &defaultAllocator; + return valueAllocator; +} + +static struct DummyValueAllocatorInitializer { + DummyValueAllocatorInitializer() + { + valueAllocator(); // ensure valueAllocator() statics are initialized before main(). + } +} dummyValueAllocatorInitializer; + + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#ifdef JSON_VALUE_USE_INTERNAL_MAP +# include "json_internalarray.inl" +# include "json_internalmap.inl" +#endif // JSON_VALUE_USE_INTERNAL_MAP + +# include "json_valueiterator.inl" + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + + +Value::CommentInfo::CommentInfo() + : comment_( 0 ) +{ +} + +Value::CommentInfo::~CommentInfo() +{ + if ( comment_ ) + valueAllocator()->releaseStringValue( comment_ ); +} + + +void +Value::CommentInfo::setComment( const char *text ) +{ + if ( comment_ ) + valueAllocator()->releaseStringValue( comment_ ); + JSON_ASSERT( text ); + JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = valueAllocator()->duplicateStringValue( text ); +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +# ifndef JSON_VALUE_USE_INTERNAL_MAP + +// Notes: index_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString( int index ) + : cstr_( 0 ) + , index_( index ) +{ +} + +Value::CZString::CZString( const char *cstr, DuplicationPolicy allocate ) + : cstr_( allocate == duplicate ? valueAllocator()->makeMemberName(cstr) + : cstr ) + , index_( allocate ) +{ +} + +Value::CZString::CZString( const CZString &other ) +: cstr_( other.index_ != noDuplication && other.cstr_ != 0 + ? valueAllocator()->makeMemberName( other.cstr_ ) + : other.cstr_ ) + , index_( other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) + : other.index_ ) +{ +} + +Value::CZString::~CZString() +{ + if ( cstr_ && index_ == duplicate ) + valueAllocator()->releaseMemberName( const_cast( cstr_ ) ); +} + +void +Value::CZString::swap( CZString &other ) +{ + std::swap( cstr_, other.cstr_ ); + std::swap( index_, other.index_ ); +} + +Value::CZString & +Value::CZString::operator =( const CZString &other ) +{ + CZString temp( other ); + swap( temp ); + return *this; +} + +bool +Value::CZString::operator<( const CZString &other ) const +{ + if ( cstr_ ) + return strcmp( cstr_, other.cstr_ ) < 0; + return index_ < other.index_; +} + +bool +Value::CZString::operator==( const CZString &other ) const +{ + if ( cstr_ ) + return strcmp( cstr_, other.cstr_ ) == 0; + return index_ == other.index_; +} + + +int +Value::CZString::index() const +{ + return index_; +} + + +const char * +Value::CZString::c_str() const +{ + return cstr_; +} + +bool +Value::CZString::isStaticString() const +{ + return index_ == noDuplication; +} + +#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value( ValueType type ) + : type_( type ) + , allocated_( 0 ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + switch ( type ) + { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArray(); + break; + case objectValue: + value_.map_ = mapAllocator()->newMap(); + break; +#endif + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + + +Value::Value( Int value ) + : type_( intValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.int_ = value; +} + + +Value::Value( UInt value ) + : type_( uintValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.uint_ = value; +} + +Value::Value( double value ) + : type_( realValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.real_ = value; +} + +Value::Value( const char *value ) + : type_( stringValue ) + , allocated_( true ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = valueAllocator()->duplicateStringValue( value ); +} + + +Value::Value( const char *beginValue, + const char *endValue ) + : type_( stringValue ) + , allocated_( true ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = valueAllocator()->duplicateStringValue( beginValue, + UInt(endValue - beginValue) ); +} + + +Value::Value( const std::string &value ) + : type_( stringValue ) + , allocated_( true ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = valueAllocator()->duplicateStringValue( value.c_str(), + (unsigned int)value.length() ); + +} + +Value::Value( const StaticString &value ) + : type_( stringValue ) + , allocated_( false ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = const_cast( value.c_str() ); +} + + +# ifdef JSON_USE_CPPTL +Value::Value( const CppTL::ConstString &value ) + : type_( stringValue ) + , allocated_( true ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = valueAllocator()->duplicateStringValue( value, value.length() ); +} +# endif + +Value::Value( bool value ) + : type_( booleanValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.bool_ = value; +} + + +Value::Value( const Value &other ) + : type_( other.type_ ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if ( other.value_.string_ ) + { + value_.string_ = valueAllocator()->duplicateStringValue( other.value_.string_ ); + allocated_ = true; + } + else + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues( *other.value_.map_ ); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ ); + break; + case objectValue: + value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ ); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + if ( other.comments_ ) + { + comments_ = new CommentInfo[numberOfCommentPlacement]; + for ( int comment =0; comment < numberOfCommentPlacement; ++comment ) + { + const CommentInfo &otherComment = other.comments_[comment]; + if ( otherComment.comment_ ) + comments_[comment].setComment( otherComment.comment_ ); + } + } +} + + +Value::~Value() +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if ( allocated_ ) + valueAllocator()->releaseStringValue( value_.string_ ); + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + delete value_.map_; + break; +#else + case arrayValue: + arrayAllocator()->destructArray( value_.array_ ); + break; + case objectValue: + mapAllocator()->destructMap( value_.map_ ); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + + if ( comments_ ) + delete[] comments_; +} + +Value & +Value::operator=( const Value &other ) +{ + Value temp( other ); + swap( temp ); + return *this; +} + +void +Value::swap( Value &other ) +{ + ValueType temp = type_; + type_ = other.type_; + other.type_ = temp; + std::swap( value_, other.value_ ); + int temp2 = allocated_; + allocated_ = other.allocated_; + other.allocated_ = temp2; +} + +ValueType +Value::type() const +{ + return type_; +} + + +int +Value::compare( const Value &other ) +{ + /* + int typeDelta = other.type_ - type_; + switch ( type_ ) + { + case nullValue: + + return other.type_ == type_; + case intValue: + if ( other.type_.isNumeric() + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue, + break; + case arrayValue: + delete value_.array_; + break; + case objectValue: + delete value_.map_; + default: + JSON_ASSERT_UNREACHABLE; + } + */ + return 0; // unreachable +} + +bool +Value::operator <( const Value &other ) const +{ + int typeDelta = type_ - other.type_; + if ( typeDelta ) + return typeDelta < 0 ? true : false; + switch ( type_ ) + { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: + return ( value_.string_ == 0 && other.value_.string_ ) + || ( other.value_.string_ + && value_.string_ + && strcmp( value_.string_, other.value_.string_ ) < 0 ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + { + int delta = int( value_.map_->size() - other.value_.map_->size() ); + if ( delta ) + return delta < 0; + return (*value_.map_) < (*other.value_.map_); + } +#else + case arrayValue: + return value_.array_->compare( *(other.value_.array_) ) < 0; + case objectValue: + return value_.map_->compare( *(other.value_.map_) ) < 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable +} + +bool +Value::operator <=( const Value &other ) const +{ + return !(other > *this); +} + +bool +Value::operator >=( const Value &other ) const +{ + return !(*this < other); +} + +bool +Value::operator >( const Value &other ) const +{ + return other < *this; +} + +bool +Value::operator ==( const Value &other ) const +{ + //if ( type_ != other.type_ ) + // GCC 2.95.3 says: + // attempt to take address of bit-field structure member `Json::Value::type_' + // Beats me, but a temp solves the problem. + int temp = other.type_; + if ( type_ != temp ) + return false; + switch ( type_ ) + { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: + return ( value_.string_ == other.value_.string_ ) + || ( other.value_.string_ + && value_.string_ + && strcmp( value_.string_, other.value_.string_ ) == 0 ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() + && (*value_.map_) == (*other.value_.map_); +#else + case arrayValue: + return value_.array_->compare( *(other.value_.array_) ) == 0; + case objectValue: + return value_.map_->compare( *(other.value_.map_) ) == 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable +} + +bool +Value::operator !=( const Value &other ) const +{ + return !( *this == other ); +} + +const char * +Value::asCString() const +{ + JSON_ASSERT( type_ == stringValue ); + return value_.string_; +} + + +std::string +Value::asString() const +{ + switch ( type_ ) + { + case nullValue: + return ""; + case stringValue: + return value_.string_ ? value_.string_ : ""; + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + case uintValue: + case realValue: + case arrayValue: + case objectValue: + JSON_ASSERT_MESSAGE( false, "Type is not convertible to string" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return ""; // unreachable +} + +# ifdef JSON_USE_CPPTL +CppTL::ConstString +Value::asConstString() const +{ + return CppTL::ConstString( asString().c_str() ); +} +# endif + +Value::Int +Value::asInt() const +{ + switch ( type_ ) + { + case nullValue: + return 0; + case intValue: + return value_.int_; + case uintValue: + JSON_ASSERT_MESSAGE( value_.uint_ < (unsigned)maxInt, "integer out of signed integer range" ); + return value_.uint_; + case realValue: + JSON_ASSERT_MESSAGE( value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range" ); + return Int( value_.real_ ); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_ASSERT_MESSAGE( false, "Type is not convertible to int" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +Value::UInt +Value::asUInt() const +{ + switch ( type_ ) + { + case nullValue: + return 0; + case intValue: + JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to unsigned integer" ); + return value_.int_; + case uintValue: + return value_.uint_; + case realValue: + JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range" ); + return UInt( value_.real_ ); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_ASSERT_MESSAGE( false, "Type is not convertible to uint" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +double +Value::asDouble() const +{ + switch ( type_ ) + { + case nullValue: + return 0.0; + case intValue: + return value_.int_; + case uintValue: + return value_.uint_; + case realValue: + return value_.real_; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + case stringValue: + case arrayValue: + case objectValue: + JSON_ASSERT_MESSAGE( false, "Type is not convertible to double" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +bool +Value::asBool() const +{ + switch ( type_ ) + { + case nullValue: + return false; + case intValue: + case uintValue: + return value_.int_ != 0; + case realValue: + return value_.real_ != 0.0; + case booleanValue: + return value_.bool_; + case stringValue: + return value_.string_ && value_.string_[0] != 0; + case arrayValue: + case objectValue: + return value_.map_->size() != 0; + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable; +} + + +bool +Value::isConvertibleTo( ValueType other ) const +{ + switch ( type_ ) + { + case nullValue: + return true; + case intValue: + return ( other == nullValue && value_.int_ == 0 ) + || other == intValue + || ( other == uintValue && value_.int_ >= 0 ) + || other == realValue + || other == stringValue + || other == booleanValue; + case uintValue: + return ( other == nullValue && value_.uint_ == 0 ) + || ( other == intValue && value_.uint_ <= (unsigned)maxInt ) + || other == uintValue + || other == realValue + || other == stringValue + || other == booleanValue; + case realValue: + return ( other == nullValue && value_.real_ == 0.0 ) + || ( other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt ) + || ( other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt ) + || other == realValue + || other == stringValue + || other == booleanValue; + case booleanValue: + return ( other == nullValue && value_.bool_ == false ) + || other == intValue + || other == uintValue + || other == realValue + || other == stringValue + || other == booleanValue; + case stringValue: + return other == stringValue + || ( other == nullValue && (!value_.string_ || value_.string_[0] == 0) ); + case arrayValue: + return other == arrayValue + || ( other == nullValue && value_.map_->size() == 0 ); + case objectValue: + return other == objectValue + || ( other == nullValue && value_.map_->size() == 0 ); + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable; +} + + +/// Number of values in array or object +Value::UInt +Value::size() const +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: // size of the array is highest index + 1 + if ( !value_.map_->empty() ) + { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index()+1; + } + return 0; + case objectValue: + return Int( value_.map_->size() ); +#else + case arrayValue: + return Int( value_.array_->size() ); + case objectValue: + return Int( value_.map_->size() ); +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + + +bool +Value::empty() const +{ + if ( isNull() || isArray() || isObject() ) + return size() == 0u; + else + return false; +} + + +bool +Value::operator!() const +{ + return isNull(); +} + + +void +Value::clear() +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue || type_ == objectValue ); + + switch ( type_ ) + { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_->clear(); + break; +#else + case arrayValue: + value_.array_->clear(); + break; + case objectValue: + value_.map_->clear(); + break; +#endif + default: + break; + } +} + +void +Value::resize( UInt newSize ) +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + *this = Value( arrayValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + UInt oldSize = size(); + if ( newSize == 0 ) + clear(); + else if ( newSize > oldSize ) + (*this)[ newSize - 1 ]; + else + { + for ( UInt index = newSize; index < oldSize; ++index ) + value_.map_->erase( index ); + assert( size() == newSize ); + } +#else + value_.array_->resize( newSize ); +#endif +} + + +Value & +Value::operator[]( UInt index ) +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + *this = Value( arrayValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key( index ); + ObjectValues::iterator it = value_.map_->lower_bound( key ); + if ( it != value_.map_->end() && (*it).first == key ) + return (*it).second; + + ObjectValues::value_type defaultValue( key, null ); + it = value_.map_->insert( it, defaultValue ); + return (*it).second; +#else + return value_.array_->resolveReference( index ); +#endif +} + + +const Value & +Value::operator[]( UInt index ) const +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key( index ); + ObjectValues::const_iterator it = value_.map_->find( key ); + if ( it == value_.map_->end() ) + return null; + return (*it).second; +#else + Value *value = value_.array_->find( index ); + return value ? *value : null; +#endif +} + + +Value & +Value::operator[]( const char *key ) +{ + return resolveReference( key, false ); +} + + +Value & +Value::resolveReference( const char *key, + bool isStatic ) +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + *this = Value( objectValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, isStatic ? CZString::noDuplication + : CZString::duplicateOnCopy ); + ObjectValues::iterator it = value_.map_->lower_bound( actualKey ); + if ( it != value_.map_->end() && (*it).first == actualKey ) + return (*it).second; + + ObjectValues::value_type defaultValue( actualKey, null ); + it = value_.map_->insert( it, defaultValue ); + Value &value = (*it).second; + return value; +#else + return value_.map_->resolveReference( key, isStatic ); +#endif +} + + +Value +Value::get( UInt index, + const Value &defaultValue ) const +{ + const Value *value = &((*this)[index]); + return value == &null ? defaultValue : *value; +} + + +bool +Value::isValidIndex( UInt index ) const +{ + return index < size(); +} + + + +const Value & +Value::operator[]( const char *key ) const +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, CZString::noDuplication ); + ObjectValues::const_iterator it = value_.map_->find( actualKey ); + if ( it == value_.map_->end() ) + return null; + return (*it).second; +#else + const Value *value = value_.map_->find( key ); + return value ? *value : null; +#endif +} + + +Value & +Value::operator[]( const std::string &key ) +{ + return (*this)[ key.c_str() ]; +} + + +const Value & +Value::operator[]( const std::string &key ) const +{ + return (*this)[ key.c_str() ]; +} + +Value & +Value::operator[]( const StaticString &key ) +{ + return resolveReference( key, true ); +} + + +# ifdef JSON_USE_CPPTL +Value & +Value::operator[]( const CppTL::ConstString &key ) +{ + return (*this)[ key.c_str() ]; +} + + +const Value & +Value::operator[]( const CppTL::ConstString &key ) const +{ + return (*this)[ key.c_str() ]; +} +# endif + + +Value & +Value::append( const Value &value ) +{ + return (*this)[size()] = value; +} + + +Value +Value::get( const char *key, + const Value &defaultValue ) const +{ + const Value *value = &((*this)[key]); + return value == &null ? defaultValue : *value; +} + + +Value +Value::get( const std::string &key, + const Value &defaultValue ) const +{ + return get( key.c_str(), defaultValue ); +} + +Value +Value::removeMember( const char* key ) +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, CZString::noDuplication ); + ObjectValues::iterator it = value_.map_->find( actualKey ); + if ( it == value_.map_->end() ) + return null; + Value old(it->second); + value_.map_->erase(it); + return old; +#else + Value *value = value_.map_->find( key ); + if (value){ + Value old(*value); + value_.map_.remove( key ); + return old; + } else { + return null; + } +#endif +} + +Value +Value::removeMember( const std::string &key ) +{ + return removeMember( key.c_str() ); +} + +# ifdef JSON_USE_CPPTL +Value +Value::get( const CppTL::ConstString &key, + const Value &defaultValue ) const +{ + return get( key.c_str(), defaultValue ); +} +# endif + +bool +Value::isMember( const char *key ) const +{ + const Value *value = &((*this)[key]); + return value != &null; +} + + +bool +Value::isMember( const std::string &key ) const +{ + return isMember( key.c_str() ); +} + + +# ifdef JSON_USE_CPPTL +bool +Value::isMember( const CppTL::ConstString &key ) const +{ + return isMember( key.c_str() ); +} +#endif + +Value::Members +Value::getMemberNames() const +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return Value::Members(); + Members members; + members.reserve( value_.map_->size() ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for ( ; it != itEnd; ++it ) + members.push_back( std::string( (*it).first.c_str() ) ); +#else + ValueInternalMap::IteratorState it; + ValueInternalMap::IteratorState itEnd; + value_.map_->makeBeginIterator( it ); + value_.map_->makeEndIterator( itEnd ); + for ( ; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) ) + members.push_back( std::string( ValueInternalMap::key( it ) ) ); +#endif + return members; +} +// +//# ifdef JSON_USE_CPPTL +//EnumMemberNames +//Value::enumMemberNames() const +//{ +// if ( type_ == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +//EnumValues +//Value::enumValues() const +//{ +// if ( type_ == objectValue || type_ == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type() ); +// return EnumValues(); +//} +// +//# endif + + +bool +Value::isNull() const +{ + return type_ == nullValue; +} + + +bool +Value::isBool() const +{ + return type_ == booleanValue; +} + + +bool +Value::isInt() const +{ + return type_ == intValue; +} + + +bool +Value::isUInt() const +{ + return type_ == uintValue; +} + + +bool +Value::isIntegral() const +{ + return type_ == intValue + || type_ == uintValue + || type_ == booleanValue; +} + + +bool +Value::isDouble() const +{ + return type_ == realValue; +} + + +bool +Value::isNumeric() const +{ + return isIntegral() || isDouble(); +} + + +bool +Value::isString() const +{ + return type_ == stringValue; +} + + +bool +Value::isArray() const +{ + return type_ == nullValue || type_ == arrayValue; +} + + +bool +Value::isObject() const +{ + return type_ == nullValue || type_ == objectValue; +} + + +void +Value::setComment( const char *comment, + CommentPlacement placement ) +{ + if ( !comments_ ) + comments_ = new CommentInfo[numberOfCommentPlacement]; + comments_[placement].setComment( comment ); +} + + +void +Value::setComment( const std::string &comment, + CommentPlacement placement ) +{ + setComment( comment.c_str(), placement ); +} + + +bool +Value::hasComment( CommentPlacement placement ) const +{ + return comments_ != 0 && comments_[placement].comment_ != 0; +} + +std::string +Value::getComment( CommentPlacement placement ) const +{ + if ( hasComment(placement) ) + return comments_[placement].comment_; + return ""; +} + + +std::string +Value::toStyledString() const +{ + StyledWriter writer; + return writer.write( *this ); +} + + +Value::const_iterator +Value::begin() const +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator( it ); + return const_iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator( it ); + return const_iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return const_iterator( value_.map_->begin() ); + break; +#endif + default: + break; + } + return const_iterator(); +} + +Value::const_iterator +Value::end() const +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator( it ); + return const_iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator( it ); + return const_iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return const_iterator( value_.map_->end() ); + break; +#endif + default: + break; + } + return const_iterator(); +} + + +Value::iterator +Value::begin() +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator( it ); + return iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator( it ); + return iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return iterator( value_.map_->begin() ); + break; +#endif + default: + break; + } + return iterator(); +} + +Value::iterator +Value::end() +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator( it ); + return iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator( it ); + return iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return iterator( value_.map_->end() ); + break; +#endif + default: + break; + } + return iterator(); +} + + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() + : kind_( kindNone ) +{ +} + + +PathArgument::PathArgument( Value::UInt index ) + : index_( index ) + , kind_( kindIndex ) +{ +} + + +PathArgument::PathArgument( const char *key ) + : key_( key ) + , kind_( kindKey ) +{ +} + + +PathArgument::PathArgument( const std::string &key ) + : key_( key.c_str() ) + , kind_( kindKey ) +{ +} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path( const std::string &path, + const PathArgument &a1, + const PathArgument &a2, + const PathArgument &a3, + const PathArgument &a4, + const PathArgument &a5 ) +{ + InArgs in; + in.push_back( &a1 ); + in.push_back( &a2 ); + in.push_back( &a3 ); + in.push_back( &a4 ); + in.push_back( &a5 ); + makePath( path, in ); +} + + +void +Path::makePath( const std::string &path, + const InArgs &in ) +{ + const char *current = path.c_str(); + const char *end = current + path.length(); + InArgs::const_iterator itInArg = in.begin(); + while ( current != end ) + { + if ( *current == '[' ) + { + ++current; + if ( *current == '%' ) + addPathInArg( path, in, itInArg, PathArgument::kindIndex ); + else + { + Value::UInt index = 0; + for ( ; current != end && *current >= '0' && *current <= '9'; ++current ) + index = index * 10 + Value::UInt(*current - '0'); + args_.push_back( index ); + } + if ( current == end || *current++ != ']' ) + invalidPath( path, int(current - path.c_str()) ); + } + else if ( *current == '%' ) + { + addPathInArg( path, in, itInArg, PathArgument::kindKey ); + ++current; + } + else if ( *current == '.' ) + { + ++current; + } + else + { + const char *beginName = current; + while ( current != end && !strchr( "[.", *current ) ) + ++current; + args_.push_back( std::string( beginName, current ) ); + } + } +} + + +void +Path::addPathInArg( const std::string &path, + const InArgs &in, + InArgs::const_iterator &itInArg, + PathArgument::Kind kind ) +{ + if ( itInArg == in.end() ) + { + // Error: missing argument %d + } + else if ( (*itInArg)->kind_ != kind ) + { + // Error: bad argument type + } + else + { + args_.push_back( **itInArg ); + } +} + + +void +Path::invalidPath( const std::string &path, + int location ) +{ + // Error: invalid path. +} + + +const Value & +Path::resolve( const Value &root ) const +{ + const Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() || node->isValidIndex( arg.index_ ) ) + { + // Error: unable to resolve path (array value expected at position... + } + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + { + // Error: unable to resolve path (object value expected at position...) + } + node = &((*node)[arg.key_]); + if ( node == &Value::null ) + { + // Error: unable to resolve path (object has no member named '' at position...) + } + } + } + return *node; +} + + +Value +Path::resolve( const Value &root, + const Value &defaultValue ) const +{ + const Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() || node->isValidIndex( arg.index_ ) ) + return defaultValue; + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + return defaultValue; + node = &((*node)[arg.key_]); + if ( node == &Value::null ) + return defaultValue; + } + } + return *node; +} + + +Value & +Path::make( Value &root ) const +{ + Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() ) + { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + + +} // namespace Json diff --git a/Demos/bdwallpaper/helper/jsoncpp/json_valueiterator.inl b/Demos/bdwallpaper/helper/jsoncpp/json_valueiterator.inl new file mode 100644 index 00000000..736e260e --- /dev/null +++ b/Demos/bdwallpaper/helper/jsoncpp/json_valueiterator.inl @@ -0,0 +1,292 @@ +// included by json_value.cpp +// everything is within Json namespace + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() +#ifndef JSON_VALUE_USE_INTERNAL_MAP + : current_() + , isNull_( true ) +{ +} +#else + : isArray_( true ) + , isNull_( true ) +{ + iterator_.array_ = ValueInternalArray::IteratorState(); +} +#endif + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator ¤t ) + : current_( current ) + , isNull_( false ) +{ +} +#else +ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state ) + : isArray_( true ) +{ + iterator_.array_ = state; +} + + +ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state ) + : isArray_( false ) +{ + iterator_.map_ = state; +} +#endif + +Value & +ValueIteratorBase::deref() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + return current_->second; +#else + if ( isArray_ ) + return ValueInternalArray::dereference( iterator_.array_ ); + return ValueInternalMap::value( iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::increment() +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ++current_; +#else + if ( isArray_ ) + ValueInternalArray::increment( iterator_.array_ ); + ValueInternalMap::increment( iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::decrement() +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + --current_; +#else + if ( isArray_ ) + ValueInternalArray::decrement( iterator_.array_ ); + ValueInternalMap::decrement( iterator_.map_ ); +#endif +} + + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance( const SelfType &other ) const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP +# ifdef JSON_USE_CPPTL_SMALLMAP + return current_ - other.current_; +# else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if ( isNull_ && other.isNull_ ) + { + return 0; + } + + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it ) + { + ++myDistance; + } + return myDistance; +# endif +#else + if ( isArray_ ) + return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ ); + return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ ); +#endif +} + + +bool +ValueIteratorBase::isEqual( const SelfType &other ) const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + if ( isNull_ ) + { + return other.isNull_; + } + return current_ == other.current_; +#else + if ( isArray_ ) + return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ ); + return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::copy( const SelfType &other ) +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + current_ = other.current_; +#else + if ( isArray_ ) + iterator_.array_ = other.iterator_.array_; + iterator_.map_ = other.iterator_.map_; +#endif +} + + +Value +ValueIteratorBase::key() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if ( czstring.c_str() ) + { + if ( czstring.isStaticString() ) + return Value( StaticString( czstring.c_str() ) ); + return Value( czstring.c_str() ); + } + return Value( czstring.index() ); +#else + if ( isArray_ ) + return Value( ValueInternalArray::indexOf( iterator_.array_ ) ); + bool isStatic; + const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic ); + if ( isStatic ) + return Value( StaticString( memberName ) ); + return Value( memberName ); +#endif +} + + +UInt +ValueIteratorBase::index() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if ( !czstring.c_str() ) + return czstring.index(); + return Value::UInt( -1 ); +#else + if ( isArray_ ) + return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) ); + return Value::UInt( -1 ); +#endif +} + + +const char * +ValueIteratorBase::memberName() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const char *name = (*current_).first.c_str(); + return name ? name : ""; +#else + if ( !isArray_ ) + return ValueInternalMap::key( iterator_.map_ ); + return ""; +#endif +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() +{ +} + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator ¤t ) + : ValueIteratorBase( current ) +{ +} +#else +ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} + +ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} +#endif + +ValueConstIterator & +ValueConstIterator::operator =( const ValueIteratorBase &other ) +{ + copy( other ); + return *this; +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() +{ +} + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIterator::ValueIterator( const Value::ObjectValues::iterator ¤t ) + : ValueIteratorBase( current ) +{ +} +#else +ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} + +ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} +#endif + +ValueIterator::ValueIterator( const ValueConstIterator &other ) + : ValueIteratorBase( other ) +{ +} + +ValueIterator::ValueIterator( const ValueIterator &other ) + : ValueIteratorBase( other ) +{ +} + +ValueIterator & +ValueIterator::operator =( const SelfType &other ) +{ + copy( other ); + return *this; +} diff --git a/Demos/bdwallpaper/helper/jsoncpp/json_writer.cpp b/Demos/bdwallpaper/helper/jsoncpp/json_writer.cpp new file mode 100644 index 00000000..2a9f0dba --- /dev/null +++ b/Demos/bdwallpaper/helper/jsoncpp/json_writer.cpp @@ -0,0 +1,829 @@ +#include "writer.h" +#include +#include +#include +#include +#include +#include +#include + +#if _MSC_VER >= 1400 // VC++ 8.0 +#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. +#endif + +namespace Json { + +static bool isControlCharacter(char ch) +{ + return ch > 0 && ch <= 0x1F; +} + +static bool containsControlCharacter( const char* str ) +{ + while ( *str ) + { + if ( isControlCharacter( *(str++) ) ) + return true; + } + return false; +} +static void uintToString( unsigned int value, + char *¤t ) +{ + *--current = 0; + do + { + *--current = (value % 10) + '0'; + value /= 10; + } + while ( value != 0 ); +} + +std::string valueToString( Int value ) +{ + char buffer[32]; + char *current = buffer + sizeof(buffer); + bool isNegative = value < 0; + if ( isNegative ) + value = -value; + uintToString( UInt(value), current ); + if ( isNegative ) + *--current = '-'; + assert( current >= buffer ); + return current; +} + + +std::string valueToString( UInt value ) +{ + char buffer[32]; + char *current = buffer + sizeof(buffer); + uintToString( value, current ); + assert( current >= buffer ); + return current; +} + +std::string valueToString( double value ) +{ + char buffer[32]; +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. + sprintf_s(buffer, sizeof(buffer), "%#.16g", value); +#else + sprintf(buffer, "%#.16g", value); +#endif + char* ch = buffer + strlen(buffer) - 1; + if (*ch != '0') return buffer; // nothing to truncate, so save time + while(ch > buffer && *ch == '0'){ + --ch; + } + char* last_nonzero = ch; + while(ch >= buffer){ + switch(*ch){ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + --ch; + continue; + case '.': + // Truncate zeroes to save bytes in output, but keep one. + *(last_nonzero+2) = '\0'; + return buffer; + default: + return buffer; + } + } + return buffer; +} + + +std::string valueToString( bool value ) +{ + return value ? "true" : "false"; +} + +std::string valueToQuotedString( const char *value ) +{ + // Not sure how to handle unicode... + if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value )) + return std::string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + unsigned maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL + std::string result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + for (const char* c=value; *c != 0; ++c) + { + switch(*c) + { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + //case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something. + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } + else + { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() +{ +} + + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + : yamlCompatiblityEnabled_( false ) +{ +} + + +void +FastWriter::enableYAMLCompatibility() +{ + yamlCompatiblityEnabled_ = true; +} + + +std::string +FastWriter::write( const Value &root ) +{ + document_ = ""; + writeValue( root ); + document_ += "\n"; + return document_; +} + + +void +FastWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + document_ += "null"; + break; + case intValue: + document_ += valueToString( value.asInt() ); + break; + case uintValue: + document_ += valueToString( value.asUInt() ); + break; + case realValue: + document_ += valueToString( value.asDouble() ); + break; + case stringValue: + document_ += valueToQuotedString( value.asCString() ); + break; + case booleanValue: + document_ += valueToString( value.asBool() ); + break; + case arrayValue: + { + document_ += "["; + int size = value.size(); + for ( int index =0; index < size; ++index ) + { + if ( index > 0 ) + document_ += ","; + writeValue( value[index] ); + } + document_ += "]"; + } + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + document_ += "{"; + for ( Value::Members::iterator it = members.begin(); + it != members.end(); + ++it ) + { + const std::string &name = *it; + if ( it != members.begin() ) + document_ += ","; + document_ += valueToQuotedString( name.c_str() ); + document_ += yamlCompatiblityEnabled_ ? ": " + : ":"; + writeValue( value[name] ); + } + document_ += "}"; + } + break; + } +} + + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() + : rightMargin_( 74 ) + , indentSize_( 3 ) +{ +} + + +std::string +StyledWriter::write( const Value &root ) +{ + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue( root ); + writeValue( root ); + writeCommentAfterValueOnSameLine( root ); + document_ += "\n"; + return document_; +} + + +void +StyledWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + pushValue( "null" ); + break; + case intValue: + pushValue( valueToString( value.asInt() ) ); + break; + case uintValue: + pushValue( valueToString( value.asUInt() ) ); + break; + case realValue: + pushValue( valueToString( value.asDouble() ) ); + break; + case stringValue: + pushValue( valueToQuotedString( value.asCString() ) ); + break; + case booleanValue: + pushValue( valueToString( value.asBool() ) ); + break; + case arrayValue: + writeArrayValue( value); + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + if ( members.empty() ) + pushValue( "{}" ); + else + { + writeWithIndent( "{" ); + indent(); + Value::Members::iterator it = members.begin(); + while ( true ) + { + const std::string &name = *it; + const Value &childValue = value[name]; + writeCommentBeforeValue( childValue ); + writeWithIndent( valueToQuotedString( name.c_str() ) ); + document_ += " : "; + writeValue( childValue ); + if ( ++it == members.end() ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "}" ); + } + } + break; + } +} + + +void +StyledWriter::writeArrayValue( const Value &value ) +{ + unsigned size = value.size(); + if ( size == 0 ) + pushValue( "[]" ); + else + { + bool isArrayMultiLine = isMultineArray( value ); + if ( isArrayMultiLine ) + { + writeWithIndent( "[" ); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index =0; + while ( true ) + { + const Value &childValue = value[index]; + writeCommentBeforeValue( childValue ); + if ( hasChildValue ) + writeWithIndent( childValues_[index] ); + else + { + writeIndent(); + writeValue( childValue ); + } + if ( ++index == size ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "]" ); + } + else // output on a single line + { + assert( childValues_.size() == size ); + document_ += "[ "; + for ( unsigned index =0; index < size; ++index ) + { + if ( index > 0 ) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + + +bool +StyledWriter::isMultineArray( const Value &value ) +{ + int size = value.size(); + bool isMultiLine = size*3 >= rightMargin_ ; + childValues_.clear(); + for ( int index =0; index < size && !isMultiLine; ++index ) + { + const Value &childValue = value[index]; + isMultiLine = isMultiLine || + ( (childValue.isArray() || childValue.isObject()) && + childValue.size() > 0 ); + } + if ( !isMultiLine ) // check if line length > max line length + { + childValues_.reserve( size ); + addChildValues_ = true; + int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' + for ( int index =0; index < size && !isMultiLine; ++index ) + { + writeValue( value[index] ); + lineLength += int( childValues_[index].length() ); + isMultiLine = isMultiLine && hasCommentForValue( value[index] ); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + + +void +StyledWriter::pushValue( const std::string &value ) +{ + if ( addChildValues_ ) + childValues_.push_back( value ); + else + document_ += value; +} + + +void +StyledWriter::writeIndent() +{ + if ( !document_.empty() ) + { + char last = document_[document_.length()-1]; + if ( last == ' ' ) // already indented + return; + if ( last != '\n' ) // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + + +void +StyledWriter::writeWithIndent( const std::string &value ) +{ + writeIndent(); + document_ += value; +} + + +void +StyledWriter::indent() +{ + indentString_ += std::string( indentSize_, ' ' ); +} + + +void +StyledWriter::unindent() +{ + assert( int(indentString_.size()) >= indentSize_ ); + indentString_.resize( indentString_.size() - indentSize_ ); +} + + +void +StyledWriter::writeCommentBeforeValue( const Value &root ) +{ + if ( !root.hasComment( commentBefore ) ) + return; + document_ += normalizeEOL( root.getComment( commentBefore ) ); + document_ += "\n"; +} + + +void +StyledWriter::writeCommentAfterValueOnSameLine( const Value &root ) +{ + if ( root.hasComment( commentAfterOnSameLine ) ) + document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); + + if ( root.hasComment( commentAfter ) ) + { + document_ += "\n"; + document_ += normalizeEOL( root.getComment( commentAfter ) ); + document_ += "\n"; + } +} + + +bool +StyledWriter::hasCommentForValue( const Value &value ) +{ + return value.hasComment( commentBefore ) + || value.hasComment( commentAfterOnSameLine ) + || value.hasComment( commentAfter ); +} + + +std::string +StyledWriter::normalizeEOL( const std::string &text ) +{ + std::string normalized; + normalized.reserve( text.length() ); + const char *begin = text.c_str(); + const char *end = begin + text.length(); + const char *current = begin; + while ( current != end ) + { + char c = *current++; + if ( c == '\r' ) // mac or dos EOL + { + if ( *current == '\n' ) // convert dos EOL + ++current; + normalized += '\n'; + } + else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter( std::string indentation ) + : document_(NULL) + , rightMargin_( 74 ) + , indentation_( indentation ) +{ +} + + +void +StyledStreamWriter::write( std::ostream &out, const Value &root ) +{ + document_ = &out; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue( root ); + writeValue( root ); + writeCommentAfterValueOnSameLine( root ); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + + +void +StyledStreamWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + pushValue( "null" ); + break; + case intValue: + pushValue( valueToString( value.asInt() ) ); + break; + case uintValue: + pushValue( valueToString( value.asUInt() ) ); + break; + case realValue: + pushValue( valueToString( value.asDouble() ) ); + break; + case stringValue: + pushValue( valueToQuotedString( value.asCString() ) ); + break; + case booleanValue: + pushValue( valueToString( value.asBool() ) ); + break; + case arrayValue: + writeArrayValue( value); + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + if ( members.empty() ) + pushValue( "{}" ); + else + { + writeWithIndent( "{" ); + indent(); + Value::Members::iterator it = members.begin(); + while ( true ) + { + const std::string &name = *it; + const Value &childValue = value[name]; + writeCommentBeforeValue( childValue ); + writeWithIndent( valueToQuotedString( name.c_str() ) ); + *document_ << " : "; + writeValue( childValue ); + if ( ++it == members.end() ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "}" ); + } + } + break; + } +} + + +void +StyledStreamWriter::writeArrayValue( const Value &value ) +{ + unsigned size = value.size(); + if ( size == 0 ) + pushValue( "[]" ); + else + { + bool isArrayMultiLine = isMultineArray( value ); + if ( isArrayMultiLine ) + { + writeWithIndent( "[" ); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index =0; + while ( true ) + { + const Value &childValue = value[index]; + writeCommentBeforeValue( childValue ); + if ( hasChildValue ) + writeWithIndent( childValues_[index] ); + else + { + writeIndent(); + writeValue( childValue ); + } + if ( ++index == size ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "]" ); + } + else // output on a single line + { + assert( childValues_.size() == size ); + *document_ << "[ "; + for ( unsigned index =0; index < size; ++index ) + { + if ( index > 0 ) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + + +bool +StyledStreamWriter::isMultineArray( const Value &value ) +{ + int size = value.size(); + bool isMultiLine = size*3 >= rightMargin_ ; + childValues_.clear(); + for ( int index =0; index < size && !isMultiLine; ++index ) + { + const Value &childValue = value[index]; + isMultiLine = isMultiLine || + ( (childValue.isArray() || childValue.isObject()) && + childValue.size() > 0 ); + } + if ( !isMultiLine ) // check if line length > max line length + { + childValues_.reserve( size ); + addChildValues_ = true; + int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' + for ( int index =0; index < size && !isMultiLine; ++index ) + { + writeValue( value[index] ); + lineLength += int( childValues_[index].length() ); + isMultiLine = isMultiLine && hasCommentForValue( value[index] ); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + + +void +StyledStreamWriter::pushValue( const std::string &value ) +{ + if ( addChildValues_ ) + childValues_.push_back( value ); + else + *document_ << value; +} + + +void +StyledStreamWriter::writeIndent() +{ + /* + Some comments in this method would have been nice. ;-) + + if ( !document_.empty() ) + { + char last = document_[document_.length()-1]; + if ( last == ' ' ) // already indented + return; + if ( last != '\n' ) // Comments may add new-line + *document_ << '\n'; + } + */ + *document_ << '\n' << indentString_; +} + + +void +StyledStreamWriter::writeWithIndent( const std::string &value ) +{ + writeIndent(); + *document_ << value; +} + + +void +StyledStreamWriter::indent() +{ + indentString_ += indentation_; +} + + +void +StyledStreamWriter::unindent() +{ + assert( indentString_.size() >= indentation_.size() ); + indentString_.resize( indentString_.size() - indentation_.size() ); +} + + +void +StyledStreamWriter::writeCommentBeforeValue( const Value &root ) +{ + if ( !root.hasComment( commentBefore ) ) + return; + *document_ << normalizeEOL( root.getComment( commentBefore ) ); + *document_ << "\n"; +} + + +void +StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root ) +{ + if ( root.hasComment( commentAfterOnSameLine ) ) + *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); + + if ( root.hasComment( commentAfter ) ) + { + *document_ << "\n"; + *document_ << normalizeEOL( root.getComment( commentAfter ) ); + *document_ << "\n"; + } +} + + +bool +StyledStreamWriter::hasCommentForValue( const Value &value ) +{ + return value.hasComment( commentBefore ) + || value.hasComment( commentAfterOnSameLine ) + || value.hasComment( commentAfter ); +} + + +std::string +StyledStreamWriter::normalizeEOL( const std::string &text ) +{ + std::string normalized; + normalized.reserve( text.length() ); + const char *begin = text.c_str(); + const char *end = begin + text.length(); + const char *current = begin; + while ( current != end ) + { + char c = *current++; + if ( c == '\r' ) // mac or dos EOL + { + if ( *current == '\n' ) // convert dos EOL + ++current; + normalized += '\n'; + } + else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + + +std::ostream& operator<<( std::ostream &sout, const Value &root ) +{ + Json::StyledStreamWriter writer; + writer.write(sout, root); + return sout; +} + + +} // namespace Json diff --git a/Demos/bdwallpaper/helper/jsoncpp/reader.h b/Demos/bdwallpaper/helper/jsoncpp/reader.h new file mode 100644 index 00000000..ee1d6a24 --- /dev/null +++ b/Demos/bdwallpaper/helper/jsoncpp/reader.h @@ -0,0 +1,196 @@ +#ifndef CPPTL_JSON_READER_H_INCLUDED +# define CPPTL_JSON_READER_H_INCLUDED + +# include "features.h" +# include "value.h" +# include +# include +# include +# include + +namespace Json { + + /** \brief Unserialize a JSON document into a Value. + * + */ + class JSON_API Reader + { + public: + typedef char Char; + typedef const Char *Location; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader( const Features &features ); + + /** \brief Read a Value from a JSON document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them back during + * serialization, \c false to discard comments. + * This parameter is ignored if Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an error occurred. + */ + bool parse( const std::string &document, + Value &root, + bool collectComments = true ); + + /** \brief Read a Value from a JSON document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them back during + * serialization, \c false to discard comments. + * This parameter is ignored if Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an error occurred. + */ + bool parse( const char *beginDoc, const char *endDoc, + Value &root, + bool collectComments = true ); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse( std::istream &is, + Value &root, + bool collectComments = true ); + + /** \brief Returns a user friendly string that list errors in the parsed document. + * \return Formatted error message with the list of errors with their location in + * the parsed document. An empty string is returned if no error occurred + * during parsing. + */ + std::string getFormatedErrorMessages() const; + + private: + enum TokenType + { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token + { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo + { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool expectToken( TokenType type, Token &token, const char *message ); + bool readToken( Token &token ); + void skipSpaces(); + bool match( Location pattern, + int patternLength ); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject( Token &token ); + bool readArray( Token &token ); + bool decodeNumber( Token &token ); + bool decodeString( Token &token ); + bool decodeString( Token &token, std::string &decoded ); + bool decodeDouble( Token &token ); + bool decodeUnicodeCodePoint( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ); + bool decodeUnicodeEscapeSequence( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ); + bool addError( const std::string &message, + Token &token, + Location extra = 0 ); + bool recoverFromError( TokenType skipUntilToken ); + bool addErrorAndRecover( const std::string &message, + Token &token, + TokenType skipUntilToken ); + void skipUntilSpace(); + Value ¤tValue(); + Char getNextChar(); + void getLocationLineAndColumn( Location location, + int &line, + int &column ) const; + std::string getLocationLineAndColumn( Location location ) const; + void addComment( Location begin, + Location end, + CommentPlacement placement ); + void skipCommentTokens( Token &token ); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value *lastValue_; + std::string commentsBefore_; + Features features_; + bool collectComments_; + }; + + /** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() + */ + std::istream& operator>>( std::istream&, Value& ); + +} // namespace Json + +#endif // CPPTL_JSON_READER_H_INCLUDED diff --git a/Demos/bdwallpaper/helper/jsoncpp/value.h b/Demos/bdwallpaper/helper/jsoncpp/value.h new file mode 100644 index 00000000..58bfd88e --- /dev/null +++ b/Demos/bdwallpaper/helper/jsoncpp/value.h @@ -0,0 +1,1069 @@ +#ifndef CPPTL_JSON_H_INCLUDED +# define CPPTL_JSON_H_INCLUDED + +# include "forwards.h" +# include +# include + +# ifndef JSON_USE_CPPTL_SMALLMAP +# include +# else +# include +# endif +# ifdef JSON_USE_CPPTL +# include +# endif + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + + /** \brief Type of the value held by a Value object. + */ + enum ValueType + { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). + }; + + enum CommentPlacement + { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for root value) + numberOfCommentPlacement + }; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + + /** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + class JSON_API StaticString + { + public: + explicit StaticString( const char *czstring ) + : str_( czstring ) + { + } + + operator const char *() const + { + return str_; + } + + const char *c_str() const + { + return str_; + } + + private: + const char *str_; + }; + + /** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * values of an #objectValue or #arrayValue can be accessed using operator[]() methods. + * Non const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resize and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtanis default value in the case the required element + * does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + */ + class JSON_API Value + { + friend class ValueIteratorBase; +# ifdef JSON_VALUE_USE_INTERNAL_MAP + friend class ValueInternalLink; + friend class ValueInternalMap; +# endif + public: + typedef std::vector Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; + typedef UInt ArrayIndex; + + static const Value null; + static const Int minInt; + static const Int maxInt; + static const UInt maxUInt; + + private: +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION +# ifndef JSON_VALUE_USE_INTERNAL_MAP + class CZString + { + public: + enum DuplicationPolicy + { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString( int index ); + CZString( const char *cstr, DuplicationPolicy allocate ); + CZString( const CZString &other ); + ~CZString(); + CZString &operator =( const CZString &other ); + bool operator<( const CZString &other ) const; + bool operator==( const CZString &other ) const; + int index() const; + const char *c_str() const; + bool isStaticString() const; + private: + void swap( CZString &other ); + const char *cstr_; + int index_; + }; + + public: +# ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +# else + typedef CppTL::SmallMap ObjectValues; +# endif // ifndef JSON_USE_CPPTL_SMALLMAP +# endif // ifndef JSON_VALUE_USE_INTERNAL_MAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. + This is useful since clear() and resize() will not alter types. + + Examples: + \code + Json::Value null_value; // null + Json::Value arr_value(Json::arrayValue); // [] + Json::Value obj_value(Json::objectValue); // {} + \endcode + */ + Value( ValueType type = nullValue ); + Value( Int value ); + Value( UInt value ); + Value( double value ); + Value( const char *value ); + Value( const char *beginValue, const char *endValue ); + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * \endcode + */ + Value( const StaticString &value ); + Value( const std::string &value ); +# ifdef JSON_USE_CPPTL + Value( const CppTL::ConstString &value ); +# endif + Value( bool value ); + Value( const Value &other ); + ~Value(); + + Value &operator=( const Value &other ); + /// Swap values. + /// \note Currently, comments are intentionally not swapped, for + /// both logic and efficiency. + void swap( Value &other ); + + ValueType type() const; + + bool operator <( const Value &other ) const; + bool operator <=( const Value &other ) const; + bool operator >=( const Value &other ) const; + bool operator >( const Value &other ) const; + + bool operator ==( const Value &other ) const; + bool operator !=( const Value &other ) const; + + int compare( const Value &other ); + + const char *asCString() const; + std::string asString() const; +# ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +# endif + Int asInt() const; + UInt asUInt() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isUInt() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo( ValueType other ) const; + + /// Number of values in array or object + UInt size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize( UInt size ); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value &operator[]( UInt index ); + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value &operator[]( UInt index ) const; + /// If the array contains at least index+1 elements, returns the element value, + /// otherwise returns defaultValue. + Value get( UInt index, + const Value &defaultValue ) const; + /// Return true if index < size(). + bool isValidIndex( UInt index ) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value &append( const Value &value ); + + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const char *key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const char *key ) const; + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const std::string &key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const std::string &key ) const; + /** \brief Access an object value by name, create a null member if it does not exist. + + * If the object as no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value &operator[]( const StaticString &key ); +# ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const CppTL::ConstString &key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const CppTL::ConstString &key ) const; +# endif + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const char *key, + const Value &defaultValue ) const; + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const std::string &key, + const Value &defaultValue ) const; +# ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const CppTL::ConstString &key, + const Value &defaultValue ) const; +# endif + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + Value removeMember( const char* key ); + /// Same as removeMember(const char*) + Value removeMember( const std::string &key ); + + /// Return true if the object has a member named key. + bool isMember( const char *key ) const; + /// Return true if the object has a member named key. + bool isMember( const std::string &key ) const; +# ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember( const CppTL::ConstString &key ) const; +# endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + +//# ifdef JSON_USE_CPPTL +// EnumMemberNames enumMemberNames() const; +// EnumValues enumValues() const; +//# endif + + /// Comments must be //... or /* ... */ + void setComment( const char *comment, + CommentPlacement placement ); + /// Comments must be //... or /* ... */ + void setComment( const std::string &comment, + CommentPlacement placement ); + bool hasComment( CommentPlacement placement ) const; + /// Include delimiters and embedded newlines. + std::string getComment( CommentPlacement placement ) const; + + std::string toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + + private: + Value &resolveReference( const char *key, + bool isStatic ); + +# ifdef JSON_VALUE_USE_INTERNAL_MAP + inline bool isItemAvailable() const + { + return itemIsUsed_ == 0; + } + + inline void setItemUsed( bool isUsed = true ) + { + itemIsUsed_ = isUsed ? 1 : 0; + } + + inline bool isMemberNameStatic() const + { + return memberNameIsStatic_ == 0; + } + + inline void setMemberNameIsStatic( bool isStatic ) + { + memberNameIsStatic_ = isStatic ? 1 : 0; + } +# endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP + + private: + struct CommentInfo + { + CommentInfo(); + ~CommentInfo(); + + void setComment( const char *text ); + + char *comment_; + }; + + //struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder + { + Int int_; + UInt uint_; + double real_; + bool bool_; + char *string_; +# ifdef JSON_VALUE_USE_INTERNAL_MAP + ValueInternalArray *array_; + ValueInternalMap *map_; +#else + ObjectValues *map_; +# endif + } value_; + ValueType type_ : 8; + int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. +# ifdef JSON_VALUE_USE_INTERNAL_MAP + unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. + int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. +# endif + CommentInfo *comments_; + }; + + + /** \brief Experimental and untested: represents an element of the "path" to access a node. + */ + class PathArgument + { + public: + friend class Path; + + PathArgument(); + PathArgument( UInt index ); + PathArgument( const char *key ); + PathArgument( const std::string &key ); + + private: + enum Kind + { + kindNone = 0, + kindIndex, + kindKey + }; + std::string key_; + UInt index_; + Kind kind_; + }; + + /** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ + class Path + { + public: + Path( const std::string &path, + const PathArgument &a1 = PathArgument(), + const PathArgument &a2 = PathArgument(), + const PathArgument &a3 = PathArgument(), + const PathArgument &a4 = PathArgument(), + const PathArgument &a5 = PathArgument() ); + + const Value &resolve( const Value &root ) const; + Value resolve( const Value &root, + const Value &defaultValue ) const; + /// Creates the "path" to access the specified node and returns a reference on the node. + Value &make( Value &root ) const; + + private: + typedef std::vector InArgs; + typedef std::vector Args; + + void makePath( const std::string &path, + const InArgs &in ); + void addPathInArg( const std::string &path, + const InArgs &in, + InArgs::const_iterator &itInArg, + PathArgument::Kind kind ); + void invalidPath( const std::string &path, + int location ); + + Args args_; + }; + + /** \brief Experimental do not use: Allocator to customize member name and string value memory management done by Value. + * + * - makeMemberName() and releaseMemberName() are called to respectively duplicate and + * free an Json::objectValue member name. + * - duplicateStringValue() and releaseStringValue() are called similarly to + * duplicate and free a Json::stringValue value. + */ + class ValueAllocator + { + public: + enum { unknown = (unsigned)-1 }; + + virtual ~ValueAllocator(); + + virtual char *makeMemberName( const char *memberName ) = 0; + virtual void releaseMemberName( char *memberName ) = 0; + virtual char *duplicateStringValue( const char *value, + unsigned int length = unknown ) = 0; + virtual void releaseStringValue( char *value ) = 0; + }; + +#ifdef JSON_VALUE_USE_INTERNAL_MAP + /** \brief Allocator to customize Value internal map. + * Below is an example of a simple implementation (default implementation actually + * use memory pool for speed). + * \code + class DefaultValueMapAllocator : public ValueMapAllocator + { + public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + return new ValueInternalMap(); + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + return new ValueInternalMap( other ); + } + + virtual void destructMap( ValueInternalMap *map ) + { + delete map; + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + return new ValueInternalLink(); + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + delete link; + } + }; + * \endcode + */ + class JSON_API ValueMapAllocator + { + public: + virtual ~ValueMapAllocator(); + virtual ValueInternalMap *newMap() = 0; + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0; + virtual void destructMap( ValueInternalMap *map ) = 0; + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0; + virtual void releaseMapBuckets( ValueInternalLink *links ) = 0; + virtual ValueInternalLink *allocateMapLink() = 0; + virtual void releaseMapLink( ValueInternalLink *link ) = 0; + }; + + /** \brief ValueInternalMap hash-map bucket chain link (for internal use only). + * \internal previous_ & next_ allows for bidirectional traversal. + */ + class JSON_API ValueInternalLink + { + public: + enum { itemPerLink = 6 }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. + enum InternalFlags { + flagAvailable = 0, + flagUsed = 1 + }; + + ValueInternalLink(); + + ~ValueInternalLink(); + + Value items_[itemPerLink]; + char *keys_[itemPerLink]; + ValueInternalLink *previous_; + ValueInternalLink *next_; + }; + + + /** \brief A linked page based hash-table implementation used internally by Value. + * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked + * list in each bucket to handle collision. There is an addional twist in that + * each node of the collision linked list is a page containing a fixed amount of + * value. This provides a better compromise between memory usage and speed. + * + * Each bucket is made up of a chained list of ValueInternalLink. The last + * link of a given bucket can be found in the 'previous_' field of the following bucket. + * The last link of the last bucket is stored in tailLink_ as it has no following bucket. + * Only the last link of a bucket may contains 'available' item. The last link always + * contains at least one element unless is it the bucket one very first link. + */ + class JSON_API ValueInternalMap + { + friend class ValueIteratorBase; + friend class Value; + public: + typedef unsigned int HashKey; + typedef unsigned int BucketIndex; + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState + { + IteratorState() + : map_(0) + , link_(0) + , itemIndex_(0) + , bucketIndex_(0) + { + } + ValueInternalMap *map_; + ValueInternalLink *link_; + BucketIndex itemIndex_; + BucketIndex bucketIndex_; + }; +# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalMap(); + ValueInternalMap( const ValueInternalMap &other ); + ValueInternalMap &operator =( const ValueInternalMap &other ); + ~ValueInternalMap(); + + void swap( ValueInternalMap &other ); + + BucketIndex size() const; + + void clear(); + + bool reserveDelta( BucketIndex growth ); + + bool reserve( BucketIndex newItemCount ); + + const Value *find( const char *key ) const; + + Value *find( const char *key ); + + Value &resolveReference( const char *key, + bool isStatic ); + + void remove( const char *key ); + + void doActualRemove( ValueInternalLink *link, + BucketIndex index, + BucketIndex bucketIndex ); + + ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex ); + + Value &setNewItem( const char *key, + bool isStatic, + ValueInternalLink *link, + BucketIndex index ); + + Value &unsafeAdd( const char *key, + bool isStatic, + HashKey hashedKey ); + + HashKey hash( const char *key ) const; + + int compare( const ValueInternalMap &other ) const; + + private: + void makeBeginIterator( IteratorState &it ) const; + void makeEndIterator( IteratorState &it ) const; + static bool equals( const IteratorState &x, const IteratorState &other ); + static void increment( IteratorState &iterator ); + static void incrementBucket( IteratorState &iterator ); + static void decrement( IteratorState &iterator ); + static const char *key( const IteratorState &iterator ); + static const char *key( const IteratorState &iterator, bool &isStatic ); + static Value &value( const IteratorState &iterator ); + static int distance( const IteratorState &x, const IteratorState &y ); + + private: + ValueInternalLink *buckets_; + ValueInternalLink *tailLink_; + BucketIndex bucketsSize_; + BucketIndex itemCount_; + }; + + /** \brief A simplified deque implementation used internally by Value. + * \internal + * It is based on a list of fixed "page", each page contains a fixed number of items. + * Instead of using a linked-list, a array of pointer is used for fast item look-up. + * Look-up for an element is as follow: + * - compute page index: pageIndex = itemIndex / itemsPerPage + * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] + * + * Insertion is amortized constant time (only the array containing the index of pointers + * need to be reallocated when items are appended). + */ + class JSON_API ValueInternalArray + { + friend class Value; + friend class ValueIteratorBase; + public: + enum { itemsPerPage = 8 }; // should be a power of 2 for fast divide and modulo. + typedef Value::ArrayIndex ArrayIndex; + typedef unsigned int PageIndex; + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState // Must be a POD + { + IteratorState() + : array_(0) + , currentPageIndex_(0) + , currentItemIndex_(0) + { + } + ValueInternalArray *array_; + Value **currentPageIndex_; + unsigned int currentItemIndex_; + }; +# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalArray(); + ValueInternalArray( const ValueInternalArray &other ); + ValueInternalArray &operator =( const ValueInternalArray &other ); + ~ValueInternalArray(); + void swap( ValueInternalArray &other ); + + void clear(); + void resize( ArrayIndex newSize ); + + Value &resolveReference( ArrayIndex index ); + + Value *find( ArrayIndex index ) const; + + ArrayIndex size() const; + + int compare( const ValueInternalArray &other ) const; + + private: + static bool equals( const IteratorState &x, const IteratorState &other ); + static void increment( IteratorState &iterator ); + static void decrement( IteratorState &iterator ); + static Value &dereference( const IteratorState &iterator ); + static Value &unsafeDereference( const IteratorState &iterator ); + static int distance( const IteratorState &x, const IteratorState &y ); + static ArrayIndex indexOf( const IteratorState &iterator ); + void makeBeginIterator( IteratorState &it ) const; + void makeEndIterator( IteratorState &it ) const; + void makeIterator( IteratorState &it, ArrayIndex index ) const; + + void makeIndexValid( ArrayIndex index ); + + Value **pages_; + ArrayIndex size_; + PageIndex pageCount_; + }; + + /** \brief Experimental: do not use. Allocator to customize Value internal array. + * Below is an example of a simple implementation (actual implementation use + * memory pool). + \code +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() + { + } + + virtual ValueInternalArray *newArray() + { + return new ValueInternalArray(); + } + + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) + { + return new ValueInternalArray( other ); + } + + virtual void destruct( ValueInternalArray *array ) + { + delete array; + } + + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) + { + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast( newIndexes ); + } + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) + { + if ( indexes ) + free( indexes ); + } + + virtual Value *allocateArrayPage() + { + return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); + } + + virtual void releaseArrayPage( Value *value ) + { + if ( value ) + free( value ); + } +}; + \endcode + */ + class JSON_API ValueArrayAllocator + { + public: + virtual ~ValueArrayAllocator(); + virtual ValueInternalArray *newArray() = 0; + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0; + virtual void destructArray( ValueInternalArray *array ) = 0; + /** \brief Reallocate array page index. + * Reallocates an array of pointer on each page. + * \param indexes [input] pointer on the current index. May be \c NULL. + * [output] pointer on the new index of at least + * \a minNewIndexCount pages. + * \param indexCount [input] current number of pages in the index. + * [output] number of page the reallocated index can handle. + * \b MUST be >= \a minNewIndexCount. + * \param minNewIndexCount Minimum number of page the new index must be able to + * handle. + */ + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) = 0; + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) = 0; + virtual Value *allocateArrayPage() = 0; + virtual void releaseArrayPage( Value *value ) = 0; + }; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + + + /** \brief base class for Value iterators. + * + */ + class ValueIteratorBase + { + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + ValueIteratorBase(); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIteratorBase( const Value::ObjectValues::iterator ¤t ); +#else + ValueIteratorBase( const ValueInternalArray::IteratorState &state ); + ValueIteratorBase( const ValueInternalMap::IteratorState &state ); +#endif + + bool operator ==( const SelfType &other ) const + { + return isEqual( other ); + } + + bool operator !=( const SelfType &other ) const + { + return !isEqual( other ); + } + + difference_type operator -( const SelfType &other ) const + { + return computeDistance( other ); + } + + /// Return either the index or the member name of the referenced value as a Value. + Value key() const; + + /// Return the index of the referenced Value. -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value. "" if it is not an objectValue. + const char *memberName() const; + + protected: + Value &deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance( const SelfType &other ) const; + + bool isEqual( const SelfType &other ) const; + + void copy( const SelfType &other ); + + private: +#ifndef JSON_VALUE_USE_INTERNAL_MAP + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; +#else + union + { + ValueInternalArray::IteratorState array_; + ValueInternalMap::IteratorState map_; + } iterator_; + bool isArray_; +#endif + }; + + /** \brief const iterator for object and array value. + * + */ + class ValueConstIterator : public ValueIteratorBase + { + friend class Value; + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef const Value &reference; + typedef const Value *pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + private: + /*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueConstIterator( const Value::ObjectValues::iterator ¤t ); +#else + ValueConstIterator( const ValueInternalArray::IteratorState &state ); + ValueConstIterator( const ValueInternalMap::IteratorState &state ); +#endif + public: + SelfType &operator =( const ValueIteratorBase &other ); + + SelfType operator++( int ) + { + SelfType temp( *this ); + ++*this; + return temp; + } + + SelfType operator--( int ) + { + SelfType temp( *this ); + --*this; + return temp; + } + + SelfType &operator--() + { + decrement(); + return *this; + } + + SelfType &operator++() + { + increment(); + return *this; + } + + reference operator *() const + { + return deref(); + } + }; + + + /** \brief Iterator for object and array value. + */ + class ValueIterator : public ValueIteratorBase + { + friend class Value; + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef Value &reference; + typedef Value *pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + ValueIterator( const ValueConstIterator &other ); + ValueIterator( const ValueIterator &other ); + private: + /*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIterator( const Value::ObjectValues::iterator ¤t ); +#else + ValueIterator( const ValueInternalArray::IteratorState &state ); + ValueIterator( const ValueInternalMap::IteratorState &state ); +#endif + public: + + SelfType &operator =( const SelfType &other ); + + SelfType operator++( int ) + { + SelfType temp( *this ); + ++*this; + return temp; + } + + SelfType operator--( int ) + { + SelfType temp( *this ); + --*this; + return temp; + } + + SelfType &operator--() + { + decrement(); + return *this; + } + + SelfType &operator++() + { + increment(); + return *this; + } + + reference operator *() const + { + return deref(); + } + }; + + +} // namespace Json + + +#endif // CPPTL_JSON_H_INCLUDED diff --git a/Demos/bdwallpaper/helper/jsoncpp/writer.h b/Demos/bdwallpaper/helper/jsoncpp/writer.h new file mode 100644 index 00000000..5f4b83be --- /dev/null +++ b/Demos/bdwallpaper/helper/jsoncpp/writer.h @@ -0,0 +1,174 @@ +#ifndef JSON_WRITER_H_INCLUDED +# define JSON_WRITER_H_INCLUDED + +# include "value.h" +# include +# include +# include + +namespace Json { + + class Value; + + /** \brief Abstract class for writers. + */ + class JSON_API Writer + { + public: + virtual ~Writer(); + + virtual std::string write( const Value &root ) = 0; + }; + + /** \brief Outputs a Value in JSON format without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + */ + class JSON_API FastWriter : public Writer + { + public: + FastWriter(); + virtual ~FastWriter(){} + + void enableYAMLCompatibility(); + + public: // overridden from Writer + virtual std::string write( const Value &root ); + + private: + void writeValue( const Value &value ); + + std::string document_; + bool yamlCompatiblityEnabled_; + }; + + /** \brief Writes a Value in JSON format in a human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value types, + * and all the values fit on one lines, then print the array on a single line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their #CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + */ + class JSON_API StyledWriter: public Writer + { + public: + StyledWriter(); + virtual ~StyledWriter(){} + + public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + virtual std::string write( const Value &root ); + + private: + void writeValue( const Value &value ); + void writeArrayValue( const Value &value ); + bool isMultineArray( const Value &value ); + void pushValue( const std::string &value ); + void writeIndent(); + void writeWithIndent( const std::string &value ); + void indent(); + void unindent(); + void writeCommentBeforeValue( const Value &root ); + void writeCommentAfterValueOnSameLine( const Value &root ); + bool hasCommentForValue( const Value &value ); + static std::string normalizeEOL( const std::string &text ); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string document_; + std::string indentString_; + int rightMargin_; + int indentSize_; + bool addChildValues_; + }; + + /** \brief Writes a Value in JSON format in a human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value types, + * and all the values fit on one lines, then print the array on a single line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + */ + class JSON_API StyledStreamWriter + { + public: + StyledStreamWriter( std::string indentation="\t" ); + ~StyledStreamWriter(){} + + public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not return a value. + */ + void write( std::ostream &out, const Value &root ); + + private: + void writeValue( const Value &value ); + void writeArrayValue( const Value &value ); + bool isMultineArray( const Value &value ); + void pushValue( const std::string &value ); + void writeIndent(); + void writeWithIndent( const std::string &value ); + void indent(); + void unindent(); + void writeCommentBeforeValue( const Value &root ); + void writeCommentAfterValueOnSameLine( const Value &root ); + bool hasCommentForValue( const Value &value ); + static std::string normalizeEOL( const std::string &text ); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_; + }; + + std::string JSON_API valueToString( Int value ); + std::string JSON_API valueToString( UInt value ); + std::string JSON_API valueToString( double value ); + std::string JSON_API valueToString( bool value ); + std::string JSON_API valueToQuotedString( const char *value ); + + /// \brief Output using the StyledStreamWriter. + /// \see Json::operator>>() + std::ostream& operator<<( std::ostream&, const Value &root ); + +} // namespace Json + + + +#endif // JSON_WRITER_H_INCLUDED diff --git a/Demos/bdwallpaper/helper/stb_image.c b/Demos/bdwallpaper/helper/stb_image.c new file mode 100644 index 00000000..ebb11195 --- /dev/null +++ b/Demos/bdwallpaper/helper/stb_image.c @@ -0,0 +1,4677 @@ +#define STBI_NO_STDIO +#define STBI_NO_WRITE +#define STBI_NO_HDR + +/* stbi-1.33 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c + when you control the images you're loading + no warranty implied; use at your own risk + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline (no JPEG progressive) + PNG 8-bit only + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - overridable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) + + Latest revisions: + 1.33 (2011-07-14) minor fixes suggested by Dave Moore + 1.32 (2011-07-13) info support for all filetypes (SpartanJ) + 1.31 (2011-06-19) a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) added ability to load files via io callbacks (Ben Wenger) + 1.29 (2010-08-16) various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) cast-to-uint8 to fix warnings (Laurent Gomila) + allow trailing 0s at end of image data (Laurent Gomila) + 1.26 (2010-07-24) fix bug in file buffering for PNG reported by SpartanJ + + See end of file for full revision history. + + TODO: + stbi_info support for BMP,PSD,HDR,PIC + + + ============================ Contributors ========================= + + Image formats Optimizations & bugfixes + Sean Barrett (jpeg, png, bmp) Fabian "ryg" Giesen + Nicolas Schulz (hdr, psd) + Jonathan Dummer (tga) Bug fixes & warning fixes + Jean-Marc Lienher (gif) Marc LeBlanc + Tom Seddon (pic) Christpher Lloyd + Thatcher Ulrich (psd) Dave Moore + Won Chun + the Horde3D community + Extensions, features Janez Zemva + Jetro Lauha (stbi_info) Jonathan Blow + James "moose2000" Brown (iPhone PNG) Laurent Gomila + Ben "Disch" Wenger (io callbacks) Aruelien Pocheville + Martin "SpartanJ" Golini Ryamond Barbiero + David Woo + + + If your name should be here but isn't, let Sean know. + +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// To get a header file for this, either cut and paste the header, +// or create stb_image.h, #define STBI_HEADER_FILE_ONLY, and +// then include stb_image.c from it. + +//// begin header file //////////////////////////////////////////////////// +// +// Limitations: +// - no jpeg progressive support +// - non-HDR formats support 8-bit samples only (jpeg, png) +// - no delayed line count (jpeg) -- IJG doesn't support either +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to easily see if it's opaque. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB; nominally they +// would silently load as BGR, except the existing code should have just +// failed on such iPhone PNGs. But you can disable this conversion by +// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through. +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). + + +#ifndef STBI_NO_STDIO + +#if defined(_MSC_VER) && _MSC_VER >= 0x1400 +#define _CRT_SECURE_NO_WARNINGS // suppress bogus warnings about fopen() +#endif + +#include +#endif + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,unsigned n); // skip the next 'n' bytes + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +extern stbi_uc *stbi_load_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_HDR + extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif + + extern float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + extern void stbi_hdr_to_ldr_gamma(float gamma); + extern void stbi_hdr_to_ldr_scale(float scale); + + extern void stbi_ldr_to_hdr_gamma(float gamma); + extern void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_HDR + +// stbi_is_hdr is always defined +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename); +extern int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +extern const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +extern void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +extern int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +extern void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +extern void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + + +// ZLIB client - used by PNG, available for other purposes + +extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +// define faster low-level operations (typically SIMD support) +#ifdef STBI_SIMD +typedef void (*stbi_idct_8x8)(stbi_uc *out, int out_stride, short data[64], unsigned short *dequantize); +// compute an integer IDCT on "input" +// input[x] = data[x] * dequantize[x] +// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' +// CLAMP results to 0..255 +typedef void (*stbi_YCbCr_to_RGB_run)(stbi_uc *output, stbi_uc const *y, stbi_uc const *cb, stbi_uc const *cr, int count, int step); +// compute a conversion from YCbCr to RGB +// 'count' pixels +// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B +// y: Y input channel +// cb: Cb input channel; scale/biased to be 0..255 +// cr: Cr input channel; scale/biased to be 0..255 + +extern void stbi_install_idct(stbi_idct_8x8 func); +extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); +#endif // STBI_SIMD + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifndef STBI_HEADER_FILE_ONLY + +#ifndef STBI_NO_HDR +#include // ldexp +#include // strcmp, strtok +#endif + +#ifndef STBI_NO_STDIO +#include +#endif +#include +#include +#include +#include + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +// implementation: +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; +typedef unsigned int uint; + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(uint32)==4 ? 1 : -1]; + +#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE) +#define STBI_NO_WRITE +#endif + +#define STBI_NOTUSED(v) (void)sizeof(v) + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +/////////////////////////////////////////////// +// +// stbi struct and start_xxx functions + +// stbi structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + uint8 buffer_start[128]; + + uint8 *img_buffer, *img_buffer_end; + uint8 *img_buffer_original; +} stbi; + + +static void refill_buffer(stbi *s); + +// initialize a memory-decode context +static void start_mem(stbi *s, uint8 const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (uint8 *) buffer; + s->img_buffer_end = (uint8 *) buffer+len; +} + +// initialize a callback-based context +static void start_callbacks(stbi *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + refill_buffer(s); +} + +#ifndef STBI_NO_STDIO + +static int stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stdio_skip(void *user, unsigned n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi_stdio_callbacks = +{ + stdio_read, + stdio_skip, + stdio_eof, +}; + +static void start_file(stbi *s, FILE *f) +{ + start_callbacks(s, &stbi_stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi_rewind(stbi *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; +} + +static int stbi_jpeg_test(stbi *s); +static stbi_uc *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp); +static int stbi_png_test(stbi *s); +static stbi_uc *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_png_info(stbi *s, int *x, int *y, int *comp); +static int stbi_bmp_test(stbi *s); +static stbi_uc *stbi_bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_test(stbi *s); +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_info(stbi *s, int *x, int *y, int *comp); +static int stbi_psd_test(stbi *s); +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_hdr_test(stbi *s); +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_pic_test(stbi *s); +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_test(stbi *s); +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp); + + +// this is not threadsafe +static const char *failure_reason; + +const char *stbi_failure_reason(void) +{ + return failure_reason; +} + +static int e(const char *str) +{ + failure_reason = str; + return 0; +} + +// e - error +// epf - error returning pointer to float +// epuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define e(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define e(x,y) e(y) +#else + #define e(x,y) e(x) +#endif + +#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) +#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) + +void stbi_image_free(void *retval_from_stbi_load) +{ + free(retval_from_stbi_load); +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static unsigned char *stbi_load_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + if (stbi_jpeg_test(s)) return stbi_jpeg_load(s,x,y,comp,req_comp); + if (stbi_png_test(s)) return stbi_png_load(s,x,y,comp,req_comp); + if (stbi_bmp_test(s)) return stbi_bmp_load(s,x,y,comp,req_comp); + if (stbi_gif_test(s)) return stbi_gif_load(s,x,y,comp,req_comp); + if (stbi_psd_test(s)) return stbi_psd_load(s,x,y,comp,req_comp); + if (stbi_pic_test(s)) return stbi_pic_load(s,x,y,comp,req_comp); + + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) { + float *hdr = stbi_hdr_load(s, x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + // test tga last because it's a crappy test! + if (stbi_tga_test(s)) + return stbi_tga_load(s,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + unsigned char *result; + if (!f) return epuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_load_main(&s,x,y,comp,req_comp); +} +#endif //!STBI_NO_STDIO + +unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_HDR + +float *stbi_loadf_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) + return stbi_hdr_load(s,x,y,comp,req_comp); + #endif + data = stbi_load_main(s, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} + +float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + float *result; + if (!f) return epf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_HDR + +// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is +// defined, for API simplicity; if STBI_NO_HDR is defined, it always +// reports false! + +int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi s; + start_mem(&s,buffer,len); + return stbi_hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename) +{ + FILE *f = fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +extern int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi s; + start_file(&s,f); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} + +#ifndef STBI_NO_HDR +static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; +static float l2h_gamma=2.2f, l2h_scale=1.0f; + +void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } +void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } + +void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } +void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + SCAN_load=0, + SCAN_type, + SCAN_header +}; + +static void refill_buffer(stbi *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_end-1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static int get8(stbi *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int at_eof(stbi *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +stbi_inline static uint8 get8u(stbi *s) +{ + return (uint8) get8(s); +} + +static void skip(stbi *s, int n) +{ + if (s->io.read) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int getn(stbi *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int get16(stbi *s) +{ + int z = get8(s); + return (z << 8) + get8(s); +} + +static uint32 get32(stbi *s) +{ + uint32 z = get16(s); + return (z << 16) + get16(s); +} + +static int get16le(stbi *s) +{ + int z = get8(s); + return z + (get8(s) << 8); +} + +static uint32 get32le(stbi *s) +{ + uint32 z = get16le(s); + return z + (get16le(s) << 16); +} + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static uint8 compute_y(int r, int g, int b) +{ + return (uint8) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, uint x, uint y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + assert(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) malloc(req_comp * x * y); + if (good == NULL) { + free(data); + return epuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: assert(0); + } + #undef CASE + } + + free(data); + return good; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) malloc(x * y * comp * sizeof(float)); + if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + free(data); + return output; +} + +#define float2int(x) ((int) (x)) +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) malloc(x * y * comp); + if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + } + free(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) +// +// simple implementation +// - channel subsampling of at most 2 in each dimension +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - uses a lot of intermediate memory, could cache poorly +// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 +// stb_jpeg: 1.34 seconds (MSVC6, default release build) +// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) +// IJL11.dll: 1.08 seconds (compiled by intel) +// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) +// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + uint8 fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + uint16 code[256]; + uint8 values[256]; + uint8 size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} huffman; + +typedef struct +{ + #ifdef STBI_SIMD + unsigned short dequant2[4][64]; + #endif + stbi *s; + huffman huff_dc[4]; + huffman huff_ac[4]; + uint8 dequant[4][64]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + uint8 *data; + void *raw_data; + uint8 *linebuf; + } img_comp[4]; + + uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int scan_n, order[4]; + int restart_interval, todo; +} jpeg; + +static int build_huffman(huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (uint8) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (uint16) (code++); + if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (uint8) i; + } + } + } + return 1; +} + +static void grow_buffer_unsafe(jpeg *j) +{ + do { + int b = j->nomore ? 0 : get8(j->s); + if (b == 0xff) { + int c = get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int decode(jpeg *j, huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & bmask[k]) + h->delta[k]; + assert((((j->code_buffer) >> (32 - h->size[c])) & bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +stbi_inline static int extend_receive(jpeg *j, int n) +{ + unsigned int m = 1 << (n-1); + unsigned int k; + if (j->code_bits < n) grow_buffer_unsafe(j); + + #if 1 + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~bmask[n]; + k &= bmask[n]; + j->code_bits -= n; + #else + k = (j->code_buffer >> (32 - n)) & bmask[n]; + j->code_bits -= n; + j->code_buffer <<= n; + #endif + // the following test is probably a random branch that won't + // predict well. I tried to table accelerate it but failed. + // maybe it's compiling as a conditional move? + if (k < m) + return (-1 << n) + k + 1; + else + return k; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static uint8 dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int b) +{ + int diff,dc,k; + int t = decode(j, hdc); + if (t < 0) return e("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) dc; + + // decode AC components, see JPEG spec + k = 1; + do { + int r,s; + int rs = decode(j, hac); + if (rs < 0) return e("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + data[dezigzag[k++]] = (short) extend_receive(j,s); + } + } while (k < 64); + return 1; +} + +// take a -128..127 value and clamp it and convert to 0..255 +stbi_inline static uint8 clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (uint8) x; +} + +#define f2f(x) (int) (((x) * 4096 + 0.5)) +#define fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * f2f(0.5411961f); \ + t2 = p1 + p3*f2f(-1.847759065f); \ + t3 = p1 + p2*f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = fsh(p2+p3); \ + t1 = fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*f2f( 1.175875602f); \ + t0 = t0*f2f( 0.298631336f); \ + t1 = t1*f2f( 2.053119869f); \ + t2 = t2*f2f( 3.072711026f); \ + t3 = t3*f2f( 1.501321110f); \ + p1 = p5 + p1*f2f(-0.899976223f); \ + p2 = p5 + p2*f2f(-2.562915447f); \ + p3 = p3*f2f(-1.961570560f); \ + p4 = p4*f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +#ifdef STBI_SIMD +typedef unsigned short stbi_dequantize_t; +#else +typedef uint8 stbi_dequantize_t; +#endif + +// .344 seconds on 3*anemones.jpg +static void idct_block(uint8 *out, int out_stride, short data[64], stbi_dequantize_t *dequantize) +{ + int i,val[64],*v=val; + stbi_dequantize_t *dq = dequantize; + uint8 *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SIMD +static stbi_idct_8x8 stbi_idct_installed = idct_block; + +void stbi_install_idct(stbi_idct_8x8 func) +{ + stbi_idct_installed = func; +} +#endif + +#define MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static uint8 get_marker(jpeg *j) +{ + uint8 x; + if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; } + x = get8u(j->s); + if (x != 0xff) return MARKER_none; + while (x == 0xff) + x = get8u(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, reset the entropy decoder and +// the dc prediction +static void reset(jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int parse_entropy_coded_data(jpeg *z) +{ + reset(z); + if (z->scan_n == 1) { + int i,j; + #ifdef STBI_SIMD + __declspec(align(16)) + #endif + short data[64]; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } else { // interleaved! + int i,j,k,x,y; + short data[64]; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } + return 1; +} + +static int process_marker(jpeg *z, int m) +{ + int L; + switch (m) { + case MARKER_none: // no marker found + return e("expected marker","Corrupt JPEG"); + + case 0xC2: // SOF - progressive + return e("progressive jpeg","JPEG format not supported (progressive)"); + + case 0xDD: // DRI - specify restart interval + if (get16(z->s) != 4) return e("bad DRI len","Corrupt JPEG"); + z->restart_interval = get16(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = get16(z->s)-2; + while (L > 0) { + int q = get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return e("bad DQT type","Corrupt JPEG"); + if (t > 3) return e("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][dezigzag[i]] = get8u(z->s); + #ifdef STBI_SIMD + for (i=0; i < 64; ++i) + z->dequant2[t][i] = z->dequant[t][i]; + #endif + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = get16(z->s)-2; + while (L > 0) { + uint8 *v; + int sizes[16],i,m=0; + int q = get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = get8(z->s); + m += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < m; ++i) + v[i] = get8u(z->s); + L -= m; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + skip(z->s, get16(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int process_scan_header(jpeg *z) +{ + int i; + int Ls = get16(z->s); + z->scan_n = get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return e("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = get8(z->s), which; + int q = get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + get8(z->s); // should be 63, but might be 0 + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + + return 1; +} + +static int process_frame_header(jpeg *z, int scan) +{ + stbi *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG + p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = get16(s); if (s->img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = get16(s); if (s->img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires + c = get8(s); + if (c != 3 && c != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return e("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return e("bad component ID","Corrupt JPEG"); + q = get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); + z->img_comp[i].tq = get8(s); if (z->img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); + } + + if (scan != SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + free(z->img_comp[i].raw_data); + z->img_comp[i].data = NULL; + } + return e("outofmem", "Out of memory"); + } + // align blocks for installable-idct using mmx/sse + z->img_comp[i].data = (uint8*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define DNL(x) ((x) == 0xdc) +#define SOI(x) ((x) == 0xd8) +#define EOI(x) ((x) == 0xd9) +#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) +#define SOS(x) ((x) == 0xda) + +static int decode_jpeg_header(jpeg *z, int scan) +{ + int m; + z->marker = MARKER_none; // initialize cached marker to empty + m = get_marker(z); + if (!SOI(m)) return e("no SOI","Corrupt JPEG"); + if (scan == SCAN_type) return 1; + m = get_marker(z); + while (!SOF(m)) { + if (!process_marker(z,m)) return 0; + m = get_marker(z); + while (m == MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (at_eof(z->s)) return e("no SOF", "Corrupt JPEG"); + m = get_marker(z); + } + } + if (!process_frame_header(z, scan)) return 0; + return 1; +} + +static int decode_jpeg_image(jpeg *j) +{ + int m; + j->restart_interval = 0; + if (!decode_jpeg_header(j, SCAN_load)) return 0; + m = get_marker(j); + while (!EOI(m)) { + if (SOS(m)) { + if (!process_scan_header(j)) return 0; + if (!parse_entropy_coded_data(j)) return 0; + if (j->marker == MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!at_eof(j->s)) { + int x = get8(j->s); + if (x == 255) { + j->marker = get8u(j->s); + break; + } else if (x != 0) { + return 0; + } + } + // if we reach eof without hitting a marker, get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!process_marker(j, m)) return 0; + } + m = get_marker(j); + } + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1, + int w, int hs); + +#define div4(x) ((uint8) ((x) >> 2)) + +static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + uint8 *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = div4(n+input[i-1]); + out[i*2+1] = div4(n+input[i+1]); + } + out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define div16(x) ((uint8) ((x) >> 4)) + +static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = div16(3*t0 + t1 + 8); + out[i*2 ] = div16(3*t1 + t0 + 8); + } + out[w*2-1] = div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + in_far = in_far; + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) + +// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) +// VC6 without processor=Pro is generating multiple LEAs per multiply! +static void YCbCr_to_RGB_row(uint8 *out, const uint8 *y, const uint8 *pcb, const uint8 *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (uint8)r; + out[1] = (uint8)g; + out[2] = (uint8)b; + out[3] = 255; + out += step; + } +} + +#ifdef STBI_SIMD +static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; + +void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) +{ + stbi_YCbCr_installed = func; +} +#endif + + +// clean up the temporary component buffers +static void cleanup_jpeg(jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].data) { + free(j->img_comp[i].raw_data); + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].linebuf) { + free(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + uint8 *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi_resample; + +static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + // validate req_comp + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + z->s->img_n = 0; + + // load a jpeg image from whichever source + if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + uint i,j; + uint8 *output; + uint8 *coutput[4]; + + stbi_resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (uint8 *) malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; + else r->resample = resample_row_generic; + } + + // can't error after this so, this is safe + output = (uint8 *) malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + uint8 *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + uint8 *y = coutput[0]; + if (z->s->img_n == 3) { + #ifdef STBI_SIMD + stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s.img_x, n); + #else + YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s->img_x, n); + #endif + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + uint8 *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + jpeg j; + j.s = s; + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +static int stbi_jpeg_test(stbi *s) +{ + int r; + jpeg j; + j.s = s; + r = decode_jpeg_header(&j, SCAN_type); + stbi_rewind(s); + return r; +} + +static int stbi_jpeg_info_raw(jpeg *j, int *x, int *y, int *comp) +{ + if (!decode_jpeg_header(j, SCAN_header)) { + stbi_rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp) +{ + jpeg j; + j.s = s; + return stbi_jpeg_info_raw(&j, x, y, comp); +} + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define ZFAST_BITS 9 // accelerate all cases in default tables +#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + uint16 fast[1 << ZFAST_BITS]; + uint16 firstcode[16]; + int maxcode[17]; + uint16 firstsymbol[16]; + uint8 size[288]; + uint16 value[288]; +} zhuffman; + +stbi_inline static int bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int bit_reverse(int v, int bits) +{ + assert(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return bitreverse16(v) >> (16-bits); +} + +static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 255, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + assert(sizes[i] <= (1 << i)); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (uint16) code; + z->firstsymbol[i] = (uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + z->size[c] = (uint8)s; + z->value[c] = (uint16)i; + if (s <= ZFAST_BITS) { + int k = bit_reverse(next_code[s],s); + while (k < (1 << ZFAST_BITS)) { + z->fast[k] = (uint16) c; + k += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + uint8 *zbuffer, *zbuffer_end; + int num_bits; + uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + zhuffman z_length, z_distance; +} zbuf; + +stbi_inline static int zget8(zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void fill_bits(zbuf *z) +{ + do { + assert(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int zreceive(zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +stbi_inline static int zhuffman_decode(zbuf *a, zhuffman *z) +{ + int b,s,k; + if (a->num_bits < 16) fill_bits(a); + b = z->fast[a->code_buffer & ZFAST_MASK]; + if (b < 0xffff) { + s = z->size[b]; + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; + } + + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = bit_reverse(a->code_buffer, 16); + for (s=ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + assert(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +static int expand(zbuf *z, int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + if (!z->z_expandable) return e("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) realloc(z->zout_start, limit); + if (q == NULL) return e("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int length_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int length_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int dist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int parse_huffman_block(zbuf *a) +{ + for(;;) { + int z = zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes + if (a->zout >= a->zout_end) if (!expand(a, 1)) return 0; + *a->zout++ = (char) z; + } else { + uint8 *p; + int len,dist; + if (z == 256) return 1; + z -= 257; + len = length_base[z]; + if (length_extra[z]) len += zreceive(a, length_extra[z]); + z = zhuffman_decode(a, &a->z_distance); + if (z < 0) return e("bad huffman code","Corrupt PNG"); + dist = dist_base[z]; + if (dist_extra[z]) dist += zreceive(a, dist_extra[z]); + if (a->zout - a->zout_start < dist) return e("bad dist","Corrupt PNG"); + if (a->zout + len > a->zout_end) if (!expand(a, len)) return 0; + p = (uint8 *) (a->zout - dist); + while (len--) + *a->zout++ = *p++; + } + } +} + +static int compute_huffman_codes(zbuf *a) +{ + static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + zhuffman z_codelength; + uint8 lencodes[286+32+137];//padding for maximum single op + uint8 codelength_sizes[19]; + int i,n; + + int hlit = zreceive(a,5) + 257; + int hdist = zreceive(a,5) + 1; + int hclen = zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (uint8) s; + } + if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = zhuffman_decode(a, &z_codelength); + assert(c >= 0 && c < 19); + if (c < 16) + lencodes[n++] = (uint8) c; + else if (c == 16) { + c = zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + assert(c == 18); + c = zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); + if (!zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int parse_uncompressed_block(zbuf *a) +{ + uint8 header[4]; + int len,nlen,k; + if (a->num_bits & 7) + zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (uint8) (a->code_buffer & 255); // wtf this warns? + a->code_buffer >>= 8; + a->num_bits -= 8; + } + assert(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = (uint8) zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return e("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!expand(a, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int parse_zlib_header(zbuf *a) +{ + int cmf = zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = zget8(a); + if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static uint8 default_length[288], default_distance[32]; +static void init_defaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) default_length[i] = 8; + for ( ; i <= 255; ++i) default_length[i] = 9; + for ( ; i <= 279; ++i) default_length[i] = 7; + for ( ; i <= 287; ++i) default_length[i] = 8; + + for (i=0; i <= 31; ++i) default_distance[i] = 5; +} + +int stbi_png_partial; // a quick hack to only allow decoding some of a PNG... I should implement real streaming support instead +static int parse_zlib(zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = zreceive(a,1); + type = zreceive(a,2); + if (type == 0) { + if (!parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!default_distance[31]) init_defaults(); + if (!zbuild_huffman(&a->z_length , default_length , 288)) return 0; + if (!zbuild_huffman(&a->z_distance, default_distance, 32)) return 0; + } else { + if (!compute_huffman_codes(a)) return 0; + } + if (!parse_huffman_block(a)) return 0; + } + if (stbi_png_partial && a->zout - a->zout_start > 65536) + break; + } while (!final); + return 1; +} + +static int do_zlib(zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return parse_zlib(a, parse_header); +} + +char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer+len; + if (do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + + +typedef struct +{ + uint32 length; + uint32 type; +} chunk; + +#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static chunk get_chunk_header(stbi *s) +{ + chunk c; + c.length = get32(s); + c.type = get32(s); + return c; +} + +static int check_png_header(stbi *s) +{ + static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (get8u(s) != png_sig[i]) return e("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi *s; + uint8 *idata, *expanded, *out; +} png; + + +enum { + F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, + F_avg_first, F_paeth_first +}; + +static uint8 first_row_filter[5] = +{ + F_none, F_sub, F_none, F_avg_first, F_paeth_first +}; + +static int paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +// create the png data from post-deflated data +static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, uint32 x, uint32 y) +{ + stbi *s = a->s; + uint32 i,j,stride = x*out_n; + int k; + int img_n = s->img_n; // copy it into a local for later + assert(out_n == s->img_n || out_n == s->img_n+1); + if (stbi_png_partial) y = 1; + a->out = (uint8 *) malloc(x * y * out_n); + if (!a->out) return e("outofmem", "Out of memory"); + if (!stbi_png_partial) { + if (s->img_x == x && s->img_y == y) { + if (raw_len != (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } + } + for (j=0; j < y; ++j) { + uint8 *cur = a->out + stride*j; + uint8 *prior = cur - stride; + int filter = *raw++; + if (filter > 4) return e("invalid filter","Corrupt PNG"); + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + // handle first pixel explicitly + for (k=0; k < img_n; ++k) { + switch (filter) { + case F_none : cur[k] = raw[k]; break; + case F_sub : cur[k] = raw[k]; break; + case F_up : cur[k] = raw[k] + prior[k]; break; + case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; + case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break; + case F_avg_first : cur[k] = raw[k]; break; + case F_paeth_first: cur[k] = raw[k]; break; + } + } + if (img_n != out_n) cur[img_n] = 255; + raw += img_n; + cur += out_n; + prior += out_n; + // this is a little gross, so that we don't switch per-pixel or per-component + if (img_n == out_n) { + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; + } + #undef CASE + } else { + assert(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + return 1; +} + +static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n, int interlaced) +{ + uint8 *final; + int p; + int save; + if (!interlaced) + return create_png_image_raw(a, raw, raw_len, out_n, a->s->img_x, a->s->img_y); + save = stbi_png_partial; + stbi_png_partial = 0; + + // de-interlacing + final = (uint8 *) malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + if (!create_png_image_raw(a, raw, raw_len, out_n, x, y)) { + free(final); + return 0; + } + for (j=0; j < y; ++j) + for (i=0; i < x; ++i) + memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*out_n, + a->out + (j*x+i)*out_n, out_n); + free(a->out); + raw += (x*out_n+1)*y; + raw_len -= (x*out_n+1)*y; + } + } + a->out = final; + + stbi_png_partial = save; + return 1; +} + +static int compute_transparency(png *z, uint8 tc[3], int out_n) +{ + stbi *s = z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + assert(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int expand_palette(png *a, uint8 *palette, int len, int pal_img_n) +{ + uint32 i, pixel_count = a->s->img_x * a->s->img_y; + uint8 *p, *temp_out, *orig = a->out; + + p = (uint8 *) malloc(pixel_count * pal_img_n); + if (p == NULL) return e("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + free(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi_unpremultiply_on_load = 0; +static int stbi_de_iphone_flag = 0; + +void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi_unpremultiply_on_load = flag_true_if_should_unpremultiply; +} +void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi_de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi_de_iphone(png *z) +{ + stbi *s = z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + assert(s->img_out_n == 4); + if (stbi_unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + uint8 a = p[3]; + uint8 t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +static int parse_png_file(png *z, int scan, int req_comp) +{ + uint8 palette[1024], pal_img_n=0; + uint8 has_trans=0, tc[3]; + uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, iphone=0; + stbi *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!check_png_header(s)) return 0; + + if (scan == SCAN_type) return 1; + + for (;;) { + chunk c = get_chunk_header(s); + switch (c.type) { + case PNG_TYPE('C','g','B','I'): + iphone = stbi_de_iphone_flag; + skip(s, c.length); + break; + case PNG_TYPE('I','H','D','R'): { + int depth,color,comp,filter; + if (!first) return e("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); + s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); + s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); + depth = get8(s); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); + color = get8(s); if (color > 6) return e("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); + comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG"); + filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG"); + interlace = get8(s); if (interlace>1) return e("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + if (scan == SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return e("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case PNG_TYPE('P','L','T','E'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = get8u(s); + palette[i*4+1] = get8u(s); + palette[i*4+2] = get8u(s); + palette[i*4+3] = 255; + } + break; + } + + case PNG_TYPE('t','R','N','S'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (z->idata) return e("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = get8u(s); + } else { + if (!(s->img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); + if (c.length != (uint32) s->img_n*2) return e("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (uint8) get16(s); // non 8-bit images will be larger + } + break; + } + + case PNG_TYPE('I','D','A','T'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); + if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; } + if (ioff + c.length > idata_limit) { + uint8 *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); + z->idata = p; + } + if (!getn(s, z->idata+ioff,c.length)) return e("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case PNG_TYPE('I','E','N','D'): { + uint32 raw_len; + if (first) return e("first not IHDR", "Corrupt PNG"); + if (scan != SCAN_load) return 1; + if (z->idata == NULL) return e("no IDAT","Corrupt PNG"); + z->expanded = (uint8 *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !iphone); + if (z->expanded == NULL) return 0; // zlib should set error + free(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!create_png_image(z, z->expanded, raw_len, s->img_out_n, interlace)) return 0; + if (has_trans) + if (!compute_transparency(z, tc, s->img_out_n)) return 0; + if (iphone && s->img_out_n > 2) + stbi_de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!expand_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + free(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return e("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX chunk not known"; + invalid_chunk[0] = (uint8) (c.type >> 24); + invalid_chunk[1] = (uint8) (c.type >> 16); + invalid_chunk[2] = (uint8) (c.type >> 8); + invalid_chunk[3] = (uint8) (c.type >> 0); + #endif + return e(invalid_chunk, "PNG not supported: unknown chunk type"); + } + skip(s, c.length); + break; + } + // end of chunk, read and skip CRC + get32(s); + } +} + +static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + if (parse_png_file(p, SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + free(p->out); p->out = NULL; + free(p->expanded); p->expanded = NULL; + free(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + png p; + p.s = s; + return do_png(&p, x,y,comp,req_comp); +} + +static int stbi_png_test(stbi *s) +{ + int r; + r = check_png_header(s); + stbi_rewind(s); + return r; +} + +static int stbi_png_info_raw(png *p, int *x, int *y, int *comp) +{ + if (!parse_png_file(p, SCAN_header, 0)) { + stbi_rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi_png_info(stbi *s, int *x, int *y, int *comp) +{ + png p; + p.s = s; + return stbi_png_info_raw(&p, x, y, comp); +} + +// Microsoft/Windows BMP image + +static int bmp_test(stbi *s) +{ + int sz; + if (get8(s) != 'B') return 0; + if (get8(s) != 'M') return 0; + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + get32le(s); // discard data offset + sz = get32le(s); + if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; + return 0; +} + +static int stbi_bmp_test(stbi *s) +{ + int r = bmp_test(s); + stbi_rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *out; + unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (get8(s) != 'B' || get8(s) != 'M') return epuc("not BMP", "Corrupt BMP"); + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + offset = get32le(s); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = get16le(s); + s->img_y = get16le(s); + } else { + s->img_x = get32le(s); + s->img_y = get32le(s); + } + if (get16le(s) != 1) return epuc("bad BMP", "bad BMP"); + bpp = get16le(s); + if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = get32le(s); + if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); + get32le(s); // discard sizeof + get32le(s); // discard hres + get32le(s); // discard vres + get32le(s); // discard colorsused + get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + get32le(s); + get32le(s); + get32le(s); + get32le(s); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xffu << 16; + mg = 0xffu << 8; + mb = 0xffu << 0; + ma = 0xffu << 24; + fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 + } else { + mr = 31u << 10; + mg = 31u << 5; + mb = 31u << 0; + } + } else if (compress == 3) { + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return epuc("bad BMP", "bad BMP"); + } + } else + return epuc("bad BMP", "bad BMP"); + } + } else { + assert(hsz == 108); + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + ma = get32le(s); + get32le(s); // discard color space + for (i=0; i < 12; ++i) + get32le(s); // discard color space parameters + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) malloc(target * s->img_x * s->img_y); + if (!out) return epuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + if (hsz != 12) get8(s); + pal[i][3] = 255; + } + skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (s->img_x + 1) >> 1; + else if (bpp == 8) width = s->img_x; + else { free(out); return epuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=get8(s),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { free(out); return epuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = high_bit(mr)-7; rcount = bitcount(mr); + gshift = high_bit(mg)-7; gcount = bitcount(mr); + bshift = high_bit(mb)-7; bcount = bitcount(mr); + ashift = high_bit(ma)-7; acount = bitcount(mr); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + int a; + out[z+2] = get8u(s); + out[z+1] = get8u(s); + out[z+0] = get8u(s); + z += 3; + a = (easy == 2 ? get8(s) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } else { + for (i=0; i < (int) s->img_x; ++i) { + uint32 v = (bpp == 16 ? get16le(s) : get32le(s)); + int a; + out[z++] = (uint8) shiftsigned(v & mr, rshift, rcount); + out[z++] = (uint8) shiftsigned(v & mg, gshift, gcount); + out[z++] = (uint8) shiftsigned(v & mb, bshift, bcount); + a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } + skip(s, pad); + } + } + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} + +static stbi_uc *stbi_bmp_load(stbi *s,int *x, int *y, int *comp, int req_comp) +{ + return bmp_load(s, x,y,comp,req_comp); +} + + +// Targa Truevision - TGA +// by Jonathan Dummer + +static int tga_info(stbi *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp; + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if( sz > 1 ) { + stbi_rewind(s); + return 0; // only RGB or indexed allowed + } + sz = get8u(s); // image type + // only RGB or grey allowed, +/- RLE + if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; + skip(s,9); + tga_w = get16le(s); + if( tga_w < 1 ) { + stbi_rewind(s); + return 0; // test width + } + tga_h = get16le(s); + if( tga_h < 1 ) { + stbi_rewind(s); + return 0; // test height + } + sz = get8(s); // bits per pixel + // only RGB or RGBA or grey allowed + if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { + stbi_rewind(s); + return 0; + } + tga_comp = sz; + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp / 8; + return 1; // seems to have passed everything +} + +int stbi_tga_info(stbi *s, int *x, int *y, int *comp) +{ + return tga_info(s, x, y, comp); +} + +static int tga_test(stbi *s) +{ + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if ( sz > 1 ) return 0; // only RGB or indexed allowed + sz = get8u(s); // image type + if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + get16(s); // discard palette start + get16(s); // discard palette length + get8(s); // discard bits per palette color entry + get16(s); // discard x origin + get16(s); // discard y origin + if ( get16(s) < 1 ) return 0; // test width + if ( get16(s) < 1 ) return 0; // test height + sz = get8(s); // bits per pixel + if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed + return 1; // seems to have passed everything +} + +static int stbi_tga_test(stbi *s) +{ + int res = tga_test(s); + stbi_rewind(s); + return res; +} + +static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = get8u(s); + int tga_indexed = get8u(s); + int tga_image_type = get8u(s); + int tga_is_RLE = 0; + int tga_palette_start = get16le(s); + int tga_palette_len = get16le(s); + int tga_palette_bits = get8u(s); + int tga_x_origin = get16le(s); + int tga_y_origin = get16le(s); + int tga_width = get16le(s); + int tga_height = get16le(s); + int tga_bits_per_pixel = get8u(s); + int tga_inverted = get8u(s); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + unsigned char trans_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if ( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA + } + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) + { + tga_bits_per_pixel = tga_palette_bits; + } + + // tga info + *x = tga_width; + *y = tga_height; + if ( (req_comp < 1) || (req_comp > 4) ) + { + // just use whatever the file was + req_comp = tga_bits_per_pixel / 8; + *comp = req_comp; + } else + { + // force a new number of components + *comp = tga_bits_per_pixel/8; + } + tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); + if (!tga_data) return epuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + skip(s, tga_offset ); + // do I need to load a palette? + if ( tga_indexed ) + { + // any data to skip? (offset usually = 0) + skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); + if (!tga_palette) return epuc("outofmem", "Out of memory"); + if (!getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { + free(tga_data); + free(tga_palette); + return epuc("bad palette", "Corrupt TGA"); + } + } + // load the data + trans_data[0] = trans_data[1] = trans_data[2] = trans_data[3] = 0; + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE chunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = get8u(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = get8u(s); + if ( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = get8u(s); + } + } + // convert raw to the intermediate format + switch (tga_bits_per_pixel) + { + case 8: + // Luminous => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 16: + // Luminous,Alpha => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[1]; + break; + case 24: + // BGR => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 32: + // BGRA => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[3]; + break; + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + // convert to final format + switch (req_comp) + { + case 1: + // RGBA => Luminance + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + break; + case 2: + // RGBA => Luminance,Alpha + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + tga_data[i*req_comp+1] = trans_data[3]; + break; + case 3: + // RGBA => RGB + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + break; + case 4: + // RGBA => RGBA + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + tga_data[i*req_comp+3] = trans_data[3]; + break; + } + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * req_comp; + int index2 = (tga_height - 1 - j) * tga_width * req_comp; + for (i = tga_width * req_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + free( tga_palette ); + } + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} + +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return tga_load(s,x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +static int psd_test(stbi *s) +{ + if (get32(s) != 0x38425053) return 0; // "8BPS" + else return 1; +} + +static int stbi_psd_test(stbi *s) +{ + int r = psd_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + uint8 *out; + + // Check identifier + if (get32(s) != 0x38425053) // "8BPS" + return epuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (get16(s) != 1) + return epuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) + return epuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = get32(s); + w = get32(s); + + // Make sure the depth is 8 bits. + if (get16(s) != 8) + return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (get16(s) != 3) + return epuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + skip(s,get32(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + skip(s, get32(s) ); + + // Skip the reserved data. + skip(s, get32(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = get16(s); + if (compression > 1) + return epuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) malloc(4 * w*h); + if (!out) return epuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = get8u(s); + p += 4; + len--; + } + } else if (len > 128) { + uint8 val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = get8u(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + for (i = 0; i < pixelCount; i++) + *p = get8u(s), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + out = convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} + +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return psd_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +static int pic_is4(stbi *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int pic_test(stbi *s) +{ + int i; + + if (!pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + get8(s); + + if (!pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} pic_packet_t; + +static stbi_uc *pic_readval(stbi *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (at_eof(s)) return epuc("bad file","PIC file too short"); + dest[i]=get8u(s); + } + } + + return dest; +} + +static void pic_copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *pic_load2(stbi *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + pic_packet_t packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return epuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + + act_comp |= packet->channel; + + if (at_eof(s)) return epuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return epuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return epuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=get8u(s); + if (at_eof(s)) return epuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (uint8) left; + + if (!pic_readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = get8(s), i; + if (at_eof(s)) return epuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + int i; + + if (count==128) + count = get16(s); + else + count -= 127; + if (count > left) + return epuc("bad file","scanline overrun"); + + if (!pic_readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return epuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *pic_load(stbi *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + get8(s); + + x = get16(s); + y = get16(s); + if (at_eof(s)) return epuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return epuc("too large", "Image too large to decode"); + + get32(s); //skip `ratio' + get16(s); //skip `fields' + get16(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!pic_load2(s,x,y,comp, result)) { + free(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi_pic_test(stbi *s) +{ + int r = pic_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return pic_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb +typedef struct stbi_gif_lzw_struct { + int16 prefix; + uint8 first; + uint8 suffix; +} stbi_gif_lzw; + +typedef struct stbi_gif_struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags; + uint8 pal[256][4]; + uint8 lpal[256][4]; + stbi_gif_lzw codes[4096]; + uint8 *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi_gif; + +static int gif_test(stbi *s) +{ + int sz; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') return 0; + sz = get8(s); + if (sz != '9' && sz != '7') return 0; + if (get8(s) != 'a') return 0; + return 1; +} + +static int stbi_gif_test(stbi *s) +{ + int r = gif_test(s); + stbi_rewind(s); + return r; +} + +static void stbi_gif_parse_colortable(stbi *s, uint8 pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + pal[i][3] = transp ? 0 : 255; + } +} + +static int stbi_gif_header(stbi *s, stbi_gif *g, int *comp, int is_info) +{ + uint8 version; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') + return e("not GIF", "Corrupt GIF"); + + version = get8u(s); + if (version != '7' && version != '9') return e("not GIF", "Corrupt GIF"); + if (get8(s) != 'a') return e("not GIF", "Corrupt GIF"); + + failure_reason = ""; + g->w = get16le(s); + g->h = get16le(s); + g->flags = get8(s); + g->bgindex = get8(s); + g->ratio = get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi_gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi_gif_info_raw(stbi *s, int *x, int *y, int *comp) +{ + stbi_gif g; + if (!stbi_gif_header(s, &g, comp, 1)) { + stbi_rewind( s ); + return 0; + } + if (x) *x = g.w; + if (y) *y = g.h; + return 1; +} + +static void stbi_out_gif_code(stbi_gif *g, uint16 code) +{ + uint8 *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi_out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static uint8 *stbi_process_gif_raster(stbi *s, stbi_gif *g) +{ + uint8 lzw_cs; + int32 len, code; + uint32 first; + int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi_gif_lzw *p; + + lzw_cs = get8u(s); + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (code = 0; code < clear; code++) { + g->codes[code].prefix = -1; + g->codes[code].first = (uint8) code; + g->codes[code].suffix = (uint8) code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (int32) get8(s) << valid_bits; + valid_bits += 8; + } else { + int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + skip(s, len); + while ((len = get8(s)) > 0) + skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return epuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return epuc("too many codes", "Corrupt GIF"); + p->prefix = (int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return epuc("illegal code in raster", "Corrupt GIF"); + + stbi_out_gif_code(g, (uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return epuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi_fill_gif_background(stbi_gif *g) +{ + int i; + uint8 *c = g->pal[g->bgindex]; + // @OPTIMIZE: write a dword at a time + for (i = 0; i < g->w * g->h * 4; i += 4) { + uint8 *p = &g->out[i]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static uint8 *stbi_gif_load_next(stbi *s, stbi_gif *g, int *comp, int req_comp) +{ + int i; + uint8 *old_out = 0; + + if (g->out == 0) { + if (!stbi_gif_header(s, g, comp,0)) return 0; // failure_reason set by stbi_gif_header + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + stbi_fill_gif_background(g); + } else { + // animated-gif-only path + if (((g->eflags & 0x1C) >> 2) == 3) { + old_out = g->out; + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + memcpy(g->out, old_out, g->w*g->h*4); + } + } + + for (;;) { + switch (get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int32 x, y, w, h; + uint8 *o; + + x = get16le(s); + y = get16le(s); + w = get16le(s); + h = get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return epuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi_gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (uint8 *) g->lpal; + } else if (g->flags & 0x80) { + for (i=0; i < 256; ++i) // @OPTIMIZE: reset only the previous transparent + g->pal[i][3] = 255; + if (g->transparent >= 0 && (g->eflags & 0x01)) + g->pal[g->transparent][3] = 0; + g->color_table = (uint8 *) g->pal; + } else + return epuc("missing color table", "Corrupt GIF"); + + o = stbi_process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (req_comp && req_comp != 4) + o = convert_format(o, 4, req_comp, g->w, g->h); + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (get8(s) == 0xF9) { // Graphic Control Extension. + len = get8(s); + if (len == 4) { + g->eflags = get8(s); + get16le(s); // delay + g->transparent = get8(s); + } else { + skip(s, len); + break; + } + } + while ((len = get8(s)) != 0) + skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (uint8 *) 1; + + default: + return epuc("unknown code", "Corrupt GIF"); + } + } +} + +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *u = 0; + stbi_gif g={0}; + + u = stbi_gif_load_next(s, &g, comp, req_comp); + if (u == (void *) 1) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + } + + return u; +} + +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp) +{ + return stbi_gif_info_raw(s,x,y,comp); +} + + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int hdr_test(stbi *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi_hdr_test(stbi* s) +{ + int r = hdr_test(s); + stbi_rewind(s); + return r; +} + +#define HDR_BUFLEN 1024 +static char *hdr_gettoken(stbi *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) get8(z); + + while (!at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == HDR_BUFLEN-1) { + // flush to end of line + while (!at_eof(z) && get8(z) != '\n') + ; + break; + } + c = (char) get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return epf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return epf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = strtol(token, NULL, 10); + + *x = width; + *y = height; + + *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + getn(s, rgbe, 4); + hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = get8(s); + c2 = get8(s); + len = get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + uint8 rgbe[4]; + rgbe[0] = (uint8) c1; + rgbe[1] = (uint8) c2; + rgbe[2] = (uint8) len; + rgbe[3] = (uint8) get8u(s); + hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + free(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= get8(s); + if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = get8u(s); + if (count > 128) { + // Run + value = get8u(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = get8u(s); + } + } + } + for (i=0; i < width; ++i) + hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + free(scanline); + } + + return hdr_data; +} + +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return hdr_load(s,x,y,comp,req_comp); +} + +static int stbi_hdr_info(stbi *s, int *x, int *y, int *comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { + stbi_rewind( s ); + return 0; + } + + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi_rewind( s ); + return 0; + } + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *y = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *x = strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +static int stbi_bmp_info(stbi *s, int *x, int *y, int *comp) +{ + int hsz; + if (get8(s) != 'B' || get8(s) != 'M') { + stbi_rewind( s ); + return 0; + } + skip(s,12); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) { + stbi_rewind( s ); + return 0; + } + if (hsz == 12) { + *x = get16le(s); + *y = get16le(s); + } else { + *x = get32le(s); + *y = get32le(s); + } + if (get16le(s) != 1) { + stbi_rewind( s ); + return 0; + } + *comp = get16le(s) / 8; + return 1; +} + +static int stbi_psd_info(stbi *s, int *x, int *y, int *comp) +{ + int channelCount; + if (get32(s) != 0x38425053) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 1) { + stbi_rewind( s ); + return 0; + } + skip(s, 6); + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) { + stbi_rewind( s ); + return 0; + } + *y = get32(s); + *x = get32(s); + if (get16(s) != 8) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 3) { + stbi_rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi_pic_info(stbi *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + pic_packet_t packets[10]; + + skip(s, 92); + + *x = get16(s); + *y = get16(s); + if (at_eof(s)) return 0; + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi_rewind( s ); + return 0; + } + + skip(s, 8); + + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + act_comp |= packet->channel; + + if (at_eof(s)) { + stbi_rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi_rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} + +static int stbi_info_main(stbi *s, int *x, int *y, int *comp) +{ + if (stbi_jpeg_info(s, x, y, comp)) + return 1; + if (stbi_png_info(s, x, y, comp)) + return 1; + if (stbi_gif_info(s, x, y, comp)) + return 1; + if (stbi_bmp_info(s, x, y, comp)) + return 1; + if (stbi_psd_info(s, x, y, comp)) + return 1; + if (stbi_pic_info(s, x, y, comp)) + return 1; + #ifndef STBI_NO_HDR + if (stbi_hdr_info(s, x, y, comp)) + return 1; + #endif + // test tga last because it's a crappy test! + if (stbi_tga_info(s, x, y, comp)) + return 1; + return e("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = fopen(filename, "rb"); + int result; + if (!f) return e("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi s; + long pos = ftell(f); + start_file(&s, f); + r = stbi_info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_info_main(&s,x,y,comp); +} + +int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi_info_main(&s,x,y,comp); +} + +#endif // STBI_HEADER_FILE_ONLY + +/* + revision history: + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-uint8 to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.e. Janez (U+017D)emva) + 1.21 fix use of 'uint8' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 2008-08-02 + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 first released version +*/ diff --git a/Demos/bdwallpaper/resource.h b/Demos/bdwallpaper/resource.h new file mode 100644 index 00000000..02b2d8ff Binary files /dev/null and b/Demos/bdwallpaper/resource.h differ diff --git a/Demos/duidemo/ControlEx.h b/Demos/duidemo/ControlEx.h new file mode 100644 index 00000000..264c3184 --- /dev/null +++ b/Demos/duidemo/ControlEx.h @@ -0,0 +1,17 @@ +#ifndef __CONTROLEX_H__ +#define __CONTROLEX_H__ + +#include +#include + + +class CDialogBuilderCallbackEx : public IDialogBuilderCallback +{ +public: + CControlUI* CreateControl(LPCTSTR pstrClass) + { + return NULL; + } +}; + +#endif __CONTROLEX_H__ diff --git a/Demos/duidemo/Flash11.tlb b/Demos/duidemo/Flash11.tlb new file mode 100644 index 00000000..813228bf Binary files /dev/null and b/Demos/duidemo/Flash11.tlb differ diff --git a/Demos/duidemo/Res/duidemo.ico b/Demos/duidemo/Res/duidemo.ico new file mode 100644 index 00000000..683ea4d3 Binary files /dev/null and b/Demos/duidemo/Res/duidemo.ico differ diff --git a/Demos/duidemo/Res/duidemo.zip b/Demos/duidemo/Res/duidemo.zip new file mode 100644 index 00000000..17e6f88a Binary files /dev/null and b/Demos/duidemo/Res/duidemo.zip differ diff --git a/Demos/duidemo/SkinFrame.h b/Demos/duidemo/SkinFrame.h new file mode 100644 index 00000000..cdf6092f --- /dev/null +++ b/Demos/duidemo/SkinFrame.h @@ -0,0 +1,154 @@ +#pragma once + +#include "SkinManager.h" + +class CSkinFrame : public CWindowWnd, public INotifyUI +{ +public: + CSkinFrame(HWND hParent, CControlUI *pControl) { + m_hParent = hParent; + m_pControl = pControl; + Create(NULL, _T("skinframe"), WS_POPUP | WS_VISIBLE, WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST); + }; + +public: + void Init() + { + if(m_pControl == NULL) return; + + RECT rcConrol = m_pControl->GetPos(); + SIZE szWnd = m_pm.GetInitSize(); + POINT ptWnd = {0}; + ptWnd.x = rcConrol.right - szWnd.cx; + ptWnd.y = rcConrol.bottom; + ::ClientToScreen(m_hParent, &ptWnd); + + SetWindowPos(m_hWnd, NULL, ptWnd.x, ptWnd.y, szWnd.cx, szWnd.cy, SWP_NOSIZE | SWP_NOACTIVATE); + } + +public: + LPCTSTR GetWindowClassName() const { return _T("UISkinFrame"); }; + UINT GetClassStyle() const { return CS_DBLCLKS; }; + void OnFinalMessage(HWND /*hWnd*/) { delete this; }; + + void Notify(TNotifyUI& msg) + { + if( msg.sType == _T("click") ) { + // ťϢ + OnLClick(msg.pSender); + } + } + + void OnLClick(CControlUI *pControl) + { + CDuiString sName = pControl->GetName(); + + if(sName.CompareNoCase(_T("skin_image_btn")) == 0) + { + SkinChangedParam skin; + skin.bColor = false; + skin.bgimage = pControl->GetUserData(); + CSkinManager::GetSkinManager()->Broadcast(skin); + } + else if(sName.CompareNoCase(_T("skin_color_btn")) == 0) + { + SkinChangedParam skin; + skin.bColor = true; + skin.bkcolor = pControl->GetBkColor(); + CSkinManager::GetSkinManager()->Broadcast(skin); + } + else if(sName.CompareNoCase(_T("skin_color")) == 0) + { + CTabLayoutUI *pSkinTab = (CTabLayoutUI*)m_pm.FindControl(_T("skin_tab")); + pSkinTab->SelectItem(1); + } + else if(sName.CompareNoCase(_T("skin_image")) == 0) + { + CTabLayoutUI *pSkinTab = (CTabLayoutUI*)m_pm.FindControl(_T("skin_tab")); + pSkinTab->SelectItem(0); + } + } + + LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + // ICONS ͼ + SetIcon(IDR_MAINFRAME); + + LONG styleValue = ::GetWindowLong(*this, GWL_STYLE); + styleValue &= ~WS_CAPTION; + ::SetWindowLong(*this, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); + + m_pm.Init(m_hWnd); + CDialogBuilder builder; + CDialogBuilderCallbackEx cb; + CControlUI* pRoot = builder.Create(_T("skin.xml"), (UINT)0, &cb, &m_pm); + ASSERT(pRoot && "Failed to parse XML"); + m_pm.AttachDialog(pRoot); + m_pm.AddNotifier(this); + + Init(); + return 0; + } + + LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + bHandled = FALSE; + return 0; + } + + LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + bHandled = FALSE; + return 0; + } + + LRESULT OnNcActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + if( ::IsIconic(*this) ) bHandled = FALSE; + return (wParam == 0) ? TRUE : FALSE; + } + + LRESULT OnNcCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + return 0; + } + + LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + return 0; + } + + + LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + LRESULT lRes = 0; + BOOL bHandled = TRUE; + switch( uMsg ) { + case WM_CREATE: lRes = OnCreate(uMsg, wParam, lParam, bHandled); break; + case WM_CLOSE: lRes = OnClose(uMsg, wParam, lParam, bHandled); break; + case WM_DESTROY: lRes = OnDestroy(uMsg, wParam, lParam, bHandled); break; + case WM_NCACTIVATE: lRes = OnNcActivate(uMsg, wParam, lParam, bHandled); break; + case WM_NCCALCSIZE: lRes = OnNcCalcSize(uMsg, wParam, lParam, bHandled); break; + case WM_NCPAINT: lRes = OnNcPaint(uMsg, wParam, lParam, bHandled); break; + case WM_KILLFOCUS: lRes = OnKillFocus(uMsg, wParam, lParam, bHandled); break; + default: + bHandled = FALSE; + } + if( bHandled ) return lRes; + if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes; + return CWindowWnd::HandleMessage(uMsg, wParam, lParam); + } + + LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + Close(0); + return 0; + } + +public: + CPaintManagerUI m_pm; + +private: + HWND m_hParent; + CControlUI* m_pControl; +}; diff --git a/Demos/duidemo/SkinManager.h b/Demos/duidemo/SkinManager.h new file mode 100644 index 00000000..96f96d44 --- /dev/null +++ b/Demos/duidemo/SkinManager.h @@ -0,0 +1,60 @@ +#ifndef __SKIN_MANAGER_H__ +#define __SKIN_MANAGER_H__ + +#include "..\..\DuiLib\UIlib.h" + +struct SkinChangedParam +{ + bool bColor; + DWORD bkcolor; + CDuiString bgimage; +}; + +typedef class ObserverImpl SkinChangedObserver; +typedef class ReceiverImpl SkinChangedReceiver; + +class CSkinManager +{ +public: + static CSkinManager* GetSkinManager() + { + if (m_pSkinManager == NULL) + { + m_pSkinManager = new CSkinManager(); + } + + return m_pSkinManager; + } + +public: + void AddReceiver(ReceiverImpl* receiver) + { + m_SkinChangeObserver.AddReceiver(receiver); + } + + void RemoveReceiver(ReceiverImpl* receiver) + { + m_SkinChangeObserver.RemoveReceiver(receiver); + } + + void Broadcast(SkinChangedParam param) + { + m_SkinChangeObserver.Broadcast(param); + } + + void Notify(SkinChangedParam param) + { + m_SkinChangeObserver.Notify(param); + } + +private: + CSkinManager(){} + +private: + SkinChangedObserver m_SkinChangeObserver; + static CSkinManager* m_pSkinManager; +}; + +CSkinManager* CSkinManager::m_pSkinManager = NULL; + +#endif // __SKIN_MANAGER_H__ \ No newline at end of file diff --git a/Demos/duidemo/StdAfx.cpp b/Demos/duidemo/StdAfx.cpp new file mode 100644 index 00000000..79228f61 --- /dev/null +++ b/Demos/duidemo/StdAfx.cpp @@ -0,0 +1,15 @@ +// stdafx.cpp : source file that includes just the standard includes +// App.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +#if defined _M_IX86 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_IA64 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_X64 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#else +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif diff --git a/Demos/duidemo/StdAfx.h b/Demos/duidemo/StdAfx.h new file mode 100644 index 00000000..99dd11e6 --- /dev/null +++ b/Demos/duidemo/StdAfx.h @@ -0,0 +1,35 @@ + +#if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) +#define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_ + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include + +#include "..\..\DuiLib\UIlib.h" + +using namespace DuiLib; + +#ifdef _DEBUG +# ifdef _UNICODE +# pragma comment(lib, "..\\..\\lib\\DuiLib_d.lib") +# else +# pragma comment(lib, "..\\..\\lib\\DuiLib_d.lib") +# endif +#else +# ifdef _UNICODE +# pragma comment(lib, "..\\..\\lib\\DuiLib.lib") +# else +# pragma comment(lib, "..\\..\\lib\\DuiLib.lib") +# endif +#endif + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) diff --git a/Demos/duidemo/duidemo.cpp b/Demos/duidemo/duidemo.cpp new file mode 100644 index 00000000..87cbf5d5 --- /dev/null +++ b/Demos/duidemo/duidemo.cpp @@ -0,0 +1,364 @@ +#include "stdafx.h" +#include +#include +#include "ControlEx.h" +#include "resource.h" +#include +//#include "flash10a.tlh" +#include "SkinFrame.h" + +//#import "Flash11.tlb" raw_interfaces_only, named_guids +//using namespace ShockwaveFlashObjects; + +class CDemoFrame : public CWindowWnd, public INotifyUI, public CWebBrowserEventHandler, public SkinChangedReceiver +{ +public: + CDemoFrame() { + + }; + +public: + void Init() + { + CSkinManager::GetSkinManager()->AddReceiver(this); + + m_pCloseBtn = static_cast(m_pm.FindControl(_T("closebtn"))); + m_pMaxBtn = static_cast(m_pm.FindControl(_T("maxbtn"))); + m_pRestoreBtn = static_cast(m_pm.FindControl(_T("restorebtn"))); + m_pMinBtn = static_cast(m_pm.FindControl(_T("minbtn"))); + m_pSkinBtn = static_cast(m_pm.FindControl(_T("skinbtn"))); + CWebBrowserUI* pBrowser1 = static_cast(m_pm.FindControl(_T("oneclick_browser1"))); + pBrowser1->SetWebBrowserEventHandler(this); + CWebBrowserUI* pBrowser2 = static_cast(m_pm.FindControl(_T("oneclick_browser2"))); + pBrowser2->SetWebBrowserEventHandler(this); + pBrowser1->NavigateUrl(_T("http://www.winradar.com/?f=duidemo")); + pBrowser2->NavigateUrl(_T("http://www.2345.com/?kms656067418")); + + CComboUI* pFontSize = static_cast(m_pm.FindControl(_T("font_size"))); + if(pFontSize) + { + CListLabelElementUI * pElement = new CListLabelElementUI(); + pElement->SetText(_T("aklsdjfajsdlkf")); + pElement->SetFixedHeight(30); + pElement->SetFixedWidth(120); + pFontSize->Add(pElement); + } + } + + virtual BOOL Receive(SkinChangedParam param) + { + CControlUI* pRoot = m_pm.FindControl(_T("root")); + if( pRoot != NULL ) { + if( param.bColor ) { + pRoot->SetBkColor(param.bkcolor); + pRoot->SetBkImage(_T("")); + } + else { + pRoot->SetBkColor(0); + pRoot->SetBkImage(param.bgimage); + } + } + return TRUE; + } + + virtual HRESULT STDMETHODCALLTYPE UpdateUI( void) + { + return S_OK; + } + +public: + LPCTSTR GetWindowClassName() const { return _T("UIMainFrame"); }; + UINT GetClassStyle() const { return CS_DBLCLKS; }; + void OnFinalMessage(HWND /*hWnd*/) { delete this; }; + + void Notify(TNotifyUI& msg) + { + if(msg.sType == _T("windowinit")) { + } + else if( msg.sType == _T("showactivex") ) { + if( msg.pSender->GetName().CompareNoCase(_T("ani_flash")) == 0 ) { + IShockwaveFlash* pFlash = NULL; + CActiveXUI* pActiveX = static_cast(msg.pSender); + pActiveX->GetControl(__uuidof(IShockwaveFlash), (void**)&pFlash); + if( pFlash != NULL ) { + pFlash->put_WMode( _bstr_t(_T("Transparent") ) ); + pFlash->put_Movie( _bstr_t(CPaintManagerUI::GetInstancePath() + _T("\\skin\\duidemo\\waterdrop.swf")) ); + pFlash->DisableLocalSecurity(); + pFlash->put_AllowScriptAccess(L"always"); + BSTR response; + pFlash->CallFunction(L"Click me!", &response); + pFlash->Release(); + } + } + } + else if( msg.sType == _T("click") ) { + if( msg.pSender == m_pCloseBtn ) { + PostQuitMessage(0); + return; + } + else if( msg.pSender == m_pMinBtn ) { + SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0); return; } + else if( msg.pSender == m_pMaxBtn ) { + SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, 0); return; } + else if( msg.pSender == m_pRestoreBtn ) { + SendMessage(WM_SYSCOMMAND, SC_RESTORE, 0); return; } + else if( msg.pSender == m_pSkinBtn ) { + new CSkinFrame(m_hWnd, m_pSkinBtn); + } + // ťϢ + OnLClick(msg.pSender); + } + else if(msg.sType==_T("selectchanged")) + { + CDuiString name = msg.pSender->GetName(); + CTabLayoutUI* pTabSwitch = static_cast(m_pm.FindControl(_T("tab_switch"))); + + if(name.CompareNoCase(_T("basic_tab")) == 0) pTabSwitch->SelectItem(0); + if(name.CompareNoCase(_T("rich_tab")) == 0) pTabSwitch->SelectItem(1); + if(name.CompareNoCase(_T("ani_tab")) == 0) pTabSwitch->SelectItem(2); + if(name.CompareNoCase(_T("split_tab")) == 0) pTabSwitch->SelectItem(3); + } + } + + void OnLClick(CControlUI *pControl) + { + CDuiString sName = pControl->GetName(); + if(sName.CompareNoCase(_T("homepage_btn")) == 0) + { + ShellExecute(NULL, _T("open"), _T("http://www.winradar.com/?f=duidemo"), NULL, NULL, SW_SHOW); + } + } + + LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + // ICONS ͼ + SetIcon(IDR_MAINFRAME); + + LONG styleValue = ::GetWindowLong(*this, GWL_STYLE); + styleValue &= ~WS_CAPTION; + ::SetWindowLong(*this, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); + + m_pm.Init(m_hWnd); + CDialogBuilder builder; + CDialogBuilderCallbackEx cb; + CControlUI* pRoot = builder.Create(_T("main.xml"), (UINT)0, &cb, &m_pm); + ASSERT(pRoot && "Failed to parse XML"); + m_pm.AttachDialog(pRoot); + m_pm.AddNotifier(this); + + Init(); + return 0; + } + + LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + bHandled = FALSE; + return 0; + } + + LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + ::PostQuitMessage(0L); + + bHandled = FALSE; + return 0; + } + + LRESULT OnNcActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + if( ::IsIconic(*this) ) bHandled = FALSE; + return (wParam == 0) ? TRUE : FALSE; + } + + LRESULT OnNcCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + return 0; + } + + LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + return 0; + } + + LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + POINT pt; pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam); + ::ScreenToClient(*this, &pt); + + RECT rcClient; + ::GetClientRect(*this, &rcClient); + + if( !::IsZoomed(*this) ) { + RECT rcSizeBox = m_pm.GetSizeBox(); + if( pt.y < rcClient.top + rcSizeBox.top ) { + if( pt.x < rcClient.left + rcSizeBox.left ) return HTTOPLEFT; + if( pt.x > rcClient.right - rcSizeBox.right ) return HTTOPRIGHT; + return HTTOP; + } + else if( pt.y > rcClient.bottom - rcSizeBox.bottom ) { + if( pt.x < rcClient.left + rcSizeBox.left ) return HTBOTTOMLEFT; + if( pt.x > rcClient.right - rcSizeBox.right ) return HTBOTTOMRIGHT; + return HTBOTTOM; + } + if( pt.x < rcClient.left + rcSizeBox.left ) return HTLEFT; + if( pt.x > rcClient.right - rcSizeBox.right ) return HTRIGHT; + } + + RECT rcCaption = m_pm.GetCaptionRect(); + if( pt.x >= rcClient.left + rcCaption.left && pt.x < rcClient.right - rcCaption.right \ + && pt.y >= rcCaption.top && pt.y < rcCaption.bottom ) { + CControlUI* pControl = static_cast(m_pm.FindControl(pt)); + if( pControl && _tcscmp(pControl->GetClass(), _T("ButtonUI")) != 0 && + _tcscmp(pControl->GetClass(), _T("OptionUI")) != 0 && + _tcscmp(pControl->GetClass(), _T("TextUI")) != 0 ) + return HTCAPTION; + } + + return HTCLIENT; + } + + LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + SIZE szRoundCorner = m_pm.GetRoundCorner(); + if( !::IsIconic(*this) && (szRoundCorner.cx != 0 || szRoundCorner.cy != 0) ) { + CDuiRect rcWnd; + ::GetWindowRect(*this, &rcWnd); + rcWnd.Offset(-rcWnd.left, -rcWnd.top); + rcWnd.right++; rcWnd.bottom++; + HRGN hRgn = ::CreateRoundRectRgn(rcWnd.left, rcWnd.top, rcWnd.right, rcWnd.bottom, szRoundCorner.cx, szRoundCorner.cy); + ::SetWindowRgn(*this, hRgn, TRUE); + ::DeleteObject(hRgn); + } + + bHandled = FALSE; + return 0; + } + + LRESULT OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + MONITORINFO oMonitor = {}; + oMonitor.cbSize = sizeof(oMonitor); + ::GetMonitorInfo(::MonitorFromWindow(*this, MONITOR_DEFAULTTOPRIMARY), &oMonitor); + CDuiRect rcWork = oMonitor.rcWork; + rcWork.Offset(-rcWork.left, -rcWork.top); + + LPMINMAXINFO lpMMI = (LPMINMAXINFO) lParam; + lpMMI->ptMaxPosition.x = rcWork.left; + lpMMI->ptMaxPosition.y = rcWork.top; + lpMMI->ptMaxSize.x = rcWork.right; + lpMMI->ptMaxSize.y = rcWork.bottom; + + bHandled = FALSE; + return 0; + } + + LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + // ʱյWM_NCDESTROYյwParamΪSC_CLOSEWM_SYSCOMMAND + if( wParam == SC_CLOSE ) { + ::PostQuitMessage(0L); + bHandled = TRUE; + return 0; + } + BOOL bZoomed = ::IsZoomed(*this); + LRESULT lRes = CWindowWnd::HandleMessage(uMsg, wParam, lParam); + if( ::IsZoomed(*this) != bZoomed ) { + if( !bZoomed ) { + CControlUI* pControl = static_cast(m_pm.FindControl(_T("maxbtn"))); + if( pControl ) pControl->SetVisible(false); + pControl = static_cast(m_pm.FindControl(_T("restorebtn"))); + if( pControl ) pControl->SetVisible(true); + } + else { + CControlUI* pControl = static_cast(m_pm.FindControl(_T("maxbtn"))); + if( pControl ) pControl->SetVisible(true); + pControl = static_cast(m_pm.FindControl(_T("restorebtn"))); + if( pControl ) pControl->SetVisible(false); + } + } + return lRes; + } + + LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + LRESULT lRes = 0; + BOOL bHandled = TRUE; + switch( uMsg ) { + case WM_CREATE: lRes = OnCreate(uMsg, wParam, lParam, bHandled); break; + case WM_CLOSE: lRes = OnClose(uMsg, wParam, lParam, bHandled); break; + case WM_DESTROY: lRes = OnDestroy(uMsg, wParam, lParam, bHandled); break; + case WM_NCACTIVATE: lRes = OnNcActivate(uMsg, wParam, lParam, bHandled); break; + case WM_NCCALCSIZE: lRes = OnNcCalcSize(uMsg, wParam, lParam, bHandled); break; + case WM_NCPAINT: lRes = OnNcPaint(uMsg, wParam, lParam, bHandled); break; + case WM_NCHITTEST: lRes = OnNcHitTest(uMsg, wParam, lParam, bHandled); break; + case WM_SIZE: lRes = OnSize(uMsg, wParam, lParam, bHandled); break; + case WM_GETMINMAXINFO: lRes = OnGetMinMaxInfo(uMsg, wParam, lParam, bHandled); break; + case WM_SYSCOMMAND: lRes = OnSysCommand(uMsg, wParam, lParam, bHandled); break; + default: + bHandled = FALSE; + } + if( bHandled ) return lRes; + if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes; + return CWindowWnd::HandleMessage(uMsg, wParam, lParam); + } + +public: + CPaintManagerUI m_pm; + +private: + CButtonUI* m_pCloseBtn; + CButtonUI* m_pMaxBtn; + CButtonUI* m_pRestoreBtn; + CButtonUI* m_pMinBtn; + CButtonUI* m_pSkinBtn; +}; + +static LPBYTE resource_zip_buffer_ = NULL; + +//#define USE_EMBEDED_RESOURCE + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow) +{ + CPaintManagerUI::SetInstance(hInstance); + +#ifdef USE_EMBEDED_RESOURCE + HRSRC hResource = ::FindResource(CPaintManagerUI::GetResourceDll(), MAKEINTRESOURCE(IDR_ZIPRES), _T("ZIPRES")); + if( hResource == NULL ) + return 0L; + DWORD dwSize = 0; + HGLOBAL hGlobal = ::LoadResource(CPaintManagerUI::GetResourceDll(), hResource); + if( hGlobal == NULL ) { + FreeResource(hResource); + return 0L; + } + dwSize = ::SizeofResource(CPaintManagerUI::GetResourceDll(), hResource); + if( dwSize == 0 ) + return 0L; + resource_zip_buffer_ = new BYTE[ dwSize ]; + if (resource_zip_buffer_ != NULL) + { + ::CopyMemory(resource_zip_buffer_, (LPBYTE)::LockResource(hGlobal), dwSize); + } + + ::FreeResource(hResource); + CPaintManagerUI::SetResourceZip(resource_zip_buffer_, dwSize); +#else + CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath() + _T("skin\\duidemo"));// + //CPaintManagerUI::SetResourceZip(_T("gamebox.zip")); +#endif + + + HRESULT Hr = ::CoInitialize(NULL); + if( FAILED(Hr) ) return 0; + + CDemoFrame* pFrame = new CDemoFrame(); + if( pFrame == NULL ) return 0; + pFrame->Create(NULL, _T("duilibʾ"), UI_WNDSTYLE_FRAME, 0L, 0, 0, 800, 572); + pFrame->CenterWindow(); + ::ShowWindow(*pFrame, SW_SHOW); + + CPaintManagerUI::MessageLoop(); + + ::CoUninitialize(); + return 0; +} \ No newline at end of file diff --git a/Demos/duidemo/duidemo.rc b/Demos/duidemo/duidemo.rc new file mode 100644 index 00000000..8b287c28 Binary files /dev/null and b/Demos/duidemo/duidemo.rc differ diff --git a/Demos/duidemo/duidemo.vcxproj b/Demos/duidemo/duidemo.vcxproj new file mode 100644 index 00000000..053c1f85 --- /dev/null +++ b/Demos/duidemo/duidemo.vcxproj @@ -0,0 +1,194 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + duidemo + {54019823-E923-44D0-AD60-8EB636D107DC} + 360 + + + + + + + + + + + + Application + false + Unicode + + + Application + false + Unicode + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)bin\ + $(SolutionDir)temp\duidemo\$(Configuration)\ + true + $(SolutionDir)bin\ + $(SolutionDir)temp\duidemo\$(Configuration)\ + false + AllRules.ruleset + + + AllRules.ruleset + + + $(ProjectName)_d + $(ProjectName) + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/duidemo.tlb + + + + + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + Use + stdafx.h + true + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x0406 + + + true + true + true + Windows + MachineX86 + + + true + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/duidemo.tlb + + + + + MinSpace + OnlyExplicitInline + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreaded + true + Use + stdafx.h + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0406 + + + true + Windows + MachineX86 + + + + + true + + + + + + %(PreprocessorDefinitions) + Create + %(PreprocessorDefinitions) + Create + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demos/duidemo/duidemo.vcxproj.filters b/Demos/duidemo/duidemo.vcxproj.filters new file mode 100644 index 00000000..3be8131e --- /dev/null +++ b/Demos/duidemo/duidemo.vcxproj.filters @@ -0,0 +1,135 @@ + + + + + {c75b9d7c-452a-4867-a585-e8aee4a05f51} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {249f81db-516d-4a60-9882-d3fd674e412c} + h;hpp;hxx;hm;inl + + + {5323114f-c462-496c-8a26-5f0586629382} + + + {495d8e73-881d-481f-83d1-3ca5ee46318e} + + + {d2f13808-77fd-462b-bab2-501843e85c61} + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resources + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\xmls + + + Resources\xmls + + + Header Files + + + + + Resources + + + \ No newline at end of file diff --git a/Demos/duidemo/duidemo.vcxproj.user b/Demos/duidemo/duidemo.vcxproj.user new file mode 100644 index 00000000..ace9a86a --- /dev/null +++ b/Demos/duidemo/duidemo.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Demos/duidemo/flash10a.tlh b/Demos/duidemo/flash10a.tlh new file mode 100644 index 00000000..cb6b47b5 --- /dev/null +++ b/Demos/duidemo/flash10a.tlh @@ -0,0 +1,326 @@ +// Created by Microsoft (R) C/C++ Compiler Version 14.00.50727.762 (78e39b2f). +// +// +// C++ source equivalent of Win32 type library c:\windows\system32\macromed\flash\flash10a.ocx +// compiler-generated file created 01/29/09 at 22:30:01 - DO NOT EDIT! + +#pragma once +#pragma pack(push, 8) + +#include + +// +// Forward references and typedefs +// + +struct __declspec(uuid("d27cdb6b-ae6d-11cf-96b8-444553540000")) +/* LIBID */ __ShockwaveFlashObjects; +struct __declspec(uuid("d27cdb6c-ae6d-11cf-96b8-444553540000")) +/* dual interface */ IShockwaveFlash; +struct __declspec(uuid("d27cdb6d-ae6d-11cf-96b8-444553540000")) +/* dispinterface */ _IShockwaveFlashEvents; +struct /* coclass */ ShockwaveFlash; +struct /* coclass */ FlashProp; +struct __declspec(uuid("d27cdb70-ae6d-11cf-96b8-444553540000")) +/* interface */ IFlashFactory; +struct __declspec(uuid("d27cdb72-ae6d-11cf-96b8-444553540000")) +/* interface */ IFlashObjectInterface; +struct __declspec(uuid("a6ef9860-c720-11d0-9337-00a0c90dcaa9")) +/* interface */ IDispatchEx; +struct /* coclass */ FlashObjectInterface; + +// +// Smart pointer typedef declarations +// + +_COM_SMARTPTR_TYPEDEF(IShockwaveFlash, __uuidof(IShockwaveFlash)); +_COM_SMARTPTR_TYPEDEF(_IShockwaveFlashEvents, __uuidof(_IShockwaveFlashEvents)); +_COM_SMARTPTR_TYPEDEF(IFlashFactory, __uuidof(IFlashFactory)); +_COM_SMARTPTR_TYPEDEF(IDispatchEx, __uuidof(IDispatchEx)); +_COM_SMARTPTR_TYPEDEF(IFlashObjectInterface, __uuidof(IFlashObjectInterface)); + +// +// Type library items +// + +struct __declspec(uuid("d27cdb6c-ae6d-11cf-96b8-444553540000")) +IShockwaveFlash : IDispatch +{ + // + // Raw methods provided by interface + // + + virtual HRESULT __stdcall get_ReadyState ( + /*[out,retval]*/ long * pVal ) = 0; + virtual HRESULT __stdcall get_TotalFrames ( + /*[out,retval]*/ long * pVal ) = 0; + virtual HRESULT __stdcall get_Playing ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_Playing ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; + virtual HRESULT __stdcall get_Quality ( + /*[out,retval]*/ int * pVal ) = 0; + virtual HRESULT __stdcall put_Quality ( + /*[in]*/ int pVal ) = 0; + virtual HRESULT __stdcall get_ScaleMode ( + /*[out,retval]*/ int * pVal ) = 0; + virtual HRESULT __stdcall put_ScaleMode ( + /*[in]*/ int pVal ) = 0; + virtual HRESULT __stdcall get_AlignMode ( + /*[out,retval]*/ int * pVal ) = 0; + virtual HRESULT __stdcall put_AlignMode ( + /*[in]*/ int pVal ) = 0; + virtual HRESULT __stdcall get_BackgroundColor ( + /*[out,retval]*/ long * pVal ) = 0; + virtual HRESULT __stdcall put_BackgroundColor ( + /*[in]*/ long pVal ) = 0; + virtual HRESULT __stdcall get_Loop ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_Loop ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; + virtual HRESULT __stdcall get_Movie ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_Movie ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_FrameNum ( + /*[out,retval]*/ long * pVal ) = 0; + virtual HRESULT __stdcall put_FrameNum ( + /*[in]*/ long pVal ) = 0; + virtual HRESULT __stdcall SetZoomRect ( + /*[in]*/ long left, + /*[in]*/ long top, + /*[in]*/ long right, + /*[in]*/ long bottom ) = 0; + virtual HRESULT __stdcall Zoom ( + /*[in]*/ int factor ) = 0; + virtual HRESULT __stdcall Pan ( + /*[in]*/ long x, + /*[in]*/ long y, + /*[in]*/ int mode ) = 0; + virtual HRESULT __stdcall Play ( ) = 0; + virtual HRESULT __stdcall Stop ( ) = 0; + virtual HRESULT __stdcall Back ( ) = 0; + virtual HRESULT __stdcall Forward ( ) = 0; + virtual HRESULT __stdcall Rewind ( ) = 0; + virtual HRESULT __stdcall StopPlay ( ) = 0; + virtual HRESULT __stdcall GotoFrame ( + /*[in]*/ long FrameNum ) = 0; + virtual HRESULT __stdcall CurrentFrame ( + /*[out,retval]*/ long * FrameNum ) = 0; + virtual HRESULT __stdcall IsPlaying ( + /*[out,retval]*/ VARIANT_BOOL * Playing ) = 0; + virtual HRESULT __stdcall PercentLoaded ( + /*[out,retval]*/ long * percent ) = 0; + virtual HRESULT __stdcall FrameLoaded ( + /*[in]*/ long FrameNum, + /*[out,retval]*/ VARIANT_BOOL * loaded ) = 0; + virtual HRESULT __stdcall FlashVersion ( + /*[out,retval]*/ long * version ) = 0; + virtual HRESULT __stdcall get_WMode ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_WMode ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_SAlign ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_SAlign ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_Menu ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_Menu ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; + virtual HRESULT __stdcall get_Base ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_Base ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_Scale ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_Scale ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_DeviceFont ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_DeviceFont ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; + virtual HRESULT __stdcall get_EmbedMovie ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_EmbedMovie ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; + virtual HRESULT __stdcall get_BGColor ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_BGColor ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_Quality2 ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_Quality2 ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall LoadMovie ( + /*[in]*/ int layer, + /*[in]*/ BSTR url ) = 0; + virtual HRESULT __stdcall TGotoFrame ( + /*[in]*/ BSTR target, + /*[in]*/ long FrameNum ) = 0; + virtual HRESULT __stdcall TGotoLabel ( + /*[in]*/ BSTR target, + /*[in]*/ BSTR label ) = 0; + virtual HRESULT __stdcall TCurrentFrame ( + /*[in]*/ BSTR target, + /*[out,retval]*/ long * FrameNum ) = 0; + virtual HRESULT __stdcall TCurrentLabel ( + /*[in]*/ BSTR target, + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall TPlay ( + /*[in]*/ BSTR target ) = 0; + virtual HRESULT __stdcall TStopPlay ( + /*[in]*/ BSTR target ) = 0; + virtual HRESULT __stdcall SetVariable ( + /*[in]*/ BSTR name, + /*[in]*/ BSTR value ) = 0; + virtual HRESULT __stdcall GetVariable ( + /*[in]*/ BSTR name, + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall TSetProperty ( + /*[in]*/ BSTR target, + /*[in]*/ int property, + /*[in]*/ BSTR value ) = 0; + virtual HRESULT __stdcall TGetProperty ( + /*[in]*/ BSTR target, + /*[in]*/ int property, + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall TCallFrame ( + /*[in]*/ BSTR target, + /*[in]*/ int FrameNum ) = 0; + virtual HRESULT __stdcall TCallLabel ( + /*[in]*/ BSTR target, + /*[in]*/ BSTR label ) = 0; + virtual HRESULT __stdcall TSetPropertyNum ( + /*[in]*/ BSTR target, + /*[in]*/ int property, + /*[in]*/ double value ) = 0; + virtual HRESULT __stdcall TGetPropertyNum ( + /*[in]*/ BSTR target, + /*[in]*/ int property, + /*[out,retval]*/ double * pVal ) = 0; + virtual HRESULT __stdcall TGetPropertyAsNumber ( + /*[in]*/ BSTR target, + /*[in]*/ int property, + /*[out,retval]*/ double * pVal ) = 0; + virtual HRESULT __stdcall get_SWRemote ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_SWRemote ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_FlashVars ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_FlashVars ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_AllowScriptAccess ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_AllowScriptAccess ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_MovieData ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_MovieData ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_InlineData ( + /*[out,retval]*/ IUnknown * * ppIUnknown ) = 0; + virtual HRESULT __stdcall put_InlineData ( + /*[in]*/ IUnknown * ppIUnknown ) = 0; + virtual HRESULT __stdcall get_SeamlessTabbing ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_SeamlessTabbing ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; + virtual HRESULT __stdcall EnforceLocalSecurity ( ) = 0; + virtual HRESULT __stdcall get_Profile ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_Profile ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; + virtual HRESULT __stdcall get_ProfileAddress ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_ProfileAddress ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_ProfilePort ( + /*[out,retval]*/ long * pVal ) = 0; + virtual HRESULT __stdcall put_ProfilePort ( + /*[in]*/ long pVal ) = 0; + virtual HRESULT __stdcall CallFunction ( + /*[in]*/ BSTR request, + /*[out,retval]*/ BSTR * response ) = 0; + virtual HRESULT __stdcall SetReturnValue ( + /*[in]*/ BSTR returnValue ) = 0; + virtual HRESULT __stdcall DisableLocalSecurity ( ) = 0; + virtual HRESULT __stdcall get_AllowNetworking ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_AllowNetworking ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_AllowFullScreen ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_AllowFullScreen ( + /*[in]*/ BSTR pVal ) = 0; +}; + +struct __declspec(uuid("d27cdb6d-ae6d-11cf-96b8-444553540000")) +_IShockwaveFlashEvents : IDispatch +{}; + +struct __declspec(uuid("d27cdb6e-ae6d-11cf-96b8-444553540000")) +ShockwaveFlash; + // [ default ] interface IShockwaveFlash + // [ default, source ] dispinterface _IShockwaveFlashEvents + +struct __declspec(uuid("1171a62f-05d2-11d1-83fc-00a0c9089c5a")) +FlashProp; + // [ default ] interface IUnknown + +struct __declspec(uuid("d27cdb70-ae6d-11cf-96b8-444553540000")) +IFlashFactory : IUnknown +{}; + +struct __declspec(uuid("a6ef9860-c720-11d0-9337-00a0c90dcaa9")) +IDispatchEx : IDispatch +{ + // + // Raw methods provided by interface + // + + virtual HRESULT __stdcall GetDispID ( + /*[in]*/ BSTR bstrName, + /*[in]*/ unsigned long grfdex, + /*[out]*/ long * pid ) = 0; + virtual HRESULT __stdcall RemoteInvokeEx ( + /*[in]*/ long id, + /*[in]*/ unsigned long lcid, + /*[in]*/ unsigned long dwFlags, + /*[in]*/ DISPPARAMS * pdp, + /*[out]*/ VARIANT * pvarRes, + /*[out]*/ EXCEPINFO * pei, + /*[in]*/ struct IServiceProvider * pspCaller, + /*[in]*/ unsigned int cvarRefArg, + /*[in]*/ unsigned int * rgiRefArg, + /*[in,out]*/ VARIANT * rgvarRefArg ) = 0; + virtual HRESULT __stdcall DeleteMemberByName ( + /*[in]*/ BSTR bstrName, + /*[in]*/ unsigned long grfdex ) = 0; + virtual HRESULT __stdcall DeleteMemberByDispID ( + /*[in]*/ long id ) = 0; + virtual HRESULT __stdcall GetMemberProperties ( + /*[in]*/ long id, + /*[in]*/ unsigned long grfdexFetch, + /*[out]*/ unsigned long * pgrfdex ) = 0; + virtual HRESULT __stdcall GetMemberName ( + /*[in]*/ long id, + /*[out]*/ BSTR * pbstrName ) = 0; + virtual HRESULT __stdcall GetNextDispID ( + /*[in]*/ unsigned long grfdex, + /*[in]*/ long id, + /*[out]*/ long * pid ) = 0; + virtual HRESULT __stdcall GetNameSpaceParent ( + /*[out]*/ IUnknown * * ppunk ) = 0; +}; + +struct __declspec(uuid("d27cdb72-ae6d-11cf-96b8-444553540000")) +IFlashObjectInterface : IDispatchEx +{}; + +struct __declspec(uuid("d27cdb71-ae6d-11cf-96b8-444553540000")) +FlashObjectInterface; + // [ default ] interface IFlashObjectInterface + +#pragma pack(pop) diff --git "a/Demos/duidemo/observer_impl_base - \345\211\257\346\234\254.h" "b/Demos/duidemo/observer_impl_base - \345\211\257\346\234\254.h" new file mode 100644 index 00000000..efc0cf7d --- /dev/null +++ "b/Demos/duidemo/observer_impl_base - \345\211\257\346\234\254.h" @@ -0,0 +1,139 @@ +#ifndef OBSERVER_IMPL_BASE_HPP +#define OBSERVER_IMPL_BASE_HPP + +#include +#include + +template +class ReceiverImplBase; + +template +class ObserverImplBase +{ +public: + virtual void AddReceiver(ReceiverImplBase* receiver) = 0; + virtual void RemoveReceiver(ReceiverImplBase* receiver) = 0; + virtual ReturnT Broadcast(ParamT param) = 0; + virtual ReturnT Notify(ParamT param) = 0; +}; + +template +class ReceiverImplBase +{ +public: + virtual void AddObserver(ObserverImplBase* observer) = 0; + virtual void RemoveObserver() = 0; + virtual ReturnT Receive(ParamT param) = 0; + virtual ReturnT Respond(ParamT param, ObserverImplBase* observer) = 0; +}; + +template +class ReceiverImpl; + +template +class ObserverImpl : public ObserverImplBase +{ +public: + ObserverImpl() + : count_(0) + {} + + virtual ~ObserverImpl() {} + + virtual void AddReceiver(ReceiverImplBase* receiver) + { + if (receiver == NULL) + return; + + receivers_[count_] = receiver; + receiver->AddObserver(this); + count_++; + } + + virtual void RemoveReceiver(ReceiverImplBase* receiver) + { + if (receiver == NULL) + return; + + ReceiversMap::iterator it = receivers_.begin(); + for (; it != receivers_.end(); ++it) + { + if (it->second == receiver) + { + receivers_.erase(it); + break; + } + } + } + + virtual ReturnT Broadcast(ParamT param) + { + ReceiversMap::iterator it = receivers_.begin(); + for (; it != receivers_.end(); ++it) + { + it->second->Receive(param); + } + + return ReturnT(); + } + + virtual ReturnT Notify(ParamT param) + { + ReceiversMap::iterator it = receivers_.begin(); + for (; it != receivers_.end(); ++it) + { + it->second->Respond(param, this); + } + + return ReturnT(); + } + +protected: + typedef std::map*> ReceiversMap; + ReceiversMap receivers_; + int count_; +}; + + +template +class ReceiverImpl : public ReceiverImplBase +{ +public: + ReceiverImpl() + : count_(0) + {} + + virtual ~ReceiverImpl() {} + + virtual void AddObserver(ObserverImplBase* observer) + { + observers_[count_] = observer; + count_++; + } + + virtual void RemoveObserver() + { + ObserversMap::iterator it = observers_.begin(); + for (; it != observers_.end(); ++it) + { + it->second->RemoveReceiver(this); + } + } + + virtual ReturnT Receive(ParamT param) + { + return ReturnT(); + } + + virtual ReturnT Respond(ParamT param, ObserverImplBase* observer) + { + return ReturnT(); + } + +protected: + typedef std::map*> ObserversMap; + ObserversMap observers_; + int count_; +}; + +#endif // OBSERVER_IMPL_BASE_HPP \ No newline at end of file diff --git a/Demos/duidemo/resource.h b/Demos/duidemo/resource.h new file mode 100644 index 00000000..d1f8f097 Binary files /dev/null and b/Demos/duidemo/resource.h differ diff --git a/Demos/duidemo/skin_change_event.h b/Demos/duidemo/skin_change_event.h new file mode 100644 index 00000000..a7771728 --- /dev/null +++ b/Demos/duidemo/skin_change_event.h @@ -0,0 +1,17 @@ +#ifndef SKIN_CHANGE_EVENT_HPP +#define SKIN_CHANGE_EVENT_HPP + +#include "..\..\DuiLib\UIlib.h"" + +struct SkinChangedParam +{ + bool bColor; + DWORD bkcolor; + CDuiString bgimage; +}; + +typedef class ObserverImpl SkinChangedObserver; +typedef class ReceiverImpl SkinChangedReceiver; + + +#endif // SKIN_CHANGE_EVENT_HPP \ No newline at end of file diff --git a/Demos/gamebox/AnimationHelper.cpp b/Demos/gamebox/AnimationHelper.cpp new file mode 100644 index 00000000..25d2f1d8 --- /dev/null +++ b/Demos/gamebox/AnimationHelper.cpp @@ -0,0 +1,1201 @@ +#include "stdafx.h" +#include "AnimationHelper.h" + +/////////////////////////////////////////////////////////////////////////////////////// +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened +typedef DWORD ZRESULT; +typedef struct +{ + int index; // index of this file within the zip + char name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; +typedef struct +{ + int index; // index of this file within the zip + TCHAR name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; +#define OpenZip OpenZipU +#define CloseZip(hz) CloseZipU(hz) +extern HZIP OpenZipU(void *z,unsigned int len,DWORD flags); +extern ZRESULT CloseZipU(HZIP hz); +#ifdef _UNICODE +#define ZIPENTRY ZIPENTRYW +#define GetZipItem GetZipItemW +#define FindZipItem FindZipItemW +#else +#define GetZipItem GetZipItemA +#define FindZipItem FindZipItemA +#endif +extern ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze); +extern ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *ze); +extern ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze); +extern ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *ze); +extern ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags); +/////////////////////////////////////////////////////////////////////////////////////// + +extern "C" +{ + extern unsigned char *stbi_load_from_memory(unsigned char const *buffer, int len, int *x, int *y, \ + int *comp, int req_comp); + extern void stbi_image_free(void *retval_from_stbi_load); + +}; + +namespace DuiLib +{ + /************************************************************************/ + /* CAnimation */ + /************************************************************************/ + + ////////////////////////////////////////////////////////////////////// + // Nested structures member functions + ////////////////////////////////////////////////////////////////////// + inline int CAnimation::TGIFControlExt::GetPackedValue(enum ControlExtValues Value) + { + int nRet = (int) m_cPacked; + switch(Value) + { + case GCX_PACKED_DISPOSAL: + nRet = (nRet & 28) >> 2; + break; + + case GCX_PACKED_USERINPUT: + nRet = (nRet & 2) >> 1; + break; + + case GCX_PACKED_TRANSPCOLOR: + nRet &= 1; + break; + }; + + return nRet; + } + + inline int CAnimation::TGIFLSDescriptor::GetPackedValue(enum LSDPackedValues Value) + { + int nRet = (int) m_cPacked; + + switch(Value) + { + case LSD_PACKED_GLOBALCT: + nRet = nRet >> 7; + break; + + case LSD_PACKED_CRESOLUTION: + nRet = ((nRet & 0x70) >> 4) + 1; + break; + + case LSD_PACKED_SORT: + nRet = (nRet & 8) >> 3; + break; + + case LSD_PACKED_GLOBALCTSIZE: + nRet &= 7; + break; + }; + + return nRet; + } + + inline int CAnimation::TGIFImageDescriptor::GetPackedValue(enum IDPackedValues Value) + { + int nRet = (int) m_cPacked; + + switch(Value) + { + case ID_PACKED_LOCALCT: + nRet >>= 7; + break; + + case ID_PACKED_INTERLACE: + nRet = ((nRet & 0x40) >> 6); + break; + + case ID_PACKED_SORT: + nRet = (nRet & 0x20) >> 5; + break; + + case ID_PACKED_LOCALCTSIZE: + nRet &= 7; + break; + }; + + return nRet; + } + + ////////////////////////////////////////////////////////////////////// + // ctor && dtor + ////////////////////////////////////////////////////////////////////// + CAnimation::CAnimation(IAnimationCallback* pCallback) : m_pCallback(pCallback) + { + // check structures size + assert(sizeof(TGIFImageDescriptor) == 10); + assert(sizeof(TGIFAppExtension) == 14); + assert(sizeof(TGIFPlainTextExt) == 15); + assert(sizeof(TGIFLSDescriptor) == 7); + assert(sizeof(TGIFControlExt) == 8); + assert(sizeof(TGIFCommentExt) == 2); + assert(sizeof(TGIFHeader) == 6); + + m_pRawData = NULL; + m_nDataSize = 0; + m_pGIFHeader = NULL; + m_pGIFLSDescriptor = NULL; + m_nGlobalCTSize = 0; + m_PictureSize.cx = m_PictureSize.cy = 0; + m_clrBackground = RGB(255, 255, 255); // white by default + m_nCurrFrame = 0; + m_nCurrOffset = 0; + m_hThread = NULL; + m_bExitThread = true; + + m_hDrawEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); + assert(m_hDrawEvent); + m_pvFrames = new VTFRAME; + assert(m_pvFrames); + } + + CAnimation::~CAnimation() + { + UnLoad(); + delete m_pvFrames; + m_pvFrames = NULL; + CloseHandle(m_hDrawEvent); + m_hDrawEvent = NULL; + } + + const TImageInfo* CAnimation::LoadGIF(LPCTSTR bitmap, LPCTSTR type/* = NULL*/, DWORD mask/* = 0*/) + { + LPBYTE pData = NULL; + DWORD dwSize = 0; + if( type != NULL && isdigit(*bitmap) ) + { + LPTSTR pstr = NULL; + int iIndex = _tcstol(bitmap, &pstr, 10); + HRSRC hPicture = ::FindResource(CPaintManagerUI::GetResourceDll(), MAKEINTRESOURCE(iIndex), type); + if (!hPicture) + return NULL; + HGLOBAL hResData; + if (!(hResData = ::LoadResource(CPaintManagerUI::GetResourceDll(), hPicture))) + { + ::FreeResource(hPicture); + return NULL; + }; + dwSize = ::SizeofResource(CPaintManagerUI::GetResourceDll(),hPicture); + if( dwSize < sizeof(TGIFHeader)) + { + ::FreeResource(hPicture); + return NULL; + } + pData = static_cast( malloc(dwSize*sizeof(BYTE)) ); + if (!pData) + { + ::FreeResource(hPicture); + return NULL; + } + LPBYTE pSrc = static_cast( ::LockResource(hResData) ); + if (!pSrc) + { + free(pData); + ::FreeResource(hPicture); + return NULL; + } + ::CopyMemory(pData, pSrc, dwSize); + ::FreeResource(hPicture); + } + else + { + CDuiString sFile = CPaintManagerUI::GetResourcePath(); + if( CPaintManagerUI::GetResourceZip().IsEmpty() ) { + sFile += bitmap; + HANDLE hFile = INVALID_HANDLE_VALUE; + DWORD dwRead=0; + hFile = ::CreateFile(sFile.GetData(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, \ + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if(INVALID_HANDLE_VALUE == hFile) + return NULL; + dwSize = ::GetFileSize(hFile, NULL); + if (dwSize < sizeof(TGIFHeader)) + { + CloseHandle(hFile); + return NULL; + } + pData = static_cast( malloc(dwSize*sizeof(BYTE)) ); + if (!pData) + { + CloseHandle(hFile); + return NULL; + } + ::ReadFile(hFile, pData, dwSize, &dwRead, NULL); + ::CloseHandle(hFile); + } + else + { + sFile += CPaintManagerUI::GetResourceZip(); + HZIP hz = NULL; + if( CPaintManagerUI::IsCachedResourceZip() ) hz = (HZIP)CPaintManagerUI::GetResourceZipHandle(); + else hz = OpenZip((void*)sFile.GetData(), 0, 2); + if( hz == NULL ) + return NULL; + ZIPENTRY ze; + int i; + if( FindZipItem(hz, bitmap, true, &i, &ze) != 0 ) + { + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + return NULL; + } + dwSize = ze.unc_size; + if (dwSize < sizeof(TGIFHeader)) + { + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + return NULL; + } + pData = static_cast( malloc(dwSize*sizeof(BYTE)) ); + if (!pData) + { + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + return NULL; + } + int res = UnzipItem(hz, i, pData, dwSize, 3); + if( res != 0x00000000 && res != 0x00000600) { + free(pData); + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + return NULL; + } + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + } + } + + const TImageInfo* data = LoadGIF(pData, dwSize); + free(pData); + return data; + } + + const TImageInfo* CAnimation::LoadGIF(const LPBYTE pData, DWORD dwSize, DWORD mask/* = 0*/) + { + if (!pData || dwSize==0) + return NULL; + + UnLoad(); + + if(!(m_pRawData = const_cast(pData))) + return NULL; + + m_nDataSize = dwSize; + m_pGIFHeader = (TGIFHeader*)m_pRawData; + + if((memcmp(&m_pGIFHeader->m_cSignature, "GIF", 3) != 0) + && ((memcmp(&m_pGIFHeader->m_cVersion, "87a", 3) != 0) + || (memcmp(&m_pGIFHeader->m_cVersion, "89a", 3) != 0))) + { + // it's neither GIF87a nor GIF89a + // do nothing + // clear GIF variables + m_pRawData = NULL; + m_pGIFHeader = NULL; + m_nDataSize = 0; + + return NULL; + } + + m_pGIFLSDescriptor = (TGIFLSDescriptor*)(m_pRawData + sizeof(TGIFHeader)); + if(m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT) == 1) + { + // calculate the globat color table size + m_nGlobalCTSize = static_cast(3 * (1 + << (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE) + + 1))); + // get the background color if GCT is present + unsigned char * pBkClr = m_pRawData + + sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + + 3 * m_pGIFLSDescriptor->m_cBkIndex; + m_clrBackground = RGB(pBkClr[0], pBkClr[1], pBkClr[2]); + }; + + // store the picture's size + m_PictureSize.cx = m_pGIFLSDescriptor->m_wWidth; + m_PictureSize.cy = m_pGIFLSDescriptor->m_wHeight; + + // determine frame count for this picture + UINT nFrameCount = 0; + ResetDataPointer(); + while(SkipNextGraphicBlock()) + nFrameCount++; + +#ifdef GIF_TRACING + OutputDebugString(_T(" -= GIF encountered\n" + "Logical Screen dimensions = %dx%d\n" + "Global color table = %d\n" + "Color depth = %d\n" + "Sort flag = %d\n" + "Size of Global Color Table = %d\n" + "Background color index = %d\n" + "Pixel aspect ratio = %d\n" + "Frame count = %d\n" + "Background color = %06Xh\n\n"), + m_pGIFLSDescriptor->m_wWidth, + m_pGIFLSDescriptor->m_wHeight, + m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT), + m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_CRESOLUTION), + m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_SORT), + m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE), + m_pGIFLSDescriptor->m_cBkIndex, + m_pGIFLSDescriptor->m_cPixelAspect, + nFrameCount, + m_clrBackground); + EnumGIFBlocks(); +#endif + + if(nFrameCount <= 1) + { + // now check the frame count + // if there's an empty GIF or only one frame, + // no need to animate this GIF + // therefore, treat it like any other pic + m_pRawData = NULL; + return NULL; + } + + // if, on the contrary, there are several frames + // then store separate frames in an array + TFrame frame; + UINT nBlockLen = 0; + LPBYTE pFrameData = NULL; + UINT nCurFrame = 0; + ////////////////////////////////////////////////////////////////////////// + // Before rendering a frame we should take care of what's + // behind that frame. TFrame::m_nDisposal will be our guide: + // 0 - no disposal specified (do nothing) + // 1 - do not dispose (again, do nothing) + // 2 - restore to background color (m_clrBackground) + // 3 - restore to previous + // background color for first frame + LPBYTE pDispData = static_cast(malloc(m_PictureSize.cx * m_PictureSize.cy * sizeof(COLORREF))); + if (!pDispData) + { + m_pRawData = NULL; + return NULL; + } + for (int n = 0; n < m_PictureSize.cx*m_PictureSize.cy; n++) + ((COLORREF*)pDispData)[n] = m_clrBackground; + + ResetDataPointer(); + while(pFrameData = GetNextGraphicBlock(&nBlockLen, + &frame.m_nDelay, + &frame.m_frameSize, + &frame.m_frameOffset, + &frame.m_nDisposal)) + { +#ifdef GIF_TRACING + ////////////////////////////////////////////// + // uncomment the following strings if you want + // to write separate frames on disk + // + // CString szName; + // szName.Format(_T("%.4d.gif"),nCurFrame); + // WriteDataOnDisk(szName,hFrameData,nBlockLen); + // nCurFrame++; +#endif // GIF_TRACING + if (!pFrameData) + continue; + + LPBYTE pImage = NULL; + int x,y,n; + pImage = stbi_load_from_memory(pFrameData, nBlockLen, &x, &y, &n, 4); + free(pFrameData); + pFrameData = NULL; + BITMAPINFO bmi; + ::ZeroMemory(&bmi, sizeof(BITMAPINFO)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = x; + bmi.bmiHeader.biHeight = -y; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = x * y * 4; + + bool bAlphaChannel = false; + LPBYTE pDest = NULL; + HBITMAP hBitmap = ::CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**)&pDest, NULL, 0); + if( !hBitmap ) + { + stbi_image_free(pImage); + continue; + } + + for( int i = 0; i < x * y; i++ ) + { + DWORD dwPixel = pImage[i*4+3]==BYTE(0) ? ((DWORD*)pDispData)[i] : ((DWORD*)pImage)[i];; + LPBYTE pPixel = (LPBYTE)(&dwPixel); + pDest[i*4 + 3] = pPixel[3]; + if( pDest[i*4 + 3] < 255 ) + { + pDest[i*4] = (BYTE)(DWORD(pPixel[2])*pPixel[3]/255); + pDest[i*4 + 1] = (BYTE)(DWORD(pPixel[1])*pPixel[3]/255); + pDest[i*4 + 2] = (BYTE)(DWORD(pPixel[0])*pPixel[3]/255); + bAlphaChannel = true; + } + else + { + pDest[i*4] = pPixel[2]; + pDest[i*4 + 1] = pPixel[1]; + pDest[i*4 + 2] = pPixel[0]; + } + + if( *(DWORD*)(&pDest[i*4]) == mask ) { + pDest[i*4] = (BYTE)0; + pDest[i*4 + 1] = (BYTE)0; + pDest[i*4 + 2] = (BYTE)0; + pDest[i*4 + 3] = (BYTE)0; + bAlphaChannel = true; + } + } + stbi_image_free(pImage); + + switch (frame.m_nDisposal) + { + case 0: + memset(pDispData, 0, m_PictureSize.cx*m_PictureSize.cy*sizeof(COLORREF)); + break; + case 1: + memcpy(pDispData, pDest, x * y * 4); + break; + case 2: + for (int n = 0; n < m_PictureSize.cx*m_PictureSize.cy; n++) + ((COLORREF*)pDispData)[n] = m_clrBackground; + break; + case 3: + break; + } + + frame.m_pImage = new TImageInfo; + frame.m_pImage->hBitmap = hBitmap; + frame.m_pImage->nX = x; + frame.m_pImage->nY = y; + frame.m_pImage->alphaChannel = bAlphaChannel; + + nCurFrame++; + // everything went well, add this frame + m_pvFrames->push_back(frame); + }; + + // clean after ourselves + free(pDispData); pDispData = NULL; + m_pRawData = NULL; + m_nDataSize = 0; + m_pGIFHeader = NULL; + m_pGIFLSDescriptor = NULL; + m_nGlobalCTSize = 0; + m_nCurrOffset = 0; + + return m_pvFrames->empty() ? NULL : ((*m_pvFrames)[0]).m_pImage; + } + + void CAnimation::UnLoad() + { + Stop(); + + std::vector::iterator it; + for(it = m_pvFrames->begin();it < m_pvFrames->end();it++) + { + if ((*it).m_pImage) + { + ::DeleteObject((*it).m_pImage->hBitmap); + delete (*it).m_pImage; + } + } + m_pvFrames->clear(); + m_PictureSize.cx = m_PictureSize.cy = 0; + m_clrBackground = RGB(255, 255, 255); // white by default + m_nCurrFrame = 0; + } + + bool CAnimation::Play() + { + if(!m_bExitThread) + return false; + + if (m_hThread != NULL) + { + ::WaitForSingleObject(m_hThread, 5000); + ::CloseHandle(m_hThread); + m_hThread = NULL; + } + + DWORD nDummy; + m_bExitThread = false; + m_hThread = (HANDLE) ::CreateThread(NULL, + 0, + _ThreadAnimation, + (void *)this, + CREATE_SUSPENDED, + &nDummy); + if(!m_hThread) + { + m_bExitThread = true; + return false; + } + else + ::ResumeThread(m_hThread); + + return true; + } + + void CAnimation::Stop() + { + m_bExitThread = true; + ::SetEvent(m_hDrawEvent); + // we'll wait for 5 seconds then continue execution + ::WaitForSingleObject(m_hThread, 5000); + ::CloseHandle(m_hThread); + m_hThread = NULL; + } + + bool CAnimation::IsPlaying() const + { + return !m_bExitThread; + } + + SIZE CAnimation::GetSize() const + { + return m_PictureSize; + } + + int CAnimation::GetFrameCount() const + { + if (m_pvFrames) + return m_pvFrames->size(); + else + return 0; + } + + COLORREF CAnimation::GetBkColor() const + { + return m_clrBackground; + } + + const TImageInfo* CAnimation::GetCurImage() + { + TImageInfo* data = ((*m_pvFrames)[m_nCurrFrame]).m_pImage; + if (!IsPlaying()) + Play(); + ::SetEvent(m_hDrawEvent); + return data; + } + + ////////////////////////////////////////////////////////////////////// + // protected methods + ////////////////////////////////////////////////////////////////////// + int CAnimation::GetNextBlockLen() const + { + GIFBlockTypes nBlock = GetNextBlock(); + + int nTmp; + + switch(nBlock) + { + case BLOCK_UNKNOWN: + return -1; + break; + + case BLOCK_TRAILER: + return 1; + break; + + case BLOCK_APPEXT: + nTmp = GetSubBlocksLen(m_nCurrOffset + sizeof(TGIFAppExtension)); + if(nTmp > 0) + return sizeof(TGIFAppExtension) + nTmp; + break; + + case BLOCK_COMMEXT: + nTmp = GetSubBlocksLen(m_nCurrOffset + sizeof(TGIFCommentExt)); + if(nTmp > 0) + return sizeof(TGIFCommentExt) + nTmp; + break; + + case BLOCK_CONTROLEXT: + return sizeof(TGIFControlExt); + break; + + case BLOCK_PLAINTEXT: + nTmp = GetSubBlocksLen(m_nCurrOffset + sizeof(TGIFPlainTextExt)); + if(nTmp > 0) + return sizeof(TGIFPlainTextExt) + nTmp; + break; + + case BLOCK_IMAGE: + TGIFImageDescriptor* pIDescr = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + int nLCTSize = (int) + (pIDescr->GetPackedValue(ID_PACKED_LOCALCT) * 3 * (1 + << (pIDescr->GetPackedValue(ID_PACKED_LOCALCTSIZE) + + 1))); + + int nTmp = GetSubBlocksLen(m_nCurrOffset + sizeof(TGIFImageDescriptor) + nLCTSize + 1); + if(nTmp > 0) + return sizeof(TGIFImageDescriptor) + nLCTSize + 1 + nTmp; + break; + }; + + return 0; + } + + BOOL CAnimation::SkipNextBlock() + { + if(!m_pRawData) + return FALSE; + + int nLen = GetNextBlockLen(); + if((nLen <= 0) || ((m_nCurrOffset + nLen) > m_nDataSize)) + return FALSE; + + m_nCurrOffset += nLen; + return TRUE; + } + + BOOL CAnimation::SkipNextGraphicBlock() + { + if(!m_pRawData) + return FALSE; + + // GIF header + LSDescriptor [+ GCT] [+ Control block] + Data + + enum GIFBlockTypes nBlock; + + nBlock = GetNextBlock(); + + while((nBlock != BLOCK_CONTROLEXT) + && (nBlock != BLOCK_IMAGE) + && (nBlock != BLOCK_PLAINTEXT) + && (nBlock != BLOCK_UNKNOWN) + && (nBlock != BLOCK_TRAILER)) + { + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + }; + + if((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER)) + return FALSE; + + // it's either a control ext.block, an image or a plain text + + if(GetNextBlockLen() <= 0) + return FALSE; + + if(nBlock == BLOCK_CONTROLEXT) + { + if(!SkipNextBlock()) + return FALSE; + nBlock = GetNextBlock(); + + // skip everything until we meet an image block or a plain-text block + while((nBlock != BLOCK_IMAGE) + && (nBlock != BLOCK_PLAINTEXT) + && (nBlock != BLOCK_UNKNOWN) + && (nBlock != BLOCK_TRAILER)) + { + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + }; + + if((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER)) + return FALSE; + }; + + // skip the found data block (image or plain-text) + if(!SkipNextBlock()) + return FALSE; + + return TRUE; + } + + void CAnimation::ResetDataPointer() + { + // skip header and logical screen descriptor + m_nCurrOffset = sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + m_nGlobalCTSize; + } + + enum CAnimation::GIFBlockTypes CAnimation::GetNextBlock() const + { + switch(m_pRawData[m_nCurrOffset]) + { + case 0x21: + // extension block + switch(m_pRawData[m_nCurrOffset + 1]) + { + case 0x01: + // plain text extension + return BLOCK_PLAINTEXT; + break; + + case 0xF9: + // graphic control extension + return BLOCK_CONTROLEXT; + break; + + case 0xFE: + // comment extension + return BLOCK_COMMEXT; + break; + + case 0xFF: + // application extension + return BLOCK_APPEXT; + break; + }; + break; + + case 0x3B: + // trailer + return BLOCK_TRAILER; + break; + + case 0x2C: + // image data + return BLOCK_IMAGE; + break; + }; + + return BLOCK_UNKNOWN; + } + + UINT CAnimation::GetSubBlocksLen(UINT nStartingOffset) const + { + UINT nRet = 0; + UINT nCurOffset = nStartingOffset; + + while(m_pRawData[nCurOffset] != 0 ) + { + nRet += m_pRawData[nCurOffset] + 1; + nCurOffset += m_pRawData[nCurOffset] + 1; + }; + + return nRet + 1; + } + + LPBYTE CAnimation::GetNextGraphicBlock(UINT* pBlockLen, UINT* pDelay, SIZE* pBlockSize, SIZE* pBlockOffset, UINT* pDisposal) + { + if(!m_pRawData) + return NULL; + + // GIF header + LSDescriptor [+ GCT] [+ Control block] + Data + + *pDisposal = 0; + enum GIFBlockTypes nBlock; + nBlock = GetNextBlock(); + + while((nBlock != BLOCK_CONTROLEXT) + && (nBlock != BLOCK_IMAGE) + && (nBlock != BLOCK_PLAINTEXT) + && (nBlock != BLOCK_UNKNOWN) + && (nBlock != BLOCK_TRAILER)) + { + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + }; + + if((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER)) + return NULL; + + // it's either a control ext.block, an image or a plain text + + int nStart = m_nCurrOffset; + int nBlockLen = GetNextBlockLen(); + + if(nBlockLen <= 0) + return NULL; + + if(nBlock == BLOCK_CONTROLEXT) + { + // get the following data + TGIFControlExt* pControl = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + // store delay time + *pDelay = pControl->m_wDelayTime; + // store disposal method + *pDisposal = pControl->GetPackedValue(GCX_PACKED_DISPOSAL); + + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + + // skip everything until we find data to display + // (image block or plain-text block) + + while((nBlock != BLOCK_IMAGE) + && (nBlock != BLOCK_PLAINTEXT) + && (nBlock != BLOCK_UNKNOWN) + && (nBlock != BLOCK_TRAILER)) + { + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + nBlockLen += GetNextBlockLen(); + }; + + if((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER)) + return NULL; + nBlockLen += GetNextBlockLen(); + } + else + *pDelay = -1; // to indicate that there was no delay value + + if(nBlock == BLOCK_IMAGE) + { + // store size and offsets + TGIFImageDescriptor* pImage = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + pBlockSize->cx = pImage->m_wWidth; + pBlockSize->cy = pImage->m_wHeight; + pBlockOffset->cx = pImage->m_wLeftPos; + pBlockOffset->cy = pImage->m_wTopPos; + }; + + if(!SkipNextBlock()) + return NULL; + + LPBYTE pData = static_cast(malloc(sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + m_nGlobalCTSize + nBlockLen + 1)); + if (!pData) + return NULL; + + int nOffset = 0; + + CopyMemory(pData, m_pRawData, sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + m_nGlobalCTSize); + nOffset += sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + m_nGlobalCTSize; + + CopyMemory(pData + nOffset, &m_pRawData[nStart], nBlockLen); + nOffset += nBlockLen; + + pData[nOffset] = 0x3B; // trailer + nOffset++; + + *pBlockLen = nOffset; + + return pData; + } + +#ifdef GIF_TRACING + void CAnimation::WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize) + { + CFile file; + + if(!file.Open(szFileName, CFile::modeCreate | CFile::modeWrite | CFile::shareDenyNone)) + { + OutputDebugString(_T("WriteData: Error creating file %s\n"), szFileName); + return; + }; + + char* pData = reinterpret_cast(GlobalLock(hData)); + if(!pData) + { + OutputDebugString(_T("WriteData: Error locking memory\n")); + return; + }; + + TRY + { + file.Write(pData, dwSize); + } + CATCH(CFileException, e); + { + OutputDebugString(_T("WriteData: An exception occured while writing to the file %s\n"), szFileName); + e->Delete(); + GlobalUnlock(hData); + file.Close(); + return; + } + END_CATCH GlobalUnlock(hData); + file.Close(); + } + + void CAnimation::EnumGIFBlocks() + { + enum GIFBlockTypes nBlock; + ResetDataPointer(); + while(m_nCurrOffset < m_nDataSize) + { + nBlock = GetNextBlock(); + switch(nBlock) + { + case BLOCK_UNKNOWN: + OutputDebugString(_T("- Unknown block\n")); + return; + break; + case BLOCK_TRAILER: + OutputDebugString(_T("- Trailer block\n")); + break; + case BLOCK_APPEXT: + OutputDebugString(_T("- Application extension block\n")); + break; + case BLOCK_COMMEXT: + OutputDebugString(_T("- Comment extension block\n")); + break; + case BLOCK_CONTROLEXT: + { + TGIFControlExt* pControl = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + OutputDebugString(_T("- Graphic control extension block (delay %d, disposal %d)\n"), + pControl->m_wDelayTime, + pControl->GetPackedValue(GCX_PACKED_DISPOSAL)); + }; + break; + case BLOCK_PLAINTEXT: + OutputDebugString(_T("- Plain text extension block\n")); + break; + case BLOCK_IMAGE: + TGIFImageDescriptor* pIDescr = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + OutputDebugString(_T("- Image data block (%dx%d %d,%d)\n"), + pIDescr->m_wWidth, + pIDescr->m_wHeight, + pIDescr->m_wLeftPos, + pIDescr->m_wTopPos); + break; + }; + SkipNextBlock(); + }; + OutputDebugString(_T("\n")); + } +#endif // GIF_TRACING + + DWORD CAnimation::ThreadAnimation() + { + while(!m_bExitThread) + { + if (m_pCallback) m_pCallback->OnFrame(); + // wait until currframe is drawn. if currframe is not drawn over 1min + // this ani may be no use in the future. so exit this thread to stop + // the animation playing by herself. + if (::WaitForSingleObject(m_hDrawEvent, 60*1000) != WAIT_OBJECT_0) + { + m_bExitThread = true; + continue; + } + + // if the delay time is too short (like in old GIFs), wait for 100ms + if((*m_pvFrames)[m_nCurrFrame].m_nDelay < 5) + Sleep(100); + else + Sleep(10 * (*m_pvFrames)[m_nCurrFrame].m_nDelay); + + ::InterlockedIncrement(&m_nCurrFrame); + if(m_nCurrFrame == m_pvFrames->size()) + ::InterlockedExchange(&m_nCurrFrame, (LONG)0); + } + + return 0; + } + + DWORD __stdcall CAnimation::_ThreadAnimation(LPVOID pParam) + { + assert(pParam); + CAnimation* pAni = static_cast(pParam); + if (!pAni) + return 1; + + return pAni->ThreadAnimation(); + } + + //************************************************************************/ + //* CEUIAniHelper */ + //************************************************************************/ + + + bool CEUIAniHelper::DrawAniImage(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, const CDuiString& sImageName, \ + const CDuiString& sImageResType, RECT rcItem, RECT rcBmpPart, RECT rcCorner, DWORD dwMask, BYTE bFade, \ + bool bHole, bool bTiledX, bool bTiledY) + { + const TImageInfo* data = NULL; + if ( !(data=GetAniImage((LPCTSTR)sImageName)) && !(data=pManager->GetImage((LPCTSTR)sImageName)) ) + { + // new Image + // try GIF first + data = AddAniImage(pManager->GetPaintDC(), (LPCTSTR)sImageName, (LPCTSTR)sImageResType, dwMask); + if (!data) + { + // may be not GIF + if( sImageResType.IsEmpty() ) { + data = pManager->GetImageEx((LPCTSTR)sImageName, NULL, dwMask); + } + else { + data = pManager->GetImageEx((LPCTSTR)sImageName, (LPCTSTR)sImageResType, dwMask); + } + } + } + + if (!data) + return false; + + if( rcBmpPart.left == 0 && rcBmpPart.right == 0 && rcBmpPart.top == 0 && rcBmpPart.bottom == 0 ) { + rcBmpPart.right = data->nX; + rcBmpPart.bottom = data->nY; + } + if (rcBmpPart.right > data->nX) rcBmpPart.right = data->nX; + if (rcBmpPart.bottom > data->nY) rcBmpPart.bottom = data->nY; + + RECT rcTemp; + if( !::IntersectRect(&rcTemp, &rcItem, &rc) ) return true; + if( !::IntersectRect(&rcTemp, &rcItem, &rcPaint) ) return true; + + CRenderEngine::DrawImage(hDC, data->hBitmap, rcItem, rcPaint, rcBmpPart, rcCorner, data->alphaChannel, bFade, bHole, bTiledX, bTiledY); + + return true; + } + + bool CEUIAniHelper::DrawImageEx(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, + LPCTSTR pStrImage, LPCTSTR pStrModify) + { + if ((pManager == NULL) || (hDC == NULL)) return false; + + // 1aaa.jpg + // 2file='aaa.jpg' res='' restype='0' dest='0,0,0,0' source='0,0,0,0' corner='0,0,0,0' + // mask='#FF0000' fade='255' hole='false' xtiled='false' ytiled='false' + + CDuiString sImageName = pStrImage; + CDuiString sImageResType; + RECT rcItem = rc; + RECT rcBmpPart = {0}; + RECT rcCorner = {0}; + DWORD dwMask = 0; + BYTE bFade = 0xFF; + bool bHole = false; + bool bTiledX = false; + bool bTiledY = false; + + int image_count = 0; + + CDuiString sItem; + CDuiString sValue; + LPTSTR pstr = NULL; + + for( int i = 0; i < 2; ++i,image_count = 0 ) { + if( i == 1) + pStrImage = pStrModify; + + if( !pStrImage ) continue; + + while( *pStrImage != _T('\0') ) { + sItem.Empty(); + sValue.Empty(); + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + while( *pStrImage != _T('\0') && *pStrImage != _T('=') && *pStrImage > _T(' ') ) { + LPTSTR pstrTemp = ::CharNext(pStrImage); + while( pStrImage < pstrTemp) { + sItem += *pStrImage++; + } + } + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + if( *pStrImage++ != _T('=') ) break; + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + if( *pStrImage++ != _T('\'') ) break; + while( *pStrImage != _T('\0') && *pStrImage != _T('\'') ) { + LPTSTR pstrTemp = ::CharNext(pStrImage); + while( pStrImage < pstrTemp) { + sValue += *pStrImage++; + } + } + if( *pStrImage++ != _T('\'') ) break; + if( !sValue.IsEmpty() ) { + if( sItem == _T("file") || sItem == _T("res") ) { + if( image_count > 0 ) + DrawAniImage(hDC, pManager, rc, rcPaint, sImageName, sImageResType, + rcItem, rcBmpPart, rcCorner, dwMask, bFade, bHole, bTiledX, bTiledY); + + sImageName = sValue; + if( sItem == _T("file") ) + ++image_count; + } + else if( sItem == _T("restype") ) { + if( image_count > 0 ) + DrawAniImage(hDC, pManager, rc, rcPaint, sImageName, sImageResType, + rcItem, rcBmpPart, rcCorner, dwMask, bFade, bHole, bTiledX, bTiledY); + + sImageResType = sValue; + ++image_count; + } + else if( sItem == _T("dest") ) { + rcItem.left = rc.left + _tcstol(sValue.GetData(), &pstr, 10); ASSERT(pstr); + rcItem.top = rc.top + _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcItem.right = rc.left + _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + if (rcItem.right > rc.right) rcItem.right = rc.right; + rcItem.bottom = rc.top + _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + if (rcItem.bottom > rc.bottom) rcItem.bottom = rc.bottom; + } + else if( sItem == _T("source") ) { + rcBmpPart.left = _tcstol(sValue.GetData(), &pstr, 10); ASSERT(pstr); + rcBmpPart.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcBmpPart.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcBmpPart.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + } + else if( sItem == _T("corner") ) { + rcCorner.left = _tcstol(sValue.GetData(), &pstr, 10); ASSERT(pstr); + rcCorner.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcCorner.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcCorner.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + } + else if( sItem == _T("mask") ) { + if( sValue[0] == _T('#')) dwMask = _tcstoul(sValue.GetData() + 1, &pstr, 16); + else dwMask = _tcstoul(sValue.GetData(), &pstr, 16); + } + else if( sItem == _T("fade") ) { + bFade = (BYTE)_tcstoul(sValue.GetData(), &pstr, 10); + } + else if( sItem == _T("hole") ) { + bHole = (_tcscmp(sValue.GetData(), _T("true")) == 0); + } + else if( sItem == _T("xtiled") ) { + bTiledX = (_tcscmp(sValue.GetData(), _T("true")) == 0); + } + else if( sItem == _T("ytiled") ) { + bTiledY = (_tcscmp(sValue.GetData(), _T("true")) == 0); + } + } + if( *pStrImage++ != _T(' ') ) break; + } + } + + DrawAniImage(hDC, pManager, rc, rcPaint, sImageName, sImageResType, + rcItem, rcBmpPart, rcCorner, dwMask, bFade, bHole, bTiledX, bTiledY); + + return true; + } + + const TImageInfo* CEUIAniHelper::GetAniImage(LPCTSTR bitmap) + { + CAnimation* data = static_cast(m_mAniHash.Find(bitmap)); + if (data) + return data->GetCurImage(); + else + return NULL; + } + + const TImageInfo* CEUIAniHelper::AddAniImage(HDC hDC, LPCTSTR bitmap, LPCTSTR type/* = NULL*/, DWORD mask/* = 0*/) + { + CAnimation* pAniImage = new CAnimation(this); + if (!pAniImage->LoadGIF(bitmap, type, mask)) + { + delete pAniImage; + return NULL; + } + if ( !m_mAniHash.Insert(bitmap, pAniImage) ) + { + delete pAniImage; + return NULL; + } + + pAniImage->Play(); + return pAniImage->GetCurImage(); + } +} diff --git a/Demos/gamebox/AnimationHelper.h b/Demos/gamebox/AnimationHelper.h new file mode 100644 index 00000000..a1bd8aa8 --- /dev/null +++ b/Demos/gamebox/AnimationHelper.h @@ -0,0 +1,229 @@ +#ifndef _EUI_ANIMATION_HELPER__H__ +#define _EUI_ANIMATION_HELPER__H__ + +#include + +namespace DuiLib +{ + class IAnimationCallback + { + public: + virtual void OnFrame() = 0; + }; + + class CAnimation + { +#pragma pack(1) // turn byte alignment on + enum GIFBlockTypes + { + BLOCK_UNKNOWN, + BLOCK_APPEXT, + BLOCK_COMMEXT, + BLOCK_CONTROLEXT, + BLOCK_PLAINTEXT, + BLOCK_IMAGE, + BLOCK_TRAILER + }; + + enum ControlExtValues // graphic control extension packed field values + { + GCX_PACKED_DISPOSAL, // disposal method + GCX_PACKED_USERINPUT, + GCX_PACKED_TRANSPCOLOR + }; + + enum LSDPackedValues // logical screen descriptor packed field values + { + LSD_PACKED_GLOBALCT, + LSD_PACKED_CRESOLUTION, + LSD_PACKED_SORT, + LSD_PACKED_GLOBALCTSIZE + }; + + enum IDPackedValues // image descriptor packed field values + { + ID_PACKED_LOCALCT, + ID_PACKED_INTERLACE, + ID_PACKED_SORT, + ID_PACKED_LOCALCTSIZE + }; + + struct TGIFHeader // GIF header + { + char m_cSignature[3]; // Signature - Identifies the GIF Data Stream + // This field contains the fixed value 'GIF' + char m_cVersion[3]; // Version number. May be one of the following: + // "87a" or "89a" + }; + + struct TGIFLSDescriptor // Logical Screen Descriptor + { + WORD m_wWidth; // 2 bytes. Logical screen width + WORD m_wHeight; // 2 bytes. Logical screen height + unsigned char m_cPacked; // packed field + unsigned char m_cBkIndex; // 1 byte. Background color index + unsigned char m_cPixelAspect; // 1 byte. Pixel aspect ratio + inline int GetPackedValue(enum LSDPackedValues Value); + }; + + struct TGIFAppExtension // application extension block + { + unsigned char m_cExtIntroducer; // extension introducer (0x21) + unsigned char m_cExtLabel; // app. extension label (0xFF) + unsigned char m_cBlockSize; // fixed value of 11 + char m_cAppIdentifier[8]; // application identifier + char m_cAppAuth[3]; // application authentication code + }; + + struct TGIFControlExt // graphic control extension block + { + unsigned char m_cExtIntroducer; // extension introducer (0x21) + unsigned char m_cControlLabel; // control extension label (0xF9) + unsigned char m_cBlockSize; // fixed value of 4 + unsigned char m_cPacked; // packed field + WORD m_wDelayTime; // delay time + unsigned char m_cTColorIndex; // transparent color index + unsigned char m_cBlockTerm; // block terminator (0x00) + inline int GetPackedValue(enum ControlExtValues Value); + }; + + struct TGIFCommentExt // comment extension block + { + unsigned char m_cExtIntroducer; // extension introducer (0x21) + unsigned char m_cCommentLabel; // comment extension label (0xFE) + }; + + struct TGIFPlainTextExt // plain text extension block + { + unsigned char m_cExtIntroducer; // extension introducer (0x21) + unsigned char m_cPlainTextLabel; // text extension label (0x01) + unsigned char m_cBlockSize; // fixed value of 12 + WORD m_wLeftPos; // text grid left position + WORD m_wTopPos; // text grid top position + WORD m_wGridWidth; // text grid width + WORD m_wGridHeight; // text grid height + unsigned char m_cCellWidth; // character cell width + unsigned char m_cCellHeight; // character cell height + unsigned char m_cFgColor; // text foreground color index + unsigned char m_cBkColor; // text background color index + }; + + struct TGIFImageDescriptor // image descriptor block + { + unsigned char m_cImageSeparator; // image separator byte (0x2C) + WORD m_wLeftPos; // image left position + WORD m_wTopPos; // image top position + WORD m_wWidth; // image width + WORD m_wHeight; // image height + unsigned char m_cPacked; // packed field + inline int GetPackedValue(enum IDPackedValues Value); + }; +#pragma pack() // turn byte alignment off + struct TFrame // structure that keeps a single frame info + { + TImageInfo* m_pImage; // pointer to one frame image + SIZE m_frameSize; + SIZE m_frameOffset; + UINT m_nDelay; // delay (in 1/100s of a second) + UINT m_nDisposal; // disposal method + }; + typedef std::vector VTFRAME; + + public: + CAnimation(IAnimationCallback* pCallback); + ~CAnimation(); + + const TImageInfo* GetCurImage(); + // loads a picture from a file or a program resource + const TImageInfo* LoadGIF(LPCTSTR bitmap, LPCTSTR type = NULL, DWORD mask = 0); + // loads a picture from a memory block (allocated by malloc) + // Warning: this function DOES NOT free the memory, pointed to by pData + const TImageInfo* LoadGIF(const LPBYTE pData, DWORD dwSize, DWORD mask = 0); + SIZE GetSize() const; + int GetFrameCount() const; + COLORREF GetBkColor() const; + + bool Play(); // play the animation (just change m_nCurrFrame) + void Stop(); // stops animation + void UnLoad(); // stops animation plus releases all resources + bool IsPlaying() const; + + protected: + int GetNextBlockLen() const; + BOOL SkipNextBlock(); + BOOL SkipNextGraphicBlock(); + void ResetDataPointer(); + enum GIFBlockTypes GetNextBlock() const; + UINT GetSubBlocksLen(UINT nStartingOffset) const; + // create a single-frame GIF from one of frames, and put in a memeory block (allocated by malloc) + // Warning: we should free the memeory block, pointed to by return value + LPBYTE GetNextGraphicBlock(UINT* pBlockLen, UINT* pDelay, SIZE* pBlockSize, SIZE* pBlockOffset, UINT* pDisposal); +#ifdef GIF_TRACING + void EnumGIFBlocks(); + void WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize); +#endif // GIF_TRACING + + private: + unsigned char* m_pRawData; // ʱݴ涯ļ Ч + UINT m_nDataSize; // GIFļС + TGIFHeader* m_pGIFHeader; // GIFļͷ + TGIFLSDescriptor* m_pGIFLSDescriptor; // ߼Ļʶ + UINT m_nGlobalCTSize; // ȫɫбС + SIZE m_PictureSize; // ͼߴ + COLORREF m_clrBackground; // ɫ + volatile long m_nCurrFrame; // ǰֵ֡ + UINT m_nCurrOffset; // ȡƫ + VTFRAME* m_pvFrames; // ֡ + + HANDLE m_hThread; + HANDLE m_hDrawEvent; // ͼ¼ ǰ֡Ѿȡڻ ֹȾٶ֡ + volatile bool m_bExitThread; + IAnimationCallback* m_pCallback; + + DWORD ThreadAnimation(); + static DWORD __stdcall _ThreadAnimation(LPVOID pParam); + }; + + class CEUIAniHelper : public IAnimationCallback + { + public: + ~CEUIAniHelper() + { + CAnimation* data = NULL; + while (m_mAniHash.GetSize() > 0) + { + if(LPCTSTR key = m_mAniHash.GetAt(0)) { + data = static_cast(m_mAniHash.Find(key)); + if (data) + { + delete data; + data = NULL; + } + m_mAniHash.Remove(key); + } + } + } + /** + * ͼǿ ԭCControlUI::DrawImage CRenderEngine::DrawImageString + * Note: PaintXXImage + */ + bool DrawImageEx(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, + LPCTSTR pStrImage, LPCTSTR pStrModify = NULL); + /** + * ͼǿ ԭDuiLib::DrawImage + * Note: жļ͡ʼȡGIFݵ + */ + bool DrawAniImage(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, const CDuiString& sImageName, \ + const CDuiString& sImageResType, RECT rcItem, RECT rcBmpPart, RECT rcCorner, DWORD dwMask, BYTE bFade, \ + bool bHole, bool bTiledX, bool bTiledY); + + protected: + const TImageInfo* GetAniImage(LPCTSTR bitmap); + const TImageInfo* AddAniImage(HDC hDC, LPCTSTR bitmap, LPCTSTR type = NULL, DWORD mask = 0); + + protected: + CStdStringPtrMap m_mAniHash; + }; +} + +#endif diff --git a/Demos/gamebox/ControlEx.h b/Demos/gamebox/ControlEx.h new file mode 100644 index 00000000..8bb77c4b --- /dev/null +++ b/Demos/gamebox/ControlEx.h @@ -0,0 +1,340 @@ +#ifndef __CONTROLEX_H__ +#define __CONTROLEX_H__ + +#include +#include +#include "LableEx.h" +#include "GameItemUI.h" +#include "ShortCutUI.h" +inline double CalculateDelay(double state) { + return pow(state, 2); +} + +// category(0)->game(1) +class GameListUI : public CListUI +{ +public: + enum { SCROLL_TIMERID = 10 }; + + struct NodeData + { + int id; + int _level; + bool _expand; + CDuiString _text; + CListLabelElementUI* _pListElement; + }; + + class Node + { + typedef std::vector Children; + Children _children; + Node* _parent; + NodeData _data; + + private: + void set_parent(Node* parent) { _parent = parent; } + + public: + Node() : _parent (NULL) {} + explicit Node(NodeData t) : _data (t), _parent (NULL) {} + Node(NodeData t, Node* parent) : _data (t), _parent (parent) {} + ~Node() + { + for (int i = 0; i < num_children(); i++) + delete _children[i]; + } + NodeData& data() { return _data; } + int num_children() const { return _children.size(); } + Node* child(int i) { return _children[i]; } + Node* parent() { return ( _parent); } + bool has_children() const { return num_children() > 0; } + void add_child(Node* child) + { + child->set_parent(this); + _children.push_back(child); + } + void remove_child(Node* child) + { + Children::iterator iter = _children.begin(); + for( ; iter < _children.end(); ++iter ) + { + if( *iter == child ) + { + _children.erase(iter); + return; + } + } + } + Node* get_last_child() + { + if( has_children() ) + { + return child(num_children() - 1)->get_last_child(); + } + else return this; + } + }; + + GameListUI() : _root(NULL), m_dwDelayDeltaY(0), m_dwDelayNum(0), m_dwDelayLeft(0) + { + SetItemShowHtml(true); + + _root = new Node; + _root->data()._level = -1; + _root->data()._expand = true; + _root->data()._pListElement = NULL; + } + + ~GameListUI() { if(_root) delete _root; } + + bool Add(CControlUI* pControl) + { + if( !pControl ) return false; + if( _tcscmp(pControl->GetClass(), _T("ListLabelElementUI")) != 0 ) return false; + return CListUI::Add(pControl); + } + + bool AddAt(CControlUI* pControl, int iIndex) + { + if( !pControl ) return false; + if( _tcscmp(pControl->GetClass(), _T("ListLabelElementUI")) != 0 ) return false; + return CListUI::AddAt(pControl, iIndex); + } + + bool Remove(CControlUI* pControl) + { + if( !pControl ) return false; + if( _tcscmp(pControl->GetClass(), _T("ListLabelElementUI")) != 0 ) return false; + + if (reinterpret_cast(static_cast(pControl->GetInterface(_T("ListLabelElement")))->GetTag()) == NULL) + return CListUI::Remove(pControl); + else + return RemoveNode(reinterpret_cast(static_cast(pControl->GetInterface(_T("ListLabelElement")))->GetTag())); + } + + bool RemoveAt(int iIndex) + { + CControlUI* pControl = GetItemAt(iIndex); + if( !pControl ) return false; + if( _tcscmp(pControl->GetClass(), _T("ListLabelElementUI")) != 0 ) return false; + + if (reinterpret_cast(static_cast(pControl->GetInterface(_T("ListLabelElement")))->GetTag()) == NULL) + return CListUI::RemoveAt(iIndex); + else + return RemoveNode(reinterpret_cast(static_cast(pControl->GetInterface(_T("ListLabelElement")))->GetTag())); + } + + void RemoveAll() + { + CListUI::RemoveAll(); + for (int i = 0; i < _root->num_children(); ++i) + { + Node* child = _root->child(i); + RemoveNode(child); + } + delete _root; + _root = new Node; + _root->data()._level = -1; + _root->data()._expand = true; + _root->data()._pListElement = NULL; + } + void SetVisible(bool bVisible = true) + { + if( m_bVisible == bVisible ) return; + CControlUI::SetVisible(bVisible); + } + + void SetInternVisible(bool bVisible = true) + { + CControlUI::SetInternVisible(bVisible); + } + + void DoEvent(TEventUI& event) + { + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pParent != NULL ) m_pParent->DoEvent(event); + else CVerticalLayoutUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_TIMER && event.wParam == SCROLL_TIMERID ) { + if( m_dwDelayLeft > 0 ) { + --m_dwDelayLeft; + SIZE sz = GetScrollPos(); + LONG lDeltaY = (LONG)(CalculateDelay((double)m_dwDelayLeft / m_dwDelayNum) * m_dwDelayDeltaY); + if( (lDeltaY > 0 && sz.cy != 0) || (lDeltaY < 0 && sz.cy != GetScrollRange().cy ) ) { + sz.cy -= lDeltaY; + SetScrollPos(sz); + return; + } + } + m_dwDelayDeltaY = 0; + m_dwDelayNum = 0; + m_dwDelayLeft = 0; + m_pManager->KillTimer(this, SCROLL_TIMERID); + return; + } + if( event.Type == UIEVENT_SCROLLWHEEL ) { + LONG lDeltaY = 0; + if( m_dwDelayNum > 0 ) lDeltaY = (LONG)(CalculateDelay((double)m_dwDelayLeft / m_dwDelayNum) * m_dwDelayDeltaY); + switch( LOWORD(event.wParam) ) { + case SB_LINEUP: + if( m_dwDelayDeltaY >= 0 ) m_dwDelayDeltaY = lDeltaY + 8; + else m_dwDelayDeltaY = lDeltaY + 12; + break; + case SB_LINEDOWN: + if( m_dwDelayDeltaY <= 0 ) m_dwDelayDeltaY = lDeltaY - 8; + else m_dwDelayDeltaY = lDeltaY - 12; + break; + } + if( m_dwDelayDeltaY > 100 ) m_dwDelayDeltaY = 100; + else if( m_dwDelayDeltaY < -100 ) m_dwDelayDeltaY = -100; + m_dwDelayNum = (DWORD)sqrt((double)abs(m_dwDelayDeltaY)) * 5; + m_dwDelayLeft = m_dwDelayNum; + m_pManager->SetTimer(this, SCROLL_TIMERID, 50U); + return; + } + + CListUI::DoEvent(event); + } + + Node* GetRoot() { return _root; } + + Node* AddNode(LPCTSTR text, int id, Node* parent = NULL) + { + if( !parent ) parent = _root; + + CListLabelElementUI* pListElement = new CListLabelElementUI; + Node* node = new Node; + node->data().id = id; + node->data()._level = parent->data()._level + 1; + if( node->data()._level == 0 ) node->data()._expand = true; + else node->data()._expand = false; + node->data()._text = text; + node->data()._pListElement = pListElement; + + if( parent != _root ) { + if( !(parent->data()._expand && parent->data()._pListElement->IsVisible()) ) + pListElement->SetInternVisible(false); + } + + CDuiString html_text; + html_text += _T(""); + for( int i = 0; i < node->data()._level; ++i ) { + html_text += _T(""); + } + if( node->data()._level < 1 ) { + if( node->data()._expand ) html_text += _T(""); + else html_text += _T(""); + } + html_text += node->data()._text; + pListElement->SetText(html_text); + pListElement->SetTag((UINT_PTR)node); + if( node->data()._level == 0 ) { + pListElement->SetBkImage(_T("file='tree_top.png' corner='2,1,2,1' fade='100'")); + } + + int index = 0; + if( parent->has_children() ) { + Node* prev = parent->get_last_child(); + index = prev->data()._pListElement->GetIndex() + 1; + } + else { + if( parent == _root ) index = 0; + else index = parent->data()._pListElement->GetIndex() + 1; + } + if( !CListUI::AddAt(pListElement, index) ) { + delete pListElement; + delete node; + node = NULL; + } + parent->add_child(node); + return node; + } + + bool RemoveNode(Node* node) + { + if( !node || node == _root ) return false; + for( int i = 0; i < node->num_children(); ++i ) { + Node* child = node->child(i); + RemoveNode(child); + } + CListUI::Remove(node->data()._pListElement); + node->parent()->remove_child(node); + delete node; + return true; + } + + void ExpandNode(Node* node, bool expand) + { + if( !node || node == _root ) return; + + if( node->data()._expand == expand ) return; + node->data()._expand = expand; + + CDuiString html_text; + html_text += _T(""); + for( int i = 0; i < node->data()._level; ++i ) { + html_text += _T(""); + } + if( node->data()._level < 1 ) { + if( node->data()._expand ) html_text += _T(""); + else html_text += _T(""); + } + html_text += node->data()._text; + node->data()._pListElement->SetText(html_text); + + if( !node->data()._pListElement->IsVisible() ) return; + if( !node->has_children() ) return; + + Node* begin = node->child(0); + Node* end = node->get_last_child(); + int nFirst = begin->data()._pListElement->GetIndex(); + for( int i = nFirst; i <= end->data()._pListElement->GetIndex(); ++i ) { + CControlUI* control = GetItemAt(i); + if( _tcscmp(control->GetClass(), _T("ListLabelElementUI")) == 0 ) { + + Node* local_parent = ((GameListUI::Node*)control->GetTag())->parent(); + control->SetInternVisible(local_parent->data()._expand && local_parent->data()._pListElement->IsVisible()); + } + } + + NeedUpdate(); + } + + SIZE GetExpanderSizeX(Node* node) const + { + if( !node || node == _root ) return CDuiSize(); + if( node->data()._level >= 3 ) return CDuiSize(); + + SIZE szExpander = {0}; + szExpander.cx = 6 + 24 * node->data()._level - 4/*ʵŴһ*/; + szExpander.cy = szExpander.cx + 16 + 8/*ʵŴһ*/; + return szExpander; + } + +private: + Node* _root; + + LONG m_dwDelayDeltaY; + DWORD m_dwDelayNum; + DWORD m_dwDelayLeft; +}; + +class CDialogBuilderCallbackEx : public IDialogBuilderCallback +{ +public: + CControlUI* CreateControl(LPCTSTR pstrClass) + { + if( _tcsicmp(pstrClass, _T("GameList")) == 0 ) return new GameListUI; + else if( _tcsicmp(pstrClass, _T("GameItem")) == 0 ) return new CGameItemUI; + else if( _tcsicmp(pstrClass, _T("ShortCut")) == 0 ) return new CShortCutUI; + else if( _tcsicmp(pstrClass, _T("LabelEx")) == 0 ) return new CLabelEx; + else if( _tcsicmp(pstrClass, _T("LabelMutiline")) == 0 ) return new CLabelMutiline; + return NULL; + } +}; + + +#endif __CONTROLEX_H__ diff --git a/Demos/gamebox/GameBox.cpp b/Demos/gamebox/GameBox.cpp new file mode 100644 index 00000000..347a00a8 --- /dev/null +++ b/Demos/gamebox/GameBox.cpp @@ -0,0 +1,603 @@ +#include "stdafx.h" +#include +#include +#include "ControlEx.h" +#include "SearchWnd.h" +#include "resource.h" + +class CGameBoxFrame : public CWindowWnd, public INotifyUI, public CWebBrowserEventHandler +{ +public: + CGameBoxFrame() { + + }; + +public: + void Init() { + m_pSearchWnd = new CSearchWnd(m_pm.GetRoot()); + m_pSearchWnd->Create(m_hWnd, _T("searchwnd"), WS_POPUP, WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE); + ::SetWindowPos(m_pSearchWnd->GetHWND(), NULL, 0,0,1, 1, SWP_NOACTIVATE); + m_pSearchWnd->ShowWindow(true); + + m_pCloseBtn = static_cast(m_pm.FindControl(_T("closebtn"))); + m_pMaxBtn = static_cast(m_pm.FindControl(_T("maxbtn"))); + m_pRestoreBtn = static_cast(m_pm.FindControl(_T("restorebtn"))); + m_pMinBtn = static_cast(m_pm.FindControl(_T("minbtn"))); + m_pGameList = static_cast(m_pm.FindControl(_T("gamelist"))); + m_pBaiduList = static_cast(m_pm.FindControl(_T("baidulist"))); + m_pFindList = static_cast(m_pm.FindControl(_T("findlist"))); + + //CWebBrowserUI* pBrowser = static_cast(m_pm.FindControl(_T("baidubrowser"))); + //pBrowser->SetWebBrowserEventHandler(this); + } + + void OnPrepare() { + for (int i = 0; i < 60; i++) + { + CDialogBuilderCallbackEx callback; + CDialogBuilder builder; + CContainerUI* pGameItem = static_cast(builder.Create(_T("gameitem.xml"), (UINT)0, &callback)); + m_pGameList->Add(pGameItem); + + CLabelUI *pIcon = static_cast(pGameItem->FindSubControl(_T("gameitem_icon"))); + CLabelUI *pText = static_cast(pGameItem->FindSubControl(_T("gameitem_text"))); + + //CDuiString sText; + //sText.Format(_T("Ϸ%d"), i); + //pText->SetText(sText); + } + + for (int i = 0; i < 6; i++) + { + CDialogBuilderCallbackEx callback; + CDialogBuilder builder; + CContainerUI* pGameItem = static_cast(builder.Create(_T("gameitem.xml"), (UINT)0, &callback)); + //m_pFindList->Add(pGameItem); + + CLabelUI *pIcon = static_cast(pGameItem->FindSubControl(_T("gameitem_icon"))); + CLabelUI *pText = static_cast(pGameItem->FindSubControl(_T("gameitem_text"))); + + //CDuiString sText; + //sText.Format(_T("-Ϸ%d"), i); + //pText->SetText(sText); + } + + // Ϸб + GameListUI::Node* pGameNode = NULL; + GameListUI::Node* pCategoryNode1 = AddCategoryNode(_T("һ"), 1); + GameListUI::Node* pCategoryNode2 = AddCategoryNode(_T(""), 1); + GameListUI::Node* pCategoryNode3 = AddCategoryNode(_T(""), 1); + GameListUI::Node* pCategoryNode4 = AddCategoryNode(_T(""), 1); + + for( int i = 0; i < 6; ++i ) + { + AddGameNode(_T("Ϸ"), pCategoryNode1, i); + AddGameNode(_T("Ϸ"), pCategoryNode2, i); + AddGameNode(_T("Ϸ"), pCategoryNode3, i); + AddGameNode(_T("Ϸ"), pCategoryNode4, i); + } + } + + void OnSearchEditChanged() + { + CEditUI* pEdit = static_cast(m_pm.FindControl(_T("search_edit"))); + RECT rcEdit = pEdit->GetPos(); + POINT ptPos = {rcEdit.left, rcEdit.bottom}; + ::ClientToScreen(m_hWnd, &ptPos); + SetWindowPos(m_pSearchWnd->GetHWND(), NULL, ptPos.x, ptPos.y, rcEdit.right - rcEdit.left, 200, SWP_NOACTIVATE); + if(!::IsWindowVisible(m_pSearchWnd->GetHWND())) + m_pSearchWnd->ShowWindow(true, false); + CDuiString sText = pEdit->GetText(); + + m_pSearchWnd->RemoveAll(); + + for(int i = 0; i<10; i++) + m_pSearchWnd->AddItem(sText); + } + + void OnLClick(CControlUI *pControl) + { + CDuiString sName = pControl->GetName(); + + if(sName.CompareNoCase(_T("foldbtn")) == 0) + { + pControl->SetVisible(false); + GameListUI* pGameList = static_cast(m_pm.FindControl(_T("categorylist"))); + GameListUI::Node *pRoot = pGameList->GetRoot(); + for(int i = 0; i < pRoot->num_children(); i++) + { + pGameList->ExpandNode(pRoot->child(i), false); + } + + CButtonUI* pUnFoldBtn = static_cast(m_pm.FindControl(_T("unfoldbtn"))); + pUnFoldBtn->SetVisible(true); + } + else if(sName.CompareNoCase(_T("unfoldbtn")) == 0) + { + pControl->SetVisible(false); + GameListUI* pGameList = static_cast(m_pm.FindControl(_T("categorylist"))); + GameListUI::Node *pRoot = pGameList->GetRoot(); + for(int i = 0; i < pRoot->num_children(); i++) + { + pGameList->ExpandNode(pRoot->child(i), true); + } + + CButtonUI* pFoldBtn = static_cast(m_pm.FindControl(_T("foldbtn"))); + pFoldBtn->SetVisible(true); + } + else if(sName.CompareNoCase(_T("searchbtn")) == 0) + { + SearchGame(); + } + } + + void SearchGame() + { + m_pSearchWnd->ShowWindow(false); + COptionUI *pBaiduBtn = static_cast(m_pm.FindControl(_T("baidu_tab"))); + pBaiduBtn->Selected(true); + CTabLayoutUI* pTab = static_cast(m_pm.FindControl(_T("switch"))); + pTab->SelectItem(1); + + CEditUI* pEdit = static_cast(m_pm.FindControl(_T("search_edit"))); + CDuiString sText = pEdit->GetText(); + + CDuiString sUrl; + sUrl.Format(_T("http://www.baidu.com/s?wd=%s"), sText); + + CWebBrowserUI* pBrowser = static_cast(m_pm.FindControl(_T("baidubrowser"))); + pBrowser->Navigate2(sUrl); + } + void OnSearchItemSelect(CControlUI *pControl) + { + CDuiString sName = pControl->GetName(); + + if(sName.CompareNoCase(_T("searchitem")) == 0) + { + m_pSearchWnd->ShowWindow(false); + COptionUI *pBaiduBtn = static_cast(m_pm.FindControl(_T("baidu_tab"))); + pBaiduBtn->Selected(true); + CTabLayoutUI* pTab = static_cast(m_pm.FindControl(_T("switch"))); + pTab->SelectItem(1); + + CContainerUI *pContainer = static_cast(pControl); + CDuiString sText = pContainer->FindSubControl(_T("searchitem_text"))->GetText(); + + CDuiString sUrl; + sUrl.Format(_T("http://www.baidu.com/s?wd=%s"), sText); + + CWebBrowserUI* pBrowser = static_cast(m_pm.FindControl(_T("baidubrowser"))); + pBrowser->Navigate2(sUrl); + } + } + + virtual HRESULT STDMETHODCALLTYPE UpdateUI( void) + { + m_pSearchWnd->ShowWindow(false); + return S_OK; + } + + void OnCategorySelect(GameListUI *pList, int nCurSel, int nOldSel) + { + CListLabelElementUI *pItem = (CListLabelElementUI*)pList->GetItemAt(nCurSel); + if( _tcscmp(pItem->GetClass(), _T("ListLabelElementUI")) == 0 ) { + GameListUI::Node* node = (GameListUI::Node*)pItem->GetTag(); + if( node->data()._level == 0 ) { + if(node->data()._expand) pList->SelectItem(nCurSel + 1); + } + else if( node->data()._level == 1 ) + { + COptionUI *pGameBtn = static_cast(m_pm.FindControl(_T("game_tab"))); + pGameBtn->Selected(true); + CTabLayoutUI* pTab = static_cast(m_pm.FindControl(_T("switch"))); + pTab->SelectItem(0); + + int nID = node->data().id; + m_pGameList->RemoveAll(); + + for (int i = 0; i < 10; i++) + { + CDialogBuilderCallbackEx callback; + CDialogBuilder builder; + CContainerUI* pGameItem = static_cast(builder.Create(_T("gameitem.xml"), (UINT)0, &callback)); + m_pGameList->Add(pGameItem); + + CLabelUI *pIcon = static_cast(pGameItem->FindSubControl(_T("gameitem_icon"))); + CLabelUI *pText = static_cast(pGameItem->FindSubControl(_T("gameitem_text"))); + + //CDuiString sText; + //sText.Format(_T("Ϸ%d_%d"), nID, i); + //pText->SetText(sText); + } + } + } + } +public: + GameListUI::Node* AddCategoryNode(CDuiString sText, int nID) + { + GameListUI* pGameList = static_cast(m_pm.FindControl(_T("categorylist"))); + + CDuiString sIcon = _T("gameicons.png"); + CDuiString sFormat; + sFormat.Format(_T("{x 4}{i %s 2 0}{x 4}%s"), sIcon.GetData(), sText.GetData()); + GameListUI::Node* pCategoryNode = pGameList->AddNode(sFormat, nID); + + return pCategoryNode; + } + + GameListUI::Node* AddGameNode(CDuiString sText, GameListUI::Node *Category, int nID) + { + GameListUI* pGameList = static_cast(m_pm.FindControl(_T("categorylist"))); + + CDuiString sIcon = _T("gameicons.png"); + CDuiString sFormat; + sFormat.Format(_T("{x 4}{i %s 2 1}{x 4}%s"), sIcon.GetData(), sText.GetData()); + + GameListUI::Node* pGameNode = pGameList->AddNode(sFormat, nID, Category); + + return pGameNode; + } + +public: + LPCTSTR GetWindowClassName() const { return _T("UIMainFrame"); }; + UINT GetClassStyle() const { return CS_DBLCLKS; }; + void OnFinalMessage(HWND /*hWnd*/) { delete this; }; + + void Notify(TNotifyUI& msg) + { + if( msg.sType == _T("windowinit") ) OnPrepare(); + else if( msg.sType == _T("click") ) { + if( msg.pSender == m_pCloseBtn ) { + PostQuitMessage(0); + return; + } + else if( msg.pSender == m_pMinBtn ) { + SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0); return; } + else if( msg.pSender == m_pMaxBtn ) { + SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, 0); return; } + else if( msg.pSender == m_pRestoreBtn ) { + SendMessage(WM_SYSCOMMAND, SC_RESTORE, 0); return; } + + // ťϢ + OnLClick(msg.pSender); + } + else if(msg.sType == _T("return")) + { + SearchGame(); + } + else if( msg.sType == _T("textchanged") ) { + if(msg.pSender->GetName().CompareNoCase(_T("search_edit")) == 0) + { + OnSearchEditChanged(); + } + } + else if(msg.sType==_T("selectchanged")) + { + CDuiString name = msg.pSender->GetName(); + CTabLayoutUI* pControl = static_cast(m_pm.FindControl(_T("switch"))); + if(name==_T("game_tab")) + pControl->SelectItem(0); + else if(name==_T("baidu_tab")) + pControl->SelectItem(1); + } + else if( msg.sType == _T("itemclick") ) { + GameListUI* pGameList = static_cast(m_pm.FindControl(_T("categorylist"))); + if( pGameList->GetItemIndex(msg.pSender) != -1 ) + { + if( _tcscmp(msg.pSender->GetClass(), _T("ListLabelElementUI")) == 0 ) { + GameListUI::Node* node = (GameListUI::Node*)msg.pSender->GetTag(); + + POINT pt = { 0 }; + ::GetCursorPos(&pt); + ::ScreenToClient(m_pm.GetPaintWindow(), &pt); + pt.x -= msg.pSender->GetX(); + SIZE sz = pGameList->GetExpanderSizeX(node); + if( pt.x >= sz.cx && pt.x < sz.cy ) + pGameList->ExpandNode(node, !node->data()._expand); + } + } + } + else if(msg.sType == _T("itemselect")) + { + GameListUI* pGameList = static_cast(m_pm.FindControl(_T("categorylist"))); + if( pGameList == msg.pSender ) + { + OnCategorySelect(pGameList, (int)msg.wParam, (int)msg.lParam); + } + else + { + OnSearchItemSelect(msg.pSender); + } + } + else if( msg.sType == _T("itemactivate") ) { + GameListUI* pGameList = static_cast(m_pm.FindControl(_T("categorylist"))); + if( pGameList->GetItemIndex(msg.pSender) != -1 ) + { + if( _tcscmp(msg.pSender->GetClass(), _T("ListLabelElementUI")) == 0 ) { + GameListUI::Node* node = (GameListUI::Node*)msg.pSender->GetTag(); + pGameList->ExpandNode(node, !node->data()._expand); + + if( node->data()._level == 0 && node->data()._expand) { + pGameList->SelectItem(pGameList->GetItemIndex(msg.pSender) + 1); + } + } + } + } + } + + LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + // ICONS ͼ + SetIcon(IDR_MAINFRAME); + + LONG styleValue = ::GetWindowLong(*this, GWL_STYLE); + styleValue &= ~WS_CAPTION; + ::SetWindowLong(*this, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); + + m_pm.Init(m_hWnd); + CDialogBuilder builder; + CDialogBuilderCallbackEx cb; + CControlUI* pRoot = builder.Create(_T("skin.xml"), (UINT)0, &cb, &m_pm); + ASSERT(pRoot && "Failed to parse XML"); + m_pm.AttachDialog(pRoot); + m_pm.AddNotifier(this); + + Init(); + return 0; + } + + LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + bHandled = FALSE; + return 0; + } + + LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + ::PostQuitMessage(0L); + + bHandled = FALSE; + return 0; + } + + LRESULT OnNcActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + if( ::IsIconic(*this) ) bHandled = FALSE; + return (wParam == 0) ? TRUE : FALSE; + } + + LRESULT OnNcCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + return 0; + } + + LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + return 0; + } + + LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + POINT pt; pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam); + ::ScreenToClient(*this, &pt); + + RECT rcClient; + ::GetClientRect(*this, &rcClient); + + if( !::IsZoomed(*this) ) { + RECT rcSizeBox = m_pm.GetSizeBox(); + if( pt.y < rcClient.top + rcSizeBox.top ) { + if( pt.x < rcClient.left + rcSizeBox.left ) return HTTOPLEFT; + if( pt.x > rcClient.right - rcSizeBox.right ) return HTTOPRIGHT; + return HTTOP; + } + else if( pt.y > rcClient.bottom - rcSizeBox.bottom ) { + if( pt.x < rcClient.left + rcSizeBox.left ) return HTBOTTOMLEFT; + if( pt.x > rcClient.right - rcSizeBox.right ) return HTBOTTOMRIGHT; + return HTBOTTOM; + } + if( pt.x < rcClient.left + rcSizeBox.left ) return HTLEFT; + if( pt.x > rcClient.right - rcSizeBox.right ) return HTRIGHT; + } + + RECT rcCaption = m_pm.GetCaptionRect(); + if( pt.x >= rcClient.left + rcCaption.left && pt.x < rcClient.right - rcCaption.right \ + && pt.y >= rcCaption.top && pt.y < rcCaption.bottom ) { + CControlUI* pControl = static_cast(m_pm.FindControl(pt)); + if( pControl && _tcscmp(pControl->GetClass(), _T("ButtonUI")) != 0 && + _tcscmp(pControl->GetClass(), _T("OptionUI")) != 0 && + _tcscmp(pControl->GetClass(), _T("TextUI")) != 0 ) + return HTCAPTION; + } + + return HTCLIENT; + } + + LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + SIZE szRoundCorner = m_pm.GetRoundCorner(); + if( !::IsIconic(*this) && (szRoundCorner.cx != 0 || szRoundCorner.cy != 0) ) { + CDuiRect rcWnd; + ::GetWindowRect(*this, &rcWnd); + rcWnd.Offset(-rcWnd.left, -rcWnd.top); + rcWnd.right++; rcWnd.bottom++; + HRGN hRgn = ::CreateRoundRectRgn(rcWnd.left, rcWnd.top, rcWnd.right, rcWnd.bottom, szRoundCorner.cx, szRoundCorner.cy); + ::SetWindowRgn(*this, hRgn, TRUE); + ::DeleteObject(hRgn); + } + + bHandled = FALSE; + return 0; + } + + LRESULT OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + MONITORINFO oMonitor = {}; + oMonitor.cbSize = sizeof(oMonitor); + ::GetMonitorInfo(::MonitorFromWindow(*this, MONITOR_DEFAULTTOPRIMARY), &oMonitor); + CDuiRect rcWork = oMonitor.rcWork; + rcWork.Offset(-rcWork.left, -rcWork.top); + + LPMINMAXINFO lpMMI = (LPMINMAXINFO) lParam; + lpMMI->ptMaxPosition.x = rcWork.left; + lpMMI->ptMaxPosition.y = rcWork.top; + lpMMI->ptMaxSize.x = rcWork.right; + lpMMI->ptMaxSize.y = rcWork.bottom; + + bHandled = FALSE; + return 0; + } + + LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + // ʱյWM_NCDESTROYյwParamΪSC_CLOSEWM_SYSCOMMAND + if( wParam == SC_CLOSE ) { + ::PostQuitMessage(0L); + bHandled = TRUE; + return 0; + } + BOOL bZoomed = ::IsZoomed(*this); + LRESULT lRes = CWindowWnd::HandleMessage(uMsg, wParam, lParam); + if( ::IsZoomed(*this) != bZoomed ) { + if( !bZoomed ) { + CControlUI* pControl = static_cast(m_pm.FindControl(_T("maxbtn"))); + if( pControl ) pControl->SetVisible(false); + pControl = static_cast(m_pm.FindControl(_T("restorebtn"))); + if( pControl ) pControl->SetVisible(true); + } + else { + CControlUI* pControl = static_cast(m_pm.FindControl(_T("maxbtn"))); + if( pControl ) pControl->SetVisible(true); + pControl = static_cast(m_pm.FindControl(_T("restorebtn"))); + if( pControl ) pControl->SetVisible(false); + } + } + return lRes; + } + + LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + LRESULT lRes = 0; + BOOL bHandled = TRUE; + switch( uMsg ) { + case WM_CREATE: lRes = OnCreate(uMsg, wParam, lParam, bHandled); break; + case WM_CLOSE: lRes = OnClose(uMsg, wParam, lParam, bHandled); break; + case WM_DESTROY: lRes = OnDestroy(uMsg, wParam, lParam, bHandled); break; + case WM_NCACTIVATE: lRes = OnNcActivate(uMsg, wParam, lParam, bHandled); break; + case WM_NCCALCSIZE: lRes = OnNcCalcSize(uMsg, wParam, lParam, bHandled); break; + case WM_NCPAINT: lRes = OnNcPaint(uMsg, wParam, lParam, bHandled); break; + case WM_NCHITTEST: lRes = OnNcHitTest(uMsg, wParam, lParam, bHandled); break; + case WM_SIZE: lRes = OnSize(uMsg, wParam, lParam, bHandled); break; + case WM_GETMINMAXINFO: lRes = OnGetMinMaxInfo(uMsg, wParam, lParam, bHandled); break; + case WM_SYSCOMMAND: lRes = OnSysCommand(uMsg, wParam, lParam, bHandled); break; + case WM_LBUTTONDOWN: + case WM_NCLBUTTONDOWN: + { + m_pSearchWnd->ShowWindow(false); + bHandled = FALSE; + break; + } + case WM_ACTIVATEAPP: + { + if((BOOL)wParam == FALSE) + { + m_pSearchWnd->ShowWindow(false); + } + bHandled = FALSE; + break; + } + case WM_SHOWWINDOW: + { + bHandled = FALSE; + + break; + } + case WM_MOUSEWHEEL: + { + POINT ptMouse; + GetCursorPos(&ptMouse); + HWND hWnd = WindowFromPoint(ptMouse); + + if(::IsWindow(hWnd) && hWnd == m_pSearchWnd->GetHWND()) + { + ::SendMessage(hWnd, WM_MOUSEWHEEL, wParam, lParam); + } + else + { + bHandled = FALSE; + } + break; + } + default: + bHandled = FALSE; + } + if( bHandled ) return lRes; + if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes; + return CWindowWnd::HandleMessage(uMsg, wParam, lParam); + } + +public: + CPaintManagerUI m_pm; + +private: + CButtonUI* m_pCloseBtn; + CButtonUI* m_pMaxBtn; + CButtonUI* m_pRestoreBtn; + CButtonUI* m_pMinBtn; + CTileLayoutUI* m_pGameList; + CTileLayoutUI* m_pBaiduList; + CHorizontalLayoutUI* m_pFindList; + + CSearchWnd *m_pSearchWnd; + //... +}; + +static LPBYTE resource_zip_buffer_ = NULL; + +//#define USE_EMBEDED_RESOURCE + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow) +{ + CPaintManagerUI::SetInstance(hInstance); + +#ifdef USE_EMBEDED_RESOURCE + HRSRC hResource = ::FindResource(CPaintManagerUI::GetResourceDll(), MAKEINTRESOURCE(IDR_ZIPRES), _T("ZIPRES")); + if( hResource == NULL ) + return 0L; + DWORD dwSize = 0; + HGLOBAL hGlobal = ::LoadResource(CPaintManagerUI::GetResourceDll(), hResource); + if( hGlobal == NULL ) { + FreeResource(hResource); + return 0L; + } + dwSize = ::SizeofResource(CPaintManagerUI::GetResourceDll(), hResource); + if( dwSize == 0 ) + return 0L; + resource_zip_buffer_ = new BYTE[ dwSize ]; + if (resource_zip_buffer_ != NULL) + { + ::CopyMemory(resource_zip_buffer_, (LPBYTE)::LockResource(hGlobal), dwSize); + } + + ::FreeResource(hResource); + CPaintManagerUI::SetResourceZip(resource_zip_buffer_, dwSize); +#else + CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath() + _T("skin\\GameBox"));// + //CPaintManagerUI::SetResourceZip(_T("gamebox.zip")); +#endif + + + HRESULT Hr = ::CoInitialize(NULL); + if( FAILED(Hr) ) return 0; + + CGameBoxFrame* pFrame = new CGameBoxFrame(); + if( pFrame == NULL ) return 0; + pFrame->Create(NULL, _T("Ϸ"), UI_WNDSTYLE_FRAME, 0L, 0, 0, 800, 572); + pFrame->CenterWindow(); + ::ShowWindow(*pFrame, SW_SHOW); + + CPaintManagerUI::MessageLoop(); + + ::CoUninitialize(); + return 0; +} \ No newline at end of file diff --git a/Demos/gamebox/GameBox.rc b/Demos/gamebox/GameBox.rc new file mode 100644 index 00000000..9a2b80dc Binary files /dev/null and b/Demos/gamebox/GameBox.rc differ diff --git a/Demos/gamebox/GameBox.vcxproj b/Demos/gamebox/GameBox.vcxproj new file mode 100644 index 00000000..deddddba --- /dev/null +++ b/Demos/gamebox/GameBox.vcxproj @@ -0,0 +1,188 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + GameBox + {3EF9795D-3509-4664-8E73-8D315CC0B944} + 360 + + + + + + + + + + + + Application + false + Unicode + + + Application + false + Unicode + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)bin\ + $(SolutionDir)temp\gamebox\$(Configuration)\ + true + $(SolutionDir)bin\ + $(SolutionDir)temp\gamebox\$(Configuration)\ + false + AllRules.ruleset + + + AllRules.ruleset + + + $(ProjectName)_d + $(ProjectName) + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/GameBox.tlb + + + + + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + Use + stdafx.h + true + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x0406 + + + true + true + true + Windows + MachineX86 + + + true + .\Debug/GameBox.bsc + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/GameBox.tlb + + + + + MinSpace + OnlyExplicitInline + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreaded + true + Use + stdafx.h + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0406 + + + true + Windows + MachineX86 + libcmt.lib + + + true + .\Release/GameBox.bsc + + + + + + + + + NotUsing + NotUsing + + + %(PreprocessorDefinitions) + Create + %(PreprocessorDefinitions) + Create + + + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demos/gamebox/GameBox.vcxproj.filters b/Demos/gamebox/GameBox.vcxproj.filters new file mode 100644 index 00000000..fd3f588a --- /dev/null +++ b/Demos/gamebox/GameBox.vcxproj.filters @@ -0,0 +1,96 @@ + + + + + {c75b9d7c-452a-4867-a585-e8aee4a05f51} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {249f81db-516d-4a60-9882-d3fd674e412c} + h;hpp;hxx;hm;inl + + + {057557f5-517e-47cf-a695-8b482beaf54a} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + + + Resources + + + \ No newline at end of file diff --git a/Demos/gamebox/GameBox.vcxproj.user b/Demos/gamebox/GameBox.vcxproj.user new file mode 100644 index 00000000..ace9a86a --- /dev/null +++ b/Demos/gamebox/GameBox.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Demos/gamebox/GameItemUI.cpp b/Demos/gamebox/GameItemUI.cpp new file mode 100644 index 00000000..626550f8 --- /dev/null +++ b/Demos/gamebox/GameItemUI.cpp @@ -0,0 +1,52 @@ +#include "StdAfx.h" +#include "GameItemUI.h" + +CGameItemUI::CGameItemUI() +{ +} + + +CGameItemUI::~CGameItemUI(void) +{ + if(m_hIcon != NULL) + ::DestroyIcon(m_hIcon); +} + +void CGameItemUI::DoEvent(DuiLib::TEventUI& event) +{ + if(event.Type == DuiLib::UIEVENT_DBLCLICK) + { + m_pManager->SendNotify(this, _T("ldbclick"), event.wParam, event.lParam); + m_pManager->ReleaseCapture(); + } + + if(event.Type == DuiLib::UIEVENT_SETCURSOR) + { + ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); + return; + } + + return COptionUI::DoEvent(event); +} + +void CGameItemUI::PaintStatusImage(HDC hDC) +{ + COptionUI::PaintStatusImage(hDC); + if(m_hIcon != NULL) + { + ICONINFO ii; + if(::GetIconInfo(m_hIcon, &ii)) + { + BITMAP bmp; + if(::GetObject(ii.hbmColor, sizeof(bmp), &bmp)) + ::DrawIcon(hDC, m_rcItem.left+(m_rcItem.right-m_rcItem.left-bmp.bmWidth)/2, m_rcItem.top+4, m_hIcon); + ::DeleteObject(ii.hbmColor); + ::DeleteObject(ii.hbmMask); + } + } +} + +void CGameItemUI::SetIcon(HICON hIcon) +{ + m_hIcon = hIcon; +} diff --git a/Demos/gamebox/GameItemUI.h b/Demos/gamebox/GameItemUI.h new file mode 100644 index 00000000..b090277b --- /dev/null +++ b/Demos/gamebox/GameItemUI.h @@ -0,0 +1,20 @@ +#pragma once +#include "LableEx.h" + +class CGameItemUI : public DuiLib::COptionUI +{ +public: + CGameItemUI(); + ~CGameItemUI(void); + +public: + void DoEvent(DuiLib::TEventUI& event); + virtual void PaintStatusImage(HDC hDC); + +public: + void SetIcon(HICON hIcon); + +private: + HICON m_hIcon; +}; + diff --git a/Demos/gamebox/LableEx.h b/Demos/gamebox/LableEx.h new file mode 100644 index 00000000..c0ee3a93 --- /dev/null +++ b/Demos/gamebox/LableEx.h @@ -0,0 +1,130 @@ +#ifndef __LABELEX_H__ +#define __LABELEX_H__ +#include "AnimationHelper.h" + +namespace DuiLib +{ + class CLabelEx : public CLabelUI, public CEUIAniHelper + { + public: + void CLabelEx::PaintBkImage(HDC hDC) + { + if( m_sBkImage.IsEmpty() ) + return; + if( !DrawImageEx(hDC, m_pManager, m_rcItem, m_rcPaint, (LPCTSTR)m_sBkImage) ) + m_sBkImage.Empty(); + } + + void CLabelEx::OnFrame() + { + Invalidate(); + } + + void PaintText(HDC hDC) + { + if( m_dwTextColor == 0 ) m_dwTextColor = m_pManager->GetDefaultFontColor(); + if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor(); + + if( m_sText.IsEmpty() ) return; + int nLinks = 0; + RECT rc = m_rcItem; + rc.left += m_rcTextPadding.left; + rc.right -= m_rcTextPadding.right; + rc.top += m_rcTextPadding.top; + rc.bottom -= m_rcTextPadding.bottom; + if( IsEnabled() ) { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + NULL, NULL, nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + m_iFont, m_uTextStyle); + } + else { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + NULL, NULL, nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + m_iFont, m_uTextStyle); + } + } + }; + + class CLabelIcon: public DuiLib::CLabelUI + { + public: + CLabelIcon() + { + m_hIcon = NULL; + } + ~CLabelIcon(void) + { + if(m_hIcon != NULL) + ::DestroyIcon(m_hIcon); + } + + public: + virtual void PaintStatusImage(HDC hDC) + { + CLabelUI::PaintStatusImage(hDC); + if(m_hIcon != NULL) + { + ICONINFO ii; + if(::GetIconInfo(m_hIcon, &ii)) + { + BITMAP bmp; + if(::GetObject(ii.hbmColor, sizeof(bmp), &bmp)) + ::DrawIcon(hDC, m_rcItem.left+(m_rcItem.right-m_rcItem.left-bmp.bmWidth)/2, m_rcItem.top+4, m_hIcon); + ::DeleteObject(ii.hbmColor); + ::DeleteObject(ii.hbmMask); + } + } + } + + public: + void SetIcon(HICON hIcon) + { + m_hIcon = hIcon; + } + + private: + HICON m_hIcon; + }; + + class CLabelMutiline: public CLabelUI + { + public: + void PaintText(HDC hDC) + { + if( m_dwTextColor == 0 ) m_dwTextColor = m_pManager->GetDefaultFontColor(); + if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor(); + + if( m_sText.IsEmpty() ) return; + int nLinks = 0; + RECT rc = m_rcItem; + rc.left += m_rcTextPadding.left; + rc.right -= m_rcTextPadding.right; + rc.top += m_rcTextPadding.top; + rc.bottom -= m_rcTextPadding.bottom; + if( IsEnabled() ) { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + NULL, NULL, nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + m_iFont, m_uTextStyle); + } + else { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + NULL, NULL, nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + m_iFont, m_uTextStyle); + } + } + }; +} + +#endif __LABELEX_H__ diff --git a/Demos/gamebox/Res/GameBox.zip b/Demos/gamebox/Res/GameBox.zip new file mode 100644 index 00000000..cd154a2a Binary files /dev/null and b/Demos/gamebox/Res/GameBox.zip differ diff --git a/Demos/gamebox/Res/gamebox.ico b/Demos/gamebox/Res/gamebox.ico new file mode 100644 index 00000000..75c24464 Binary files /dev/null and b/Demos/gamebox/Res/gamebox.ico differ diff --git a/Demos/gamebox/SearchWnd.h b/Demos/gamebox/SearchWnd.h new file mode 100644 index 00000000..50697e77 --- /dev/null +++ b/Demos/gamebox/SearchWnd.h @@ -0,0 +1,132 @@ +#ifndef __SEARCHWND_H__ +#define __SEARCHWND_H__ + +class CSearchWnd : public CWindowWnd, public INotifyUI +{ +public: + CSearchWnd(CControlUI *pOwner) : m_pOwner(pOwner) { } + +public: + void ShowWindow(bool bShow /* = true */, bool bTakeFocus = true ) + { + CWindowWnd::ShowWindow(bShow, bTakeFocus); + AdjustPos(); + } + + void AddItem(CDuiString sItem) + { + CListUI* pGameList = static_cast(m_pm.FindControl(_T("searchlist"))); + if(pGameList != NULL) + { + CDialogBuilder builder; + CListContainerElementUI* pGameItem = static_cast(builder.Create(_T("searchitem.xml"), (UINT)0)); + CControlUI *pText = pGameItem->FindSubControl(_T("searchitem_text")); + pGameList->Add(pGameItem); + pText->SetText(sItem); + + AdjustPos(); + } + } + + void RemoveAll() + { + CListUI* pGameList = static_cast(m_pm.FindControl(_T("searchlist"))); + if(pGameList != NULL) + { + pGameList->RemoveAll(); + AdjustPos(); + } + } + + void AdjustPos() + { + CListUI* pGameList = static_cast(m_pm.FindControl(_T("searchlist"))); + if(pGameList != NULL) + { + int nHeight = 0; + for (int i = 0; i< pGameList->GetCount(); i++) + { + CControlUI *pItem = pGameList->GetItemAt(i); + + if(pItem != NULL) nHeight += pItem->GetFixedHeight(); + } + + if(nHeight > 200) nHeight = 200; + RECT rcWnd; + GetClientRect(m_hWnd, &rcWnd); + SetWindowPos(m_hWnd, NULL, 0, 0, rcWnd.right - rcWnd.left, nHeight, SWP_NOACTIVATE | SWP_NOMOVE); + } + } +public: + LPCTSTR GetWindowClassName() const { return _T("MenuWnd"); } + void OnFinalMessage(HWND /*hWnd*/) { delete this; } + + void Notify(TNotifyUI& msg) + { + if( msg.sType == _T("itemselect") ) { + ShowWindow(false); + CListUI* pGameList = static_cast(m_pm.FindControl(_T("searchlist"))); + if(pGameList != NULL) + { + if( m_pOwner ) m_pOwner->GetManager()->SendNotify(pGameList->GetItemAt(pGameList->GetCurSel()), _T("itemselect"), pGameList->GetCurSel(), 0, true); + } + } + } + + HWND Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x = CW_USEDEFAULT, int y = CW_USEDEFAULT, int cx = CW_USEDEFAULT, int cy = CW_USEDEFAULT, HMENU hMenu = NULL) + { + dwExStyle |= WS_EX_TOOLWINDOW; + return CWindowWnd::Create(hwndParent, pstrName, dwStyle, dwExStyle, x, y, cx, cy, hMenu); + } + + LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + m_pm.Init(m_hWnd); + CDialogBuilder builder; + CControlUI* pRoot = builder.Create(_T("search.xml"), (UINT)0, NULL, &m_pm); + ASSERT(pRoot && "Failed to parse XML"); + m_pm.AttachDialog(pRoot); + m_pm.AddNotifier(this); + m_pm.SetRoundCorner(3, 3); + + return 0; + } + + LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + if( (HWND)wParam == m_hWnd ) bHandled = TRUE; + else { + bHandled = FALSE; + } + return 0; + } + + LRESULT OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + if( wParam == VK_ESCAPE ) Close(); + return 0; + } + + LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + LRESULT lRes = 0; + BOOL bHandled = TRUE; + switch( uMsg ) { + case WM_CREATE: lRes = OnCreate(uMsg, wParam, lParam, bHandled); break; + case WM_KILLFOCUS: lRes = OnKillFocus(uMsg, wParam, lParam, bHandled); break; + case WM_KEYDOWN: lRes = OnKeyDown(uMsg, wParam, lParam, bHandled); break; + default: + bHandled = FALSE; + } + if( bHandled ) return lRes; + if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes; + return CWindowWnd::HandleMessage(uMsg, wParam, lParam); + } + +public: + CPaintManagerUI m_pm; + CControlUI* m_pOwner; + POINT m_ptPos; +}; + +#endif __SEARCHWND_H__ diff --git a/Demos/gamebox/ShortCutUI.cpp b/Demos/gamebox/ShortCutUI.cpp new file mode 100644 index 00000000..0b7c50db --- /dev/null +++ b/Demos/gamebox/ShortCutUI.cpp @@ -0,0 +1,41 @@ +#include "StdAfx.h" +#include "ShortCutUI.h" + + +CShortCutUI::CShortCutUI() +{ +} + + +CShortCutUI::~CShortCutUI(void) +{ + if(m_hIcon != NULL) + ::DestroyIcon(m_hIcon); +} + +void CShortCutUI::PaintStatusImage(HDC hDC) +{ + CButtonUI::PaintStatusImage(hDC); + if(m_hIcon != NULL) + { + ICONINFO ii; + if(::GetIconInfo(m_hIcon, &ii)) + { + BITMAP bmp; + if(::GetObject(ii.hbmColor, sizeof(bmp), &bmp)) + ::DrawIcon(hDC, m_rcItem.left, m_rcItem.top, m_hIcon); + ::DeleteObject(ii.hbmColor); + ::DeleteObject(ii.hbmMask); + } + } +} + +void CShortCutUI::SetText(LPCTSTR pstrText) +{ + m_sText = pstrText; +} + +void CShortCutUI::SetIcon(HICON hIcon) +{ + m_hIcon = hIcon; +} diff --git a/Demos/gamebox/ShortCutUI.h b/Demos/gamebox/ShortCutUI.h new file mode 100644 index 00000000..d8f1aaa3 --- /dev/null +++ b/Demos/gamebox/ShortCutUI.h @@ -0,0 +1,19 @@ +#pragma once + +class CShortCutUI : public DuiLib::CButtonUI +{ +public: + CShortCutUI(); + ~CShortCutUI(void); + +public: + virtual void PaintStatusImage(HDC hDC); + virtual void SetText(LPCTSTR pstrText); + +public: + void SetIcon(HICON hIcon); + +private: + HICON m_hIcon; +}; + diff --git a/Demos/gamebox/StdAfx.cpp b/Demos/gamebox/StdAfx.cpp new file mode 100644 index 00000000..79228f61 --- /dev/null +++ b/Demos/gamebox/StdAfx.cpp @@ -0,0 +1,15 @@ +// stdafx.cpp : source file that includes just the standard includes +// App.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +#if defined _M_IX86 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_IA64 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_X64 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#else +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif diff --git a/Demos/gamebox/StdAfx.h b/Demos/gamebox/StdAfx.h new file mode 100644 index 00000000..99dd11e6 --- /dev/null +++ b/Demos/gamebox/StdAfx.h @@ -0,0 +1,35 @@ + +#if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) +#define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_ + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include + +#include "..\..\DuiLib\UIlib.h" + +using namespace DuiLib; + +#ifdef _DEBUG +# ifdef _UNICODE +# pragma comment(lib, "..\\..\\lib\\DuiLib_d.lib") +# else +# pragma comment(lib, "..\\..\\lib\\DuiLib_d.lib") +# endif +#else +# ifdef _UNICODE +# pragma comment(lib, "..\\..\\lib\\DuiLib.lib") +# else +# pragma comment(lib, "..\\..\\lib\\DuiLib.lib") +# endif +#endif + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) diff --git a/Demos/gamebox/XUnzip.cpp b/Demos/gamebox/XUnzip.cpp new file mode 100644 index 00000000..2f7b40a8 --- /dev/null +++ b/Demos/gamebox/XUnzip.cpp @@ -0,0 +1,4445 @@ +// XUnzip.cpp Version 1.3 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich@gmail.com +// +// Version 1.3: - Corrected size bug introduced by 1.2 +// +// Version 1.2: - Many bug fixes. See CodeProject article for list. +// +// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd() +// - Changed file names to avoid conflicts with Lucian's files +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by Info-ZIP. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + + +//#define _USE_32BIT_TIME_T //+++1.2 + + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include + +#pragma warning(disable : 4996) // disable bogus deprecation warning + +// THIS FILE is almost entirely based upon code by Jean-loup Gailly +// and Mark Adler. It has been modified by Lucian Wischik. +// The original code may be found at http://www.gzip.org/zlib/ +// The original copyright text follows. +// +// +// +// zlib.h -- interface of the 'zlib' general purpose compression library +// version 1.1.3, July 9th, 1998 +// +// Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Jean-loup Gailly Mark Adler +// jloup@gzip.org madler@alumni.caltech.edu +// +// +// The data format used by the zlib library is described by RFCs (Request for +// Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt +// (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +// +// +// The 'zlib' compression library provides in-memory compression and +// decompression functions, including integrity checks of the uncompressed +// data. This version of the library supports only one compression method +// (deflation) but other algorithms will be added later and will have the same +// stream interface. +// +// Compression can be done in a single step if the buffers are large +// enough (for example if an input file is mmap'ed), or can be done by +// repeated calls of the compression function. In the latter case, the +// application must provide more input and/or consume the output +// (providing more output space) before each call. +// +// The library also supports reading and writing files in gzip (.gz) format +// with an interface similar to that of stdio. +// +// The library does not install any signal handler. The decoder checks +// the consistency of the compressed data, so the library should never +// crash even in case of corrupted input. +// +// for more info about .ZIP format, see ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip +// PkWare has also a specification at ftp://ftp.pkware.com/probdesc.zip + +#define zmalloc(len) malloc(len) + +#define zfree(p) free(p) + +/* +void *zmalloc(unsigned int len) +{ char *buf = new char[len+32]; + for (int i=0; i<16; i++) + { buf[i]=i; + buf[len+31-i]=i; + } + *((unsigned int*)buf) = len; + char c[1000]; wsprintf(c,"malloc 0x%lx - %lu",buf+16,len); + OutputDebugString(c); + return buf+16; +} + +void zfree(void *buf) +{ char c[1000]; wsprintf(c,"free 0x%lx",buf); + OutputDebugString(c); + char *p = ((char*)buf)-16; + unsigned int len = *((unsigned int*)p); + bool blown=false; + for (int i=0; i<16; i++) + { char lo = p[i]; + char hi = p[len+31-i]; + if (hi!=i || (lo!=i && i>4)) blown=true; + } + if (blown) + { OutputDebugString("BLOWN!!!"); + } + delete[] p; +} +*/ + +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened + +typedef DWORD ZRESULT; +// return codes from any of the zip functions. Listed later. + +#define ZIP_HANDLE 1 +#define ZIP_FILENAME 2 +#define ZIP_MEMORY 3 + +typedef struct +{ int index; // index of this file within the zip +char name[MAX_PATH]; // filename within the zip +DWORD attr; // attributes, as in GetFileAttributes. +FILETIME atime,ctime,mtime;// access, create, modify filetimes +long comp_size; // sizes of item, compressed and uncompressed. These +long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; + +typedef struct +{ int index; // index of this file within the zip +TCHAR name[MAX_PATH]; // filename within the zip +DWORD attr; // attributes, as in GetFileAttributes. +FILETIME atime,ctime,mtime;// access, create, modify filetimes +long comp_size; // sizes of item, compressed and uncompressed. These +long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; + +// These are the result codes: +#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, +#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. +// The following come from general system stuff (e.g. files not openable) +#define ZR_GENMASK 0x0000FF00 +#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle +#define ZR_NOFILE 0x00000200 // couldn't create/open the file +#define ZR_NOALLOC 0x00000300 // failed to allocate some resource +#define ZR_WRITE 0x00000400 // a general error writing to the file +#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip +#define ZR_MORE 0x00000600 // there's still more data to be unzipped +#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile +#define ZR_READ 0x00000800 // a general error reading the file +// The following come from mistakes on the part of the caller +#define ZR_CALLERMASK 0x00FF0000 +#define ZR_ARGS 0x00010000 // general mistake with the arguments +#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't +#define ZR_MEMSIZE 0x00030000 // the memory size is too small +#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function +#define ZR_ENDED 0x00050000 // the zip creation has already been closed +#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken +#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped +#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip +// The following come from bugs within the zip library itself +#define ZR_BUGMASK 0xFF000000 +#define ZR_NOTINITED 0x01000000 // initialisation didn't work +#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file +#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed +#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code + +#pragma warning(disable : 4702) // unreachable code + +static ZRESULT zopenerror = ZR_OK; //+++1.2 + +typedef struct tm_unz_s +{ unsigned int tm_sec; // seconds after the minute - [0,59] + unsigned int tm_min; // minutes after the hour - [0,59] + unsigned int tm_hour; // hours since midnight - [0,23] + unsigned int tm_mday; // day of the month - [1,31] + unsigned int tm_mon; // months since January - [0,11] + unsigned int tm_year; // years - [1980..2044] +} tm_unz; + + +// unz_global_info structure contain global data about the ZIPfile +typedef struct unz_global_info_s +{ unsigned long number_entry; // total number of entries in the central dir on this disk + unsigned long size_comment; // size of the global comment of the zipfile +} unz_global_info; + +// unz_file_info contain information about a file in the zipfile +typedef struct unz_file_info_s +{ unsigned long version; // version made by 2 bytes + unsigned long version_needed; // version needed to extract 2 bytes + unsigned long flag; // general purpose bit flag 2 bytes + unsigned long compression_method; // compression method 2 bytes + unsigned long dosDate; // last mod file date in Dos fmt 4 bytes + unsigned long crc; // crc-32 4 bytes + unsigned long compressed_size; // compressed size 4 bytes + unsigned long uncompressed_size; // uncompressed size 4 bytes + unsigned long size_filename; // filename length 2 bytes + unsigned long size_file_extra; // extra field length 2 bytes + unsigned long size_file_comment; // file comment length 2 bytes + unsigned long disk_num_start; // disk number start 2 bytes + unsigned long internal_fa; // internal file attributes 2 bytes + unsigned long external_fa; // external file attributes 4 bytes + tm_unz tmu_date; +} unz_file_info; + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + + + + + + + +#define ZLIB_VERSION "1.1.3" + + +// Allowed flush values; see deflate() for details +#define Z_NO_FLUSH 0 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 + + +// compression levels +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) + +// compression strategy; see deflateInit2() for details +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +// Possible values of the data_type field +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 + +// The deflate compression method (the only one supported in this version) +#define Z_DEFLATED 8 + +// for initializing zalloc, zfree, opaque +#define Z_NULL 0 + +// case sensitivity when searching for filenames +#define CASE_SENSITIVE 1 +#define CASE_INSENSITIVE 2 + + +// Return codes for the compression/decompression functions. Negative +// values are errors, positive values are used for special but normal events. +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) + + + +// Basic data types +typedef unsigned char Byte; // 8 bits +typedef unsigned int uInt; // 16 bits or more +typedef unsigned long uLong; // 32 bits or more +typedef void *voidpf; +typedef void *voidp; +typedef long z_off_t; + + + + + + + + + + + + +typedef voidpf (*alloc_func) (voidpf opaque, uInt items, uInt size); +typedef void (*free_func) (voidpf opaque, voidpf address); + +struct internal_state; + +typedef struct z_stream_s { + Byte *next_in; // next input byte + uInt avail_in; // number of bytes available at next_in + uLong total_in; // total nb of input bytes read so far + + Byte *next_out; // next output byte should be put there + uInt avail_out; // remaining free space at next_out + uLong total_out; // total nb of bytes output so far + + char *msg; // last error message, NULL if no error + struct internal_state *state; // not visible by applications + + alloc_func zalloc; // used to allocate the internal state + free_func zfree; // used to free the internal state + voidpf opaque; // private data object passed to zalloc and zfree + + int data_type; // best guess about the data type: ascii or binary + uLong adler; // adler32 value of the uncompressed data + uLong reserved; // reserved for future use +} z_stream; + +typedef z_stream *z_streamp; + + +// The application must update next_in and avail_in when avail_in has +// dropped to zero. It must update next_out and avail_out when avail_out +// has dropped to zero. The application must initialize zalloc, zfree and +// opaque before calling the init function. All other fields are set by the +// compression library and must not be updated by the application. +// +// The opaque value provided by the application will be passed as the first +// parameter for calls of zalloc and zfree. This can be useful for custom +// memory management. The compression library attaches no meaning to the +// opaque value. +// +// zalloc must return Z_NULL if there is not enough memory for the object. +// If zlib is used in a multi-threaded application, zalloc and zfree must be +// thread safe. +// +// The fields total_in and total_out can be used for statistics or +// progress reports. After compression, total_in holds the total size of +// the uncompressed data and may be saved for use in the decompressor +// (particularly if the decompressor wants to decompress everything in +// a single step). +// + + +// basic functions + +const char *zlibVersion (); +// The application can compare zlibVersion and ZLIB_VERSION for consistency. +// If the first character differs, the library code actually used is +// not compatible with the zlib.h header file used by the application. +// This check is automatically made by inflateInit. + + + + + + +int inflate (z_streamp strm, int flush); +// +// inflate decompresses as much data as possible, and stops when the input +// buffer becomes empty or the output buffer becomes full. It may some +// introduce some output latency (reading input without producing any output) +// except when forced to flush. +// +// The detailed semantics are as follows. inflate performs one or both of the +// following actions: +// +// - Decompress more input starting at next_in and update next_in and avail_in +// accordingly. If not all input can be processed (because there is not +// enough room in the output buffer), next_in is updated and processing +// will resume at this point for the next call of inflate(). +// +// - Provide more output starting at next_out and update next_out and avail_out +// accordingly. inflate() provides as much output as possible, until there +// is no more input data or no more space in the output buffer (see below +// about the flush parameter). +// +// Before the call of inflate(), the application should ensure that at least +// one of the actions is possible, by providing more input and/or consuming +// more output, and updating the next_* and avail_* values accordingly. +// The application can consume the uncompressed output when it wants, for +// example when the output buffer is full (avail_out == 0), or after each +// call of inflate(). If inflate returns Z_OK and with zero avail_out, it +// must be called again after making room in the output buffer because there +// might be more output pending. +// +// If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much +// output as possible to the output buffer. The flushing behavior of inflate is +// not specified for values of the flush parameter other than Z_SYNC_FLUSH +// and Z_FINISH, but the current implementation actually flushes as much output +// as possible anyway. +// +// inflate() should normally be called until it returns Z_STREAM_END or an +// error. However if all decompression is to be performed in a single step +// (a single call of inflate), the parameter flush should be set to +// Z_FINISH. In this case all pending input is processed and all pending +// output is flushed; avail_out must be large enough to hold all the +// uncompressed data. (The size of the uncompressed data may have been saved +// by the compressor for this purpose.) The next operation on this stream must +// be inflateEnd to deallocate the decompression state. The use of Z_FINISH +// is never required, but can be used to inform inflate that a faster routine +// may be used for the single inflate() call. +// +// If a preset dictionary is needed at this point (see inflateSetDictionary +// below), inflate sets strm-adler to the adler32 checksum of the +// dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise +// it sets strm->adler to the adler32 checksum of all output produced +// so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or +// an error code as described below. At the end of the stream, inflate() +// checks that its computed adler32 checksum is equal to that saved by the +// compressor and returns Z_STREAM_END only if the checksum is correct. +// +// inflate() returns Z_OK if some progress has been made (more input processed +// or more output produced), Z_STREAM_END if the end of the compressed data has +// been reached and all uncompressed output has been produced, Z_NEED_DICT if a +// preset dictionary is needed at this point, Z_DATA_ERROR if the input data was +// corrupted (input stream not conforming to the zlib format or incorrect +// adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent +// (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not +// enough memory, Z_BUF_ERROR if no progress is possible or if there was not +// enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR +// case, the application may then call inflateSync to look for a good +// compression block. +// + + +int inflateEnd (z_streamp strm); +// +// All dynamically allocated data structures for this stream are freed. +// This function discards any unprocessed input and does not flush any +// pending output. +// +// inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state +// was inconsistent. In the error case, msg may be set but then points to a +// static string (which must not be deallocated). + + // Advanced functions + +// The following functions are needed only in some special applications. + + + + + +int inflateSetDictionary (z_streamp strm, + const Byte *dictionary, + uInt dictLength); +// +// Initializes the decompression dictionary from the given uncompressed byte +// sequence. This function must be called immediately after a call of inflate +// if this call returned Z_NEED_DICT. The dictionary chosen by the compressor +// can be determined from the Adler32 value returned by this call of +// inflate. The compressor and decompressor must use exactly the same +// dictionary. +// +// inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a +// parameter is invalid (such as NULL dictionary) or the stream state is +// inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the +// expected one (incorrect Adler32 value). inflateSetDictionary does not +// perform any decompression: this will be done by subsequent calls of +// inflate(). + + +int inflateSync (z_streamp strm); +// +// Skips invalid compressed data until a full flush point can be found, or until all +// available input is skipped. No output is provided. +// +// inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR +// if no more input was provided, Z_DATA_ERROR if no flush point has been found, +// or Z_STREAM_ERROR if the stream structure was inconsistent. In the success +// case, the application may save the current current value of total_in which +// indicates where valid compressed data was found. In the error case, the +// application may repeatedly call inflateSync, providing more input each time, +// until success or end of the input data. + + +int inflateReset (z_streamp strm); +// This function is equivalent to inflateEnd followed by inflateInit, +// but does not free and reallocate all the internal decompression state. +// The stream will keep attributes that may have been set by inflateInit2. +// +// inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source +// stream state was inconsistent (such as zalloc or state being NULL). +// + + + +// checksum functions +// These functions are not related to compression but are exported +// anyway because they might be useful in applications using the +// compression library. + +uLong adler32 (uLong adler, const Byte *buf, uInt len); +// Update a running Adler-32 checksum with the bytes buf[0..len-1] and +// return the updated checksum. If buf is NULL, this function returns +// the required initial value for the checksum. +// An Adler-32 checksum is almost as reliable as a CRC32 but can be computed +// much faster. Usage example: +// +// uLong adler = adler32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// adler = adler32(adler, buffer, length); +// } +// if (adler != original_adler) error(); + +uLong ucrc32 (uLong crc, const Byte *buf, uInt len); +// Update a running crc with the bytes buf[0..len-1] and return the updated +// crc. If buf is NULL, this function returns the required initial value +// for the crc. Pre- and post-conditioning (one's complement) is performed +// within this function so it shouldn't be done by the application. +// Usage example: +// +// uLong crc = crc32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// crc = crc32(crc, buffer, length); +// } +// if (crc != original_crc) error(); + + + + +const char *zError (int err); +int inflateSyncPoint (z_streamp z); +const uLong *get_crc_table (void); + + + +typedef unsigned char uch; +typedef uch uchf; +typedef unsigned short ush; +typedef ush ushf; +typedef unsigned long ulg; + + + +const char * const z_errmsg[10] = { // indexed by 2-zlib_error +"need dictionary", // Z_NEED_DICT 2 +"stream end", // Z_STREAM_END 1 +"", // Z_OK 0 +"file error", // Z_ERRNO (-1) +"stream error", // Z_STREAM_ERROR (-2) +"data error", // Z_DATA_ERROR (-3) +"insufficient memory", // Z_MEM_ERROR (-4) +"buffer error", // Z_BUF_ERROR (-5) +"incompatible version",// Z_VERSION_ERROR (-6) +""}; + + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +// To be used only when the state is known to be valid + + // common constants + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + +#define PRESET_DICT 0x20 // preset dictionary flag in zlib header + + // target dependencies + +#define OS_CODE 0x0b // Window 95 & Windows NT + + + + // functions + +#define zmemzero(dest, len) memset(dest, 0, len) + +// Diagnostic functions +#undef Assert +#undef Trace +#undef Tracev +#undef Tracevv +#undef Tracec +#undef Tracecv + +#ifdef DEBUG + + int z_verbose = 0; + void z_error (char *m) {fprintf(stderr, "%s\n", m); exit(1);} + +#define Assert(cond,msg) {if(!(cond)) z_error(msg);} +#define Trace(x) {if (z_verbose>=0) fprintf x ;} +#define Tracev(x) {if (z_verbose>0) fprintf x ;} +#define Tracevv(x) {if (z_verbose>1) fprintf x ;} +#define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +#define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} + +#else + +#ifndef __noop +#if _MSC_VER < 1300 +#define __noop ((void)0) +#endif +#endif + +#define Assert(cond,msg) __noop +#define Trace(x) __noop +#define Tracev(x) __noop +#define Tracevv(x) __noop +#define Tracec(c,x) __noop +#define Tracecv(c,x) __noop + +#endif + + +typedef uLong (*check_func) (uLong check, const Byte *buf, uInt len); +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size); +void zcfree (voidpf opaque, voidpf ptr); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) + +//void ZFREE(z_streamp strm,voidpf addr) +//{ *((strm)->zfree))((strm)->opaque, addr); +//} + +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + + + + +// Huffman code lookup table entry--this entry is four bytes for machines +// that have 16-bit pointers (e.g. PC's in the small or medium model). + + +typedef struct inflate_huft_s inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; // number of extra bits or operation + Byte Bits; // number of bits in this code or subcode + } what; + uInt pad; // pad structure to a power of 2 (4 bytes for + } word; // 16-bit, 8 bytes for 32-bit int's) + uInt base; // literal, length base, distance base, or table offset +}; + +// Maximum size of dynamic tree. The maximum found in a long but non- +// exhaustive search was 1004 huft structures (850 for length/literals +// and 154 for distances, the latter actually the result of an +// exhaustive search). The actual maximum is not known, but the +// value below is more than safe. +#define MANY 1440 + +int inflate_trees_bits ( + uInt *, // 19 code lengths + uInt *, // bits tree desired/actual depth + inflate_huft * *, // bits tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_dynamic ( + uInt, // number of literal/length codes + uInt, // number of distance codes + uInt *, // that many (total) code lengths + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + inflate_huft * *, // literal/length tree result + inflate_huft * *, // distance tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_fixed ( + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + const inflate_huft * *, // literal/length tree result + const inflate_huft * *, // distance tree result + z_streamp); // for memory allocation + + + + + +struct inflate_blocks_state; +typedef struct inflate_blocks_state inflate_blocks_statef; + +inflate_blocks_statef * inflate_blocks_new ( + z_streamp z, + check_func c, // check function + uInt w); // window size + +int inflate_blocks ( + inflate_blocks_statef *, + z_streamp , + int); // initial return code + +void inflate_blocks_reset ( + inflate_blocks_statef *, + z_streamp , + uLong *); // check value on output + +int inflate_blocks_free ( + inflate_blocks_statef *, + z_streamp); + +void inflate_set_dictionary ( + inflate_blocks_statef *s, + const Byte *d, // dictionary + uInt n); // dictionary length + +int inflate_blocks_sync_point ( + inflate_blocks_statef *s); + + + + +struct inflate_codes_state; +typedef struct inflate_codes_state inflate_codes_statef; + +inflate_codes_statef *inflate_codes_new ( + uInt, uInt, + const inflate_huft *, const inflate_huft *, + z_streamp ); + +int inflate_codes ( + inflate_blocks_statef *, + z_streamp , + int); + +void inflate_codes_free ( + inflate_codes_statef *, + z_streamp ); + + + + +typedef enum { + IBM_TYPE, // get type bits (3, including end bit) + IBM_LENS, // get lengths for stored + IBM_STORED, // processing stored block + IBM_TABLE, // get table lengths + IBM_BTREE, // get bit lengths tree for a dynamic block + IBM_DTREE, // get length, distance trees for a dynamic block + IBM_CODES, // processing fixed or dynamic block + IBM_DRY, // output remaining window bytes + IBM_DONE, // finished last block, done + IBM_BAD} // got a data error--stuck here +inflate_block_mode; + +// inflate blocks semi-private state +struct inflate_blocks_state { + + // mode + inflate_block_mode mode; // current inflate_block mode + + // mode dependent information + union { + uInt left; // if STORED, bytes left to copy + struct { + uInt table; // table lengths (14 bits) + uInt index; // index into blens (or border) + uInt *blens; // bit lengths of codes + uInt bb; // bit length tree depth + inflate_huft *tb; // bit length decoding tree + } trees; // if DTREE, decoding info for trees + struct { + inflate_codes_statef + *codes; + } decode; // if CODES, current state + } sub; // submode + uInt last; // true if this block is the last block + + // mode independent information + uInt bitk; // bits in bit buffer + uLong bitb; // bit buffer + inflate_huft *hufts; // single malloc for tree space + Byte *window; // sliding window + Byte *end; // one byte after sliding window + Byte *read; // window read pointer + Byte *write; // window write pointer + check_func checkfn; // check function + uLong check; // check on output + +}; + + +// defines for inflate input/output +// update pointers and return +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=(uLong)(p-z->next_in);z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +// get bytes and bits +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +// output bytes +#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;m;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +// load local pointers +#define LOAD {LOADIN LOADOUT} + +// masks for lower bits (size given to avoid silly warnings with Visual C++) +// And'ing with mask[n] masks the lower n bits +const uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +// copy as much as possible from the sliding window to the output area +int inflate_flush (inflate_blocks_statef *, z_streamp, int); + +int inflate_fast (uInt, uInt, const inflate_huft *, const inflate_huft *, inflate_blocks_statef *, z_streamp ); + + + +const uInt fixed_bl = 9; +const uInt fixed_bd = 5; +const inflate_huft fixed_tl[] = { + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} + }; +const inflate_huft fixed_td[] = { + {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, + {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, + {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, + {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, + {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, + {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, + {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, + {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} + }; + + + + + + + +// copy as much as possible from the sliding window to the output area +int inflate_flush(inflate_blocks_statef *s,z_streamp z,int r) +{ + uInt n; + Byte *p; + Byte *q; + + // local copies of source and destination pointers + p = z->next_out; + q = s->read; + + // compute number of bytes to copy as far as end of window + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy as far as end of window + if (n!=0) // check for n!=0 to avoid waking up CodeGuard + { memcpy(p, q, n); + p += n; + q += n; + } + + // see if more to copy at beginning of window + if (q == s->end) + { + // wrap pointers + q = s->window; + if (s->write == s->end) + s->write = s->window; + + // compute bytes to copy + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy + memcpy(p, q, n); + p += n; + q += n; + } + + // update pointers + z->next_out = p; + s->read = q; + + // done + return r; +} + + + + + + +// simplify the use of the inflate_huft type with some defines +#define exop word.what.Exop +#define bits word.what.Bits + +typedef enum { // waiting for "i:"=input, "o:"=output, "x:"=nothing + START, // x: set up for LEN + LEN, // i: get length/literal/eob next + LENEXT, // i: getting length extra (have base) + DIST, // i: get distance next + DISTEXT, // i: getting distance extra + COPY, // o: copying bytes in window, waiting for space + LIT, // o: got literal, waiting for output space + WASH, // o: got eob, possibly still output waiting + END, // x: got eob and all data flushed + BADCODE} // x: got error +inflate_codes_mode; + +// inflate codes private state +struct inflate_codes_state { + + // mode + inflate_codes_mode mode; // current inflate_codes mode + + // mode dependent information + uInt len; + union { + struct { + const inflate_huft *tree; // pointer into tree + uInt need; // bits needed + } code; // if LEN or DIST, where in tree + uInt lit; // if LIT, literal + struct { + uInt get; // bits to get for extra + uInt dist; // distance back to copy from + } copy; // if EXT or COPY, where and how much + } sub; // submode + + // mode independent information + Byte lbits; // ltree bits decoded per branch + Byte dbits; // dtree bits decoder per branch + const inflate_huft *ltree; // literal/length/eob tree + const inflate_huft *dtree; // distance tree + +}; + + +inflate_codes_statef *inflate_codes_new( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +z_streamp z) +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt j; // temporary storage + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + Byte *f; // pointer to copy strings from + inflate_codes_statef *c = s->sub.decode.codes; // codes state + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input and output based on current state + for(;;) switch (c->mode) + { // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif // !SLOW + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: // i: get length/literal/eob next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) // literal + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) // length + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + if (e & 32) // end of block + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: // i: getting length extra (have base) + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: // i: get distance next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) // distance + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: // i: getting distance extra + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: // o: copying bytes in window, waiting for space + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: // o: got literal, waiting for output space + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: // o: got eob, possibly more output + if (k > 7) // return unused byte, if any + { + Assert(k < 16, "inflate_codes grabbed too many bytes"); + k -= 8; + n++; + p--; // can always return one + } + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: // x: got error + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +void inflate_codes_free(inflate_codes_statef *c,z_streamp z) +{ ZFREE(z, c); + Tracev((stderr, "inflate: codes free\n")); +} + + + +// infblock.c -- interpret and process block types to last block +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + + +// Table for deflate from PKZIP's appnote.txt. +const uInt border[] = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +// +// Notes beyond the 1.93a appnote.txt: +// +// 1. Distance pointers never point before the beginning of the output stream. +// 2. Distance pointers can point back across blocks, up to 32k away. +// 3. There is an implied maximum of 7 bits for the bit length table and +// 15 bits for the actual data. +// 4. If only one code exists, then it is encoded using one bit. (Zero +// would be more efficient, but perhaps a little confusing.) If two +// codes exist, they are coded using one bit each (0 and 1). +// 5. There is no way of sending zero distance codes--a dummy must be +// sent if there are none. (History: a pre 2.0 version of PKZIP would +// store blocks with no distance codes, but this was discovered to be +// too harsh a criterion.) Valid only for 1.93a. 2.04c does allow +// zero distance codes, which is sent as one code of zero bits in +// length. +// 6. There are up to 286 literal/length codes. Code 256 represents the +// end-of-block. Note however that the static length tree defines +// 288 codes just to fill out the Huffman codes. Codes 286 and 287 +// cannot be used though, since there is no length base or extra bits +// defined for them. Similarily, there are up to 30 distance codes. +// However, static trees define 32 codes (all 5 bits) to fill out the +// Huffman codes, but the last two had better not show up in the data. +// 7. Unzip can check dynamic Huffman blocks for complete code sets. +// The exception is that a single code would not be complete (see #4). +// 8. The five bits following the block type is really the number of +// literal codes sent minus 257. +// 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits +// (1+6+6). Therefore, to output three times the length, you output +// three codes (1+1+1), whereas to output four times the same length, +// you only need two codes (1+3). Hmm. +//10. In the tree reconstruction algorithm, Code = Code + Increment +// only if BitLength(i) is not zero. (Pretty obvious.) +//11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) +//12. Note: length code 284 can represent 227-258, but length code 285 +// really is 258. The last length deserves its own, short code +// since it gets used a lot in very redundant files. The length +// 258 is special since 258 - 3 (the min match length) is 255. +//13. The literal/length and distance code bit lengths are read as a +// single stream of lengths. It is possible (and advantageous) for +// a repeat code (16, 17, or 18) to go across the boundary between +// the two sets of lengths. + + +void inflate_blocks_reset(inflate_blocks_statef *s, z_streamp z, uLong *c) +{ + if (c != Z_NULL) + *c = s->check; + if (s->mode == IBM_BTREE || s->mode == IBM_DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == IBM_CODES) + inflate_codes_free(s->sub.decode.codes, z); + s->mode = IBM_TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, (const Byte *)Z_NULL, 0); + Tracev((stderr, "inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z_streamp z, check_func c, uInt w) +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->hufts = + (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + if ((s->window = (Byte *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s->hufts); + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = IBM_TYPE; + Tracev((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, Z_NULL); + return s; +} + + +int inflate_blocks(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt t; // temporary storage + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input based on current state + for(;;) switch (s->mode) + { + case IBM_TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: // stored + Tracev((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; // go to byte boundary + DUMPBITS(t) + s->mode = IBM_LENS; // get length of stored block + break; + case 1: // fixed + Tracev((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + const inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td, z); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + } + DUMPBITS(3) + s->mode = IBM_CODES; + break; + case 2: // dynamic + Tracev((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = IBM_TABLE; + break; + case 3: // illegal + DUMPBITS(3) + s->mode = IBM_BAD; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case IBM_LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = IBM_BAD; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; // dump bits + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? IBM_STORED : (s->last ? IBM_DRY : IBM_TYPE); + break; + case IBM_STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + memcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? IBM_DRY : IBM_TYPE; + break; + case IBM_TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; + // remove this section to workaround bug in pkzip + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = IBM_BAD; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } + // end remove + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if ((s->sub.trees.blens = (uInt*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = IBM_BTREE; + case IBM_BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, s->hufts, z); + if (t != Z_OK) + { + ZFREE(z, s->sub.trees.blens); + r = t; + if (r == Z_DATA_ERROR) + s->mode = IBM_BAD; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = IBM_DTREE; + case IBM_DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->bits; + c = h->base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else // c == 16..18 + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + ZFREE(z, s->sub.trees.blens); + s->mode = IBM_BAD; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; // must be <= 9 for lookahead assumptions + bd = 6; // must be <= 9 for lookahead assumptions + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, + s->hufts, z); + ZFREE(z, s->sub.trees.blens); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = IBM_BAD; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + } + s->mode = IBM_CODES; + case IBM_CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = IBM_TYPE; + break; + } + s->mode = IBM_DRY; + case IBM_DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = IBM_DONE; + case IBM_DONE: + r = Z_STREAM_END; + LEAVE + case IBM_BAD: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(inflate_blocks_statef *s, z_streamp z) +{ + inflate_blocks_reset(s, z, Z_NULL); + ZFREE(z, s->window); + ZFREE(z, s->hufts); + ZFREE(z, s); + Tracev((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + + +// inftrees.c -- generate Huffman trees for efficient decoding +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + + +extern const char inflate_copyright[] = + " ";//inflate 1.1.3 Copyright 1995-1998 Mark Adler "; +// If you use the zlib library in a product, an acknowledgment is welcome +// in the documentation of your product. If for some reason you cannot +// include such an acknowledgment, I would appreciate that you keep this +// copyright string in the executable of your product. + + + +int huft_build ( + uInt *, // code lengths in bits + uInt, // number of codes + uInt, // number of "simple" codes + const uInt *, // list of base values for non-simple codes + const uInt *, // list of extra bits for non-simple codes + inflate_huft **,// result: starting table + uInt *, // maximum lookup bits (returns actual) + inflate_huft *, // space for trees + uInt *, // hufts used in space + uInt * ); // space for values + +// Tables for deflate from PKZIP's appnote.txt. +const uInt cplens[31] = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + // see note #13 above about 258 +const uInt cplext[31] = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; // 112==invalid +const uInt cpdist[30] = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +const uInt cpdext[30] = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +// +// Huffman code decoding is performed using a multi-level table lookup. +// The fastest way to decode is to simply build a lookup table whose +// size is determined by the longest code. However, the time it takes +// to build this table can also be a factor if the data being decoded +// is not very long. The most common codes are necessarily the +// shortest codes, so those codes dominate the decoding time, and hence +// the speed. The idea is you can have a shorter table that decodes the +// shorter, more probable codes, and then point to subsidiary tables for +// the longer codes. The time it costs to decode the longer codes is +// then traded against the time it takes to make longer tables. +// +// This results of this trade are in the variables lbits and dbits +// below. lbits is the number of bits the first level table for literal/ +// length codes can decode in one step, and dbits is the same thing for +// the distance codes. Subsequent tables are also less than or equal to +// those sizes. These values may be adjusted either when all of the +// codes are shorter than that, in which case the longest code length in +// bits is used, or when the shortest code is *longer* than the requested +// table size, in which case the length of the shortest code in bits is +// used. +// +// There are two different values for the two tables, since they code a +// different number of possibilities each. The literal/length table +// codes 286 possible values, or in a flat code, a little over eight +// bits. The distance table codes 30 possible values, or a little less +// than five bits, flat. The optimum values for speed end up being +// about one bit more than those, so lbits is 8+1 and dbits is 5+1. +// The optimum values may differ though from machine to machine, and +// possibly even between compilers. Your mileage may vary. +// + + +// If BMAX needs to be larger than 16, then h and x[] should be uLong. +#define BMAX 15 // maximum bit length of any code + +int huft_build( +uInt *b, // code lengths in bits (all assumed <= BMAX) +uInt n, // number of codes (assumed <= 288) +uInt s, // number of simple-valued codes (0..s-1) +const uInt *d, // list of base values for non-simple codes +const uInt *e, // list of extra bits for non-simple codes +inflate_huft * *t, // result: starting table +uInt *m, // maximum lookup bits, returns actual +inflate_huft *hp, // space for trees +uInt *hn, // hufts used in space +uInt *v) // working area: values in order of bit length +// Given a list of code lengths and a maximum table size, make a set of +// tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR +// if the given code set is incomplete (the tables are still built in this +// case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of +// lengths), or Z_MEM_ERROR if not enough memory. +{ + + uInt a; // counter for codes of length k + uInt c[BMAX+1]; // bit length count table + uInt f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + register uInt i; // counter, current code + register uInt j; // counter + register int k; // number of bits in current code + int l; // bits per table (returned in m) + uInt mask; // (1 << w) - 1, to avoid cc -O bug on HP + register uInt *p; // pointer into c[], b[], or v[] + inflate_huft *q; // points to current table + struct inflate_huft_s r; // table entry for structure assignment + inflate_huft *u[BMAX]; // table stack + register int w; // bits before this table == (l * h) + uInt x[BMAX+1]; // bit offsets, then code stack + uInt *xp; // pointer into x + int y; // number of dummy codes added + uInt z; // number of entries in current table + + + // Generate counts for each bit length + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4; p; // clear c[]--assume BMAX+1 is 16 + p = b; i = n; + do { + c[*p++]++; // assume all entries <= BMAX + } while (--i); + if (c[0] == n) // null input--all zero length codes + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + // Find minimum and maximum length, bound *m by those + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; // minimum code length + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; // maximum code length + if ((uInt)l > i) + l = i; + *m = l; + + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { // note that i == g from above + *xp++ = (j += *p++); + } + + + // Make a table of values in order of bit lengths + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; // set n to length of v + + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = v; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = (inflate_huft *)Z_NULL; // just to keep compilers happy + q = (inflate_huft *)Z_NULL; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + + // compute minimum size table less than or equal to l bits + z = g - w; + z = z > (uInt)l ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) // try a k-w bit table + { // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = c + k; + if (j < z) + while (++j < z) // try smaller tables up to z bits + { + if ((f <<= 1) <= *++xp) + break; // enough codes to use up j bits + f -= *xp; // else deduct codes from patterns + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (*hn + z > MANY) // (note: doesn't matter for fixed) + return Z_MEM_ERROR; // not enough memory + u[h] = q = hp + *hn; + *hn += z; + + // connect to last table, if there is one + if (h) + { + x[h] = i; // save pattern for backing up + r.bits = (Byte)l; // bits to dump before this table + r.exop = (Byte)j; // bits in this table + j = i >> (w - l); + r.base = (uInt)(q - u[h-1] - j); // offset to this table + u[h-1][j] = r; // connect to last table + } + else + *t = q; // first table is returned result + } + + // set up table entry in r + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; // out of values--invalid code + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); // 256 is end-of-block + r.base = *p++; // simple code is just the value + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);// non-simple--look up in lists + r.base = d[*p++ - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + + + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits( +uInt *c, // 19 code lengths +uInt *bb, // bits tree desired/actual depth +inflate_huft * *tb, // bits tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + if ((v = (uInt*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + r = huft_build(c, 19, 19, (uInt*)Z_NULL, (uInt*)Z_NULL, + tb, bb, hp, &hn, v); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +} + + +int inflate_trees_dynamic( +uInt nl, // number of literal/length codes +uInt nd, // number of distance codes +uInt *c, // that many (total) code lengths +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +inflate_huft * *tl, // literal/length tree result +inflate_huft * *td, // distance tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + // allocate work area + if ((v = (uInt*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + + // build literal/length tree + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // build distance tree + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // done + ZFREE(z, v); + return Z_OK; +} + + + + + +int inflate_trees_fixed( +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +const inflate_huft * * tl, // literal/length tree result +const inflate_huft * *td, // distance tree result +z_streamp ) // for memory allocation +{ + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +// inffast.c -- process literals and length/distance pairs fast +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + +// macros for bit input with no checking and for returning unused bytes +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} + +// Called with number of bytes left to write in window at least 258 +// (the maximum string length) and number of input bytes available +// at least ten. The ten bytes are six bytes for the longest length/ +// distance pair plus four bytes for overloading the bit buffer. + +int inflate_fast( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +inflate_blocks_statef *s, +z_streamp z) +{ + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + uInt ml; // mask for literal/length tree + uInt md; // mask for distance tree + uInt c; // bytes to copy + uInt d; // distance back to copy from + Byte *r; // copy source pointer + + // load input, output, bit values + LOAD + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do { // assume called with m >= 258 && n >= 10 + // get literal/length code + GRABBITS(20) // max bits for literal/length code + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits for length + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + // decode distance base of block to copy + GRABBITS(15); // max bits for distance code + e = (t = td + ((uInt)b & md))->exop; + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits to add to distance base + e &= 15; + GRABBITS(e) // get extra bits (up to 13) + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + // do the copy + m -= c; + if ((uInt)(q - s->window) >= d) // offset before dest + { // just copy + r = q - d; + *q++ = *r++; c--; // minimum count is three, + *q++ = *r++; c--; // so unroll loop a little + } + else // else offset after destination + { + e = d - (uInt)(q - s->window); // bytes from offset to end + r = s->end - e; // pointer to offset + if (c > e) // if source crosses, + { + c -= e; // copy to end of window + do { + *q++ = *r++; + } while (--e); + r = s->window; // copy rest from start of window + } + } + do { // copy all or what's left + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + { + t += t->base; + e = (t += ((uInt)b & inflate_mask[e]))->exop; + } + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + break; + } + if ((e & 64) == 0) + { + t += t->base; + if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + } while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + UNGRAB + UPDATE + return Z_OK; +} + + + + + + +// crc32.c -- compute the CRC-32 of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + + + + + +// Table of CRC-32's of all single-byte values (made by make_crc_table) +const uLong crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +const uLong * get_crc_table() +{ return (const uLong *)crc_table; +} + +#define CRC_DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define CRC_DO2(buf) CRC_DO1(buf); CRC_DO1(buf); +#define CRC_DO4(buf) CRC_DO2(buf); CRC_DO2(buf); +#define CRC_DO8(buf) CRC_DO4(buf); CRC_DO4(buf); + +uLong ucrc32(uLong crc, const Byte *buf, uInt len) +{ if (buf == Z_NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) {CRC_DO8(buf); len -= 8;} + if (len) do {CRC_DO1(buf);} while (--len); + return crc ^ 0xffffffffL; +} + + +// adler32.c -- compute the Adler-32 checksum of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + +#define BASE 65521L // largest prime smaller than 65536 +#define NMAX 5552 +// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + +#define AD_DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define AD_DO2(buf,i) AD_DO1(buf,i); AD_DO1(buf,i+1); +#define AD_DO4(buf,i) AD_DO2(buf,i); AD_DO2(buf,i+2); +#define AD_DO8(buf,i) AD_DO4(buf,i); AD_DO4(buf,i+4); +#define AD_DO16(buf) AD_DO8(buf,0); AD_DO8(buf,8); + +// ========================================================================= +uLong adler32(uLong adler, const Byte *buf, uInt len) +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + AD_DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} + + + +// zutil.c -- target dependent utility functions for the compression library +// Copyright (C) 1995-1998 Jean-loup Gailly. +// For conditions of distribution and use, see copyright notice in zlib.h +// @(#) $Id$ + + + + + + +const char * zlibVersion() +{ + return ZLIB_VERSION; +} + +// exported to allow conversion of error code to string for compress() and +// uncompress() +const char * zError(int err) +{ return ERR_MSG(err); +} + + + + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) items += size - size; // make compiler happy + return (voidpf)calloc(items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + zfree(ptr); + if (opaque) return; // make compiler happy +} + + + +// inflate.c -- zlib interface to inflate modules +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_blocks_state {int dummy;}; // for buggy compilers + +typedef enum { + IM_METHOD, // waiting for method byte + IM_FLAG, // waiting for flag byte + IM_DICT4, // four dictionary check bytes to go + IM_DICT3, // three dictionary check bytes to go + IM_DICT2, // two dictionary check bytes to go + IM_DICT1, // one dictionary check byte to go + IM_DICT0, // waiting for inflateSetDictionary + IM_BLOCKS, // decompressing blocks + IM_CHECK4, // four check bytes to go + IM_CHECK3, // three check bytes to go + IM_CHECK2, // two check bytes to go + IM_CHECK1, // one check byte to go + IM_DONE, // finished check, done + IM_BAD} // got an error--stay here +inflate_mode; + +// inflate private state +struct internal_state { + + // mode + inflate_mode mode; // current inflate mode + + // mode dependent information + union { + uInt method; // if IM_FLAGS, method byte + struct { + uLong was; // computed check value + uLong need; // stream check value + } check; // if CHECK, check values to compare + uInt marker; // if IM_BAD, inflateSync's marker bytes count + } sub; // submode + + // mode independent information + int nowrap; // flag for no wrapper + uInt wbits; // log2(window size) (8..15, defaults to 15) + inflate_blocks_statef + *blocks; // current inflate_blocks state + +}; + +int inflateReset(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? IM_BLOCKS : IM_METHOD; + inflate_blocks_reset(z->state->blocks, z, Z_NULL); + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int inflateEnd(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z); + ZFREE(z, z->state); + z->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z_streamp z) +{ const char *version = ZLIB_VERSION; int stream_size = sizeof(z_stream); + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != sizeof(z_stream)) return Z_VERSION_ERROR; + + int w = -15; // MAX_WBITS: 32K LZ77 window. + // Warning: reducing MAX_WBITS makes minigzip unable to extract .gz files created by gzip. + // The memory requirements for deflate are (in bytes): + // (1 << (windowBits+2)) + (1 << (memLevel+9)) + // that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + // plus a few kilobytes for small objects. For example, if you want to reduce + // the default memory requirements from 256K to 128K, compile with + // make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + // Of course this will generally degrade compression (there's no free lunch). + // + // The memory requirements for inflate are (in bytes) 1 << windowBits + // that is, 32K for windowBits=15 (default value) plus a few kilobytes + // for small objects. + + // initialize state + if (z == Z_NULL) return Z_STREAM_ERROR; + z->msg = Z_NULL; + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; + if ((z->state = (struct internal_state *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + // handle undocumented nowrap option (no zlib header or check) + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + // create inflate_blocks state + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Tracev((stderr, "inflate: allocated\n")); + + // reset state + inflateReset(z); + return Z_OK; +} + + + +#define IM_NEEDBYTE {if(z->avail_in==0)return r;r=f;} +#define IM_NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z_streamp z, int f) +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + for (;;) switch (z->state->mode) + { + case IM_METHOD: + IM_NEEDBYTE + if (((z->state->sub.method = IM_NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = IM_BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = IM_BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + z->state->mode = IM_FLAG; + case IM_FLAG: + IM_NEEDBYTE + b = IM_NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = IM_BLOCKS; + break; + } + z->state->mode = IM_DICT4; + case IM_DICT4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_DICT3; + case IM_DICT3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_DICT2; + case IM_DICT2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_DICT1; + case IM_DICT1: + IM_NEEDBYTE; r; + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = IM_DICT0; + return Z_NEED_DICT; + case IM_DICT0: + z->state->mode = IM_BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case IM_BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (r == Z_DATA_ERROR) + { + z->state->mode = IM_BAD; + z->state->sub.marker = 0; // can try inflateSync + break; + } + if (r == Z_OK) + r = f; + if (r != Z_STREAM_END) + return r; + r = f; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = IM_DONE; + break; + } + z->state->mode = IM_CHECK4; + case IM_CHECK4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_CHECK3; + case IM_CHECK3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_CHECK2; + case IM_CHECK2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_CHECK1; + case IM_CHECK1: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib check ok\n")); + z->state->mode = IM_DONE; + case IM_DONE: + return Z_STREAM_END; + case IM_BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } +} + + + +#ifdef _UNICODE + +static int GetAnsiFileName(LPCWSTR name, char * buf, int nBufSize) +{ + memset(buf, 0, nBufSize); + + int n = WideCharToMultiByte(CP_ACP, // code page + 0, // performance and mapping flags + name, // wide-character string + -1, // number of chars in string + buf, // buffer for new string + nBufSize, // size of buffer + NULL, // default for unmappable chars + NULL); // set when default char used + return n; +} + +static int GetUnicodeFileName(const char * name, LPWSTR buf, int nBufSize) +{ + memset(buf, 0, nBufSize*sizeof(TCHAR)); + + int n = MultiByteToWideChar(CP_ACP, // code page + 0, // character-type options + name, // string to map + -1, // number of bytes in string + buf, // wide-character buffer + nBufSize); // size of buffer + + return n; +} + +#endif + + +// unzip.c -- IO on .zip files using zlib +// Version 0.15 beta, Mar 19th, 1998, +// Read unzip.h for more info + + + + +#define UNZ_BUFSIZE (16384) +#define UNZ_MAXFILENAMEINZIP (256) +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = " ";//unzip 0.15 Copyright 1998 Gilles Vollant "; + +// unz_file_info_interntal contain internal info about a file in zipfile +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;// relative offset of local header 4 bytes +} unz_file_info_internal; + + +typedef struct +{ bool is_handle; // either a handle or memory + bool canseek; + // for handles: + HANDLE h; bool herr; unsigned long initial_offset; + // for memory: + void *buf; unsigned int len,pos; // if it's a memory block +} LUFILE; + + +LUFILE *lufopen(void *z,unsigned int len,DWORD flags,ZRESULT *err) +{ + if (flags!=ZIP_HANDLE && flags!=ZIP_FILENAME && flags!=ZIP_MEMORY) + { + *err=ZR_ARGS; + return NULL; + } + // + HANDLE h=0; bool canseek=false; *err=ZR_OK; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + if (flags==ZIP_HANDLE) + { + HANDLE hf = z; + + BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&h,0,FALSE,DUPLICATE_SAME_ACCESS); + + if (!res) + { + *err=ZR_NODUPH; + return NULL; + } + } + else + { + h = CreateFile((const TCHAR *)z, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) + { + *err = ZR_NOFILE; + return NULL; + } + } + DWORD type = GetFileType(h); + canseek = (type==FILE_TYPE_DISK); + } + LUFILE *lf = new LUFILE; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + lf->is_handle=true; + lf->canseek=canseek; + lf->h=h; lf->herr=false; + lf->initial_offset=0; + if (canseek) + lf->initial_offset = SetFilePointer(h,0,NULL,FILE_CURRENT); + } + else + { + lf->is_handle=false; + lf->canseek=true; + lf->buf=z; + lf->len=len; + lf->pos=0; + lf->initial_offset=0; + } + *err=ZR_OK; + return lf; +} + + +int lufclose(LUFILE *stream) +{ if (stream==NULL) return EOF; + if (stream->is_handle) CloseHandle(stream->h); + delete stream; + return 0; +} + +int luferror(LUFILE *stream) +{ if (stream->is_handle && stream->herr) return 1; + else return 0; +} + +long int luftell(LUFILE *stream) +{ if (stream->is_handle && stream->canseek) return SetFilePointer(stream->h,0,NULL,FILE_CURRENT)-stream->initial_offset; + else if (stream->is_handle) return 0; + else return stream->pos; +} + +int lufseek(LUFILE *stream, long offset, int whence) +{ if (stream->is_handle && stream->canseek) + { if (whence==SEEK_SET) SetFilePointer(stream->h,stream->initial_offset+offset,0,FILE_BEGIN); + else if (whence==SEEK_CUR) SetFilePointer(stream->h,offset,NULL,FILE_CURRENT); + else if (whence==SEEK_END) SetFilePointer(stream->h,offset,NULL,FILE_END); + else return 19; // EINVAL + return 0; + } + else if (stream->is_handle) return 29; // ESPIPE + else + { if (whence==SEEK_SET) stream->pos=offset; + else if (whence==SEEK_CUR) stream->pos+=offset; + else if (whence==SEEK_END) stream->pos=stream->len+offset; + return 0; + } +} + + +size_t lufread(void *ptr,size_t size,size_t n,LUFILE *stream) +{ unsigned int toread = (unsigned int)(size*n); + if (stream->is_handle) + { DWORD red; BOOL res = ReadFile(stream->h,ptr,toread,&red,NULL); + if (!res) stream->herr=true; + return red/size; + } + if (stream->pos+toread > stream->len) toread = stream->len-stream->pos; + memcpy(ptr, (char*)stream->buf + stream->pos, toread); DWORD red = toread; + stream->pos += red; + return red/size; +} + + + + +// file_in_zip_read_info_s contain internal information about a file in zipfile, +// when reading and decompress it +typedef struct +{ + char *read_buffer; // internal buffer for compressed data + z_stream stream; // zLib stream structure for inflate + + uLong pos_in_zipfile; // position in byte on the zipfile, for fseek + uLong stream_initialised; // flag set if stream structure is initialised + + uLong offset_local_extrafield;// offset of the local extra field + uInt size_local_extrafield;// size of the local extra field + uLong pos_local_extrafield; // position in the local extra field in read + + uLong crc32; // crc32 of all data uncompressed + uLong crc32_wait; // crc32 we must obtain after decompress all + uLong rest_read_compressed; // number of byte to be decompressed + uLong rest_read_uncompressed;//number of byte to be obtained after decomp + LUFILE* file; // io structore of the zipfile + uLong compression_method; // compression method (0==store) + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) +} file_in_zip_read_info_s; + + +// unz_s contain internal information about the zipfile +typedef struct +{ + LUFILE* file; // io structore of the zipfile + unz_global_info gi; // public global information + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) + uLong num_file; // number of the current file in the zipfile + uLong pos_in_central_dir; // pos of the current file in the central dir + uLong current_file_ok; // flag about the usability of the current file + uLong central_pos; // position of the beginning of the central dir + + uLong size_central_dir; // size of the central directory + uLong offset_central_dir; // offset of start of central directory with respect to the starting disk number + + unz_file_info cur_file_info; // public info about the current file in zip + unz_file_info_internal cur_file_info_internal; // private info about it + file_in_zip_read_info_s* pfile_in_zip_read; // structure about the current file if we are decompressing it +} unz_s, *unzFile; + + +int unzStringFileNameCompare (const char* fileName1,const char* fileName2,int iCaseSensitivity); +// Compare two filename (fileName1,fileName2). + +z_off_t unztell (unzFile file); +// Give the current position in uncompressed data + +int unzeof (unzFile file); +// return 1 if the end of file was reached, 0 elsewhere + +int unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len); +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// +// if buf==NULL, it return the size of the local extra field +// +// if buf!=NULL, len is the size of the buffer, the extra header is copied in +// buf. +// the return value is the number of bytes copied in buf, or (if <0) +// the error code + + + +// =========================================================================== +// Read a byte from a gz_stream; update next_in and avail_in. Return EOF +// for end of file. +// IN assertion: the stream s has been sucessfully opened for reading. + +int unzlocal_getByte(LUFILE *fin,int *pi) +{ unsigned char c; + int err = (int)lufread(&c, 1, 1, fin); + if (err==1) + { *pi = (int)c; + return UNZ_OK; + } + else + { if (luferror(fin)) return UNZ_ERRNO; + else return UNZ_EOF; + } +} + + +// =========================================================================== +// Reads a long in LSB order from the given gz_stream. Sets +int unzlocal_getShort (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +int unzlocal_getLong (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +// My own strcmpi / strcasecmp +int strcmpcasenosensitive_internal (const char* fileName1,const char *fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= (char)0x20; + if ((c2>='a') && (c2<='z')) + c2 -= (char)0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + + + +// +// Compare two filename (fileName1,fileName2). +// If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) +// If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) +// +int unzStringFileNameCompare (const char*fileName1,const char*fileName2,int iCaseSensitivity) +{ if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); + else return strcmpcasenosensitive_internal(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + + +// Locate the Central directory of a zipfile (at the end, just before +// the global comment) +uLong unzlocal_SearchCentralDir(LUFILE *fin) +{ if (lufseek(fin,0,SEEK_END) != 0) return 0; + uLong uSizeFile = luftell(fin); + + uLong uMaxBack=0xffff; // maximum size of global comment + if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; + + unsigned char *buf = (unsigned char*)zmalloc(BUFREADCOMMENT+4); + if (buf==NULL) return 0; + uLong uPosFound=0; + + uLong uBackRead = 4; + while (uBackReaduMaxBack) uBackRead = uMaxBack; + else uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (lufseek(fin,uReadPos,SEEK_SET)!=0) break; + if (lufread(buf,(uInt)uReadSize,1,fin)!=1) break; + for (i=(int)uReadSize-3; (i--)>0;) + { if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { uPosFound = uReadPos+i; break; + } + } + if (uPosFound!=0) break; + } + if (buf) zfree(buf); + return uPosFound; +} + + +int unzGoToFirstFile (unzFile file); +int unzCloseCurrentFile (unzFile file); + +// Open a Zip file. +// If the zipfile cannot be opened (file don't exist or in not valid), return NULL. +// Otherwise, the return value is a unzFile Handle, usable with other unzip functions +unzFile unzOpenInternal(LUFILE *fin) +{ + zopenerror = ZR_OK; //+++1.2 + if (fin==NULL) { zopenerror = ZR_ARGS; return NULL; } //+++1.2 + if (unz_copyright[0]!=' ') {lufclose(fin); zopenerror = ZR_CORRUPT; return NULL; } //+++1.2 + + int err=UNZ_OK; + unz_s us; + uLong central_pos,uL; + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) err=UNZ_ERRNO; + if (lufseek(fin,central_pos,SEEK_SET)!=0) err=UNZ_ERRNO; + // the signature, already checked + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) err=UNZ_ERRNO; + // number of this disk + uLong number_disk; // number of the current dist, used for spanning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; + // number of the disk with the start of the central directory + uLong number_disk_with_CD; // number the the disk with central dir, used for spaning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir on this disk + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir + uLong number_entry_CD; // total number of entries in the central dir (same than number_entry on nospan) + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; + if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; + // size of the central directory + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // offset of start of central directory with respect to the starting disk number + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // zipfile comment length + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; + if ((central_pos+fin->initial_offsetinitial_offset - (us.offset_central_dir+us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + fin->initial_offset = 0; // since the zipfile itself is expected to handle this + + unz_s *s = (unz_s*)zmalloc(sizeof(unz_s)); + *s=us; + unzGoToFirstFile((unzFile)s); + return (unzFile)s; +} + + + +// Close a ZipFile opened with unzipOpen. +// If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), +// these files MUST be closed with unzipCloseCurrentFile before call unzipClose. +// return UNZ_OK if there is no problem. +int unzClose (unzFile file) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + lufclose(s->file); + if (s) zfree(s); // unused s=0; + return UNZ_OK; +} + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +// Translate date/time from Dos format to tm_unz (readable more easilty) +void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +// Get Info about the current file in the zipfile, with internal only info +int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize); + +int unzlocal_GetCurrentFileInfoInternal (unzFile file, unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, char *szFileName, + uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (lufseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + // we check the magic + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (lufread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (lufread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + {} // unused lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (lufread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + //unused lSeek+=file_info.size_file_comment - uSizeRead; + } + else {} //unused lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, + char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, szComment,commentBufferSize); +} + + +// Set the current file of the zipfile to the first file. +// return UNZ_OK if there is no problem +int unzGoToFirstFile (unzFile file) +{ + int err; + unz_s* s; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Set the current file of the zipfile to the next file. +// return UNZ_OK if there is no problem +// return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +int unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Try locate the file szFileName in the zipfile. +// For the iCaseSensitivity signification, see unzStringFileNameCompare +// return value : +// UNZ_OK if the file is found. It becomes the current file. +// UNZ_END_OF_LIST_OF_FILE if the file is not found +int unzLocateFile (unzFile file, const TCHAR *szFileName, int iCaseSensitivity) +{ + unz_s* s; + int err; + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + if (file==NULL) + return UNZ_PARAMERROR; + + if (_tcslen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + char szFileNameA[MAX_PATH]; + +#ifdef _UNICODE + GetAnsiFileName(szFileName, szFileNameA, MAX_PATH-1); +#else + strcpy(szFileNameA, szFileName); +#endif + + // support Windows subdirectory by:daviyang35 + int iLen=strlen(szFileNameA); + for (int i=0;icurrent_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName,szFileNameA,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +// Read the local header of the current zipfile +// Check the coherency of the local header and info in the end of central +// directory about this file +// store in *piSizeVar the size of extra info in local header +// (filename and size of extra field data) +int unzlocal_CheckCurrentFileCoherencyHeader (unz_s *s,uInt *piSizeVar, + uLong *poffset_local_extrafield, uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (lufseek(s->file,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +// else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) +// err=UNZ_BADZIPFILE; + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // date/time + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // crc + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size compr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size uncompr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + + + + + +// Open for reading data the current file in the zipfile. +// If there is no error and the file is opened, the return value is UNZ_OK. +int unzOpenCurrentFile (unzFile file) +{ + int err; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; // offset of the local extra field + uInt size_local_extrafield; // size of the local extra field + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*)zmalloc(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)zmalloc(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); //unused pfile_in_zip_read_info=0; + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) + { // unused err=UNZ_BADZIPFILE; + } + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + // windowBits is passed < 0 to tell that there is no zlib header. + // Note that in this case inflate *requires* an extra "dummy" byte + // after the compressed stream in order to complete decompression and + // return Z_STREAM_END. + // In unzip, i don't wait absolutely Z_STREAM_END because I known the + // size of both compressed and uncompressed data + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +// Read bytes from the current file. +// buf contain buffer where data must be copied +// len the size of buf. +// return the number of byte copied if somes bytes are copied +// return 0 if the end of file was reached +// return <0 with error code if there is an error +// (UNZ_ERRNO for IO error, or zLib error for uncompress error) +int unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ int err=UNZ_OK; + uInt iRead = 0; + + unz_s *s = (unz_s*)file; + if (s==NULL) return UNZ_PARAMERROR; + + file_in_zip_read_info_s* pfile_in_zip_read_info = s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; + if ((pfile_in_zip_read_info->read_buffer == NULL)) return UNZ_END_OF_LIST_OF_FILE; + if (len==0) return 0; + + pfile_in_zip_read_info->stream.next_out = (Byte*)buf; + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + { pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + } + + while (pfile_in_zip_read_info->stream.avail_out>0) + { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) + { uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) return UNZ_EOF; + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) return UNZ_ERRNO; + if (lufread(pfile_in_zip_read_info->read_buffer,uReadThis,1,pfile_in_zip_read_info->file)!=1) return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + pfile_in_zip_read_info->stream.next_in = (Byte*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) + { uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + } + else + { uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + } + for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); + } + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,pfile_in_zip_read_info->stream.next_out,uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { uLong uTotalOutBefore,uTotalOutAfter; + const Byte *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + err=inflate(&pfile_in_zip_read_info->stream,flush); + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,bufBefore,(uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; //+++1.3 + //if (err==Z_STREAM_END) return (iRead==len) ? UNZ_EOF : iRead; //+++1.2 + + if (err != Z_OK) break; + } + } + + if (err==Z_OK) return iRead; + + return iRead; +} + + +// Give the current position in uncompressed data +z_off_t unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +// return 1 if the end of file was reached, 0 elsewhere +int unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// if buf==NULL, it return the size of the local extra field that can be read +// if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. +// the return value is the number of bytes copied in buf, or (if <0) the error code +int unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (lufread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +// Close the file in zip opened with unzipOpenCurrentFile +// Return UNZ_CRCERROR if all the file was read but the CRC is not good +int unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + if (pfile_in_zip_read_info->read_buffer!=0) + { void *buf = pfile_in_zip_read_info->read_buffer; + zfree(buf); + pfile_in_zip_read_info->read_buffer=0; + } + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); // unused pfile_in_zip_read_info=0; + + s->pfile_in_zip_read=NULL; + + return err; +} + + +// Get the global comment string of the ZipFile, in the szComment buffer. +// uSizeBuf is the size of the szComment buffer. +// return the number of byte copied or an error code <0 +int unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) +{ //int err=UNZ_OK; + unz_s* s; + uLong uReadThis ; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; + if (lufseek(s->file,s->central_pos+22,SEEK_SET)!=0) return UNZ_ERRNO; + if (uReadThis>0) + { *szComment='\0'; + if (lufread(szComment,(uInt)uReadThis,1,s->file)!=1) return UNZ_ERRNO; + } + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + + + + + +int unzOpenCurrentFile (unzFile file); +int unzReadCurrentFile (unzFile file, void *buf, unsigned len); +int unzCloseCurrentFile (unzFile file); + + +FILETIME timet2filetime(time_t timer) +{ + struct tm *tm = gmtime(&timer); + SYSTEMTIME st; + st.wYear = (WORD)(tm->tm_year+1900); + st.wMonth = (WORD)(tm->tm_mon+1); + st.wDay = (WORD)(tm->tm_mday); + st.wHour = (WORD)(tm->tm_hour); + st.wMinute = (WORD)(tm->tm_min); + st.wSecond = (WORD)(tm->tm_sec); + st.wMilliseconds=0; + FILETIME ft; + SystemTimeToFileTime(&st,&ft); + return ft; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class TUnzip +{ public: + TUnzip() : uf(0), currentfile(-1), czei(-1) {} + + unzFile uf; int currentfile; ZIPENTRY cze; int czei; + TCHAR rootdir[MAX_PATH]; + + ZRESULT Open(void *z,unsigned int len,DWORD flags); + ZRESULT Get(int index,ZIPENTRY *ze); + ZRESULT Find(const TCHAR *name,bool ic,int *index,ZIPENTRY *ze); + ZRESULT Unzip(int index,void *dst,unsigned int len,DWORD flags); + ZRESULT Close(); +}; + + +ZRESULT TUnzip::Open(void *z,unsigned int len,DWORD flags) +{ + if (uf!=0 || currentfile!=-1) + return ZR_NOTINITED; + GetCurrentDirectory(MAX_PATH,rootdir); + _tcscat(rootdir,_T("\\")); + if (flags==ZIP_HANDLE) + { + DWORD type = GetFileType(z); + if (type!=FILE_TYPE_DISK) + return ZR_SEEK; + } + ZRESULT e; + LUFILE *f = lufopen(z,len,flags,&e); + if (f==NULL) + return e; + uf = unzOpenInternal(f); + //return ZR_OK; + return zopenerror; //+++1.2 +} + +ZRESULT TUnzip::Get(int index,ZIPENTRY *ze) +{ if (index<-1 || index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index==czei && index!=-1) {memcpy(ze,&cze,sizeof(ZIPENTRY)); return ZR_OK;} + if (index==-1) + { ze->index = uf->gi.number_entry; + ze->name[0]=0; + ze->attr=0; + ze->atime.dwLowDateTime=0; ze->atime.dwHighDateTime=0; + ze->ctime.dwLowDateTime=0; ze->ctime.dwHighDateTime=0; + ze->mtime.dwLowDateTime=0; ze->mtime.dwHighDateTime=0; + ze->comp_size=0; + ze->unc_size=0; + return ZR_OK; + } + if (index<(int)uf->num_file) unzGoToFirstFile(uf); + while ((int)uf->num_filefile,offset,SEEK_SET)!=0) return ZR_READ; + char *extra = new char[extralen]; + if (lufread(extra,1,(uInt)extralen,uf->file)!=extralen) {delete[] extra; return ZR_READ;} + // + ze->index=uf->num_file; + strcpy(ze->name,fn); + // zip has an 'attribute' 32bit value. Its lower half is windows stuff + // its upper half is standard unix attr. + unsigned long a = ufi.external_fa; + bool uisdir = (a&0x40000000)!=0; + //bool uwriteable= (a&0x08000000)!=0; + bool uwriteable= (a&0x00800000)!=0; // ***hd*** + //bool ureadable= (a&0x01000000)!=0; + //bool uexecutable=(a&0x00400000)!=0; + bool wreadonly= (a&0x00000001)!=0; + bool whidden= (a&0x00000002)!=0; + bool wsystem= (a&0x00000004)!=0; + bool wisdir= (a&0x00000010)!=0; + bool warchive= (a&0x00000020)!=0; + ze->attr=FILE_ATTRIBUTE_NORMAL; + if (uisdir || wisdir) ze->attr |= FILE_ATTRIBUTE_DIRECTORY; + if (warchive) ze->attr|=FILE_ATTRIBUTE_ARCHIVE; + if (whidden) ze->attr|=FILE_ATTRIBUTE_HIDDEN; + if (!uwriteable||wreadonly) ze->attr|=FILE_ATTRIBUTE_READONLY; + if (wsystem) ze->attr|=FILE_ATTRIBUTE_SYSTEM; + ze->comp_size = ufi.compressed_size; + ze->unc_size = ufi.uncompressed_size; + // + WORD dostime = (WORD)(ufi.dosDate&0xFFFF); + WORD dosdate = (WORD)((ufi.dosDate>>16)&0xFFFF); + FILETIME ft; + DosDateTimeToFileTime(dosdate,dostime,&ft); + ze->atime=ft; ze->ctime=ft; ze->mtime=ft; + // the zip will always have at least that dostime. But if it also has + // an extra header, then we'll instead get the info from that. + unsigned int epos=0; + while (epos+4mtime = timet2filetime(mtime); + } + if (hasatime) + { time_t atime = *(__time32_t*)(extra+epos); epos+=4; + ze->atime = timet2filetime(atime); + } + if (hasctime) + { time_t ctime = *(__time32_t*)(extra+epos); + ze->ctime = timet2filetime(ctime); + } + break; + } + // + if (extra!=0) delete[] extra; + memcpy(&cze,ze,sizeof(ZIPENTRY)); czei=index; + return ZR_OK; +} + +ZRESULT TUnzip::Find(const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + int res = unzLocateFile(uf,name,ic?CASE_INSENSITIVE:CASE_SENSITIVE); + if (res!=UNZ_OK) + { + if (index!=0) + *index=-1; + if (ze!=NULL) + { + ZeroMemory(ze,sizeof(ZIPENTRY)); ze->index=-1; + } + return ZR_NOTFOUND; + } + if (currentfile!=-1) + unzCloseCurrentFile(uf); currentfile=-1; + int i = (int)uf->num_file; + if (index!=NULL) + *index=i; + if (ze!=NULL) + { + ZRESULT zres = Get(i,ze); + if (zres!=ZR_OK) + return zres; + } + return ZR_OK; +} + +void EnsureDirectory(const TCHAR *rootdir, const TCHAR *dir) +{ + if (dir==NULL || dir[0] == _T('\0')) + return; + const TCHAR *lastslash = dir, *c = lastslash; + while (*c != _T('\0')) + { + if (*c==_T('/') || *c==_T('\\')) + lastslash=c; + c++; + } + const TCHAR *name=lastslash; + if (lastslash!=dir) + { + TCHAR tmp[MAX_PATH]; + _tcsncpy(tmp, dir, lastslash-dir); + tmp[lastslash-dir] = _T('\0'); + EnsureDirectory(rootdir,tmp); + name++; + } + TCHAR cd[MAX_PATH]; + _tcscpy(cd,rootdir); + //_tcscat(cd,name); + _tcscat(cd,dir); //+++1.2 + CreateDirectory(cd,NULL); +} + +ZRESULT TUnzip::Unzip(int index,void *dst,unsigned int len,DWORD flags) +{ + if (flags!=ZIP_MEMORY && flags!=ZIP_FILENAME && flags!=ZIP_HANDLE) + return ZR_ARGS; + if (flags==ZIP_MEMORY) + { + if (index!=currentfile) + { + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (index<(int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_file0) + return ZR_MORE; + unzCloseCurrentFile(uf); + currentfile=-1; + if (res==0) + return ZR_OK; + else + return ZR_FLATE; + } + + // otherwise we're writing to a handle or a file + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index >= (int)uf->gi.number_entry) + return ZR_ARGS; + if (index < (int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_filelen) n=len-1; + strncpy(buf,msg,n); buf[n]=0; + return mlen; +} + + +typedef struct +{ DWORD flag; + TUnzip *unz; +} TUnzipHandleData; + +HZIP OpenZipU(void *z,unsigned int len,DWORD flags) +{ + TUnzip *unz = new TUnzip(); + lasterrorU = unz->Open(z,len,flags); + if (lasterrorU!=ZR_OK) + { + delete unz; + return 0; + } + TUnzipHandleData *han = new TUnzipHandleData; + han->flag=1; + han->unz=unz; + return (HZIP)han; +} + +ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Get(index,ze); + return lasterrorU; +} + +ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Get(index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + return lasterrorU; +} + +ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Find(name,ic,index,ze); + return lasterrorU; +} + +ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Find(name,ic,index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + + return lasterrorU; +} + +ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Unzip(index,dst,len,flags); + return lasterrorU; +} + +ZRESULT CloseZipU(HZIP hz) +{ if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} + TUnzip *unz = han->unz; + lasterrorU = unz->Close(); + delete unz; + delete han; + return lasterrorU; +} + +bool IsZipHandleU(HZIP hz) +{ if (hz==0) return true; + TUnzipHandleData *han = (TUnzipHandleData*)hz; + return (han->flag==1); +} + + diff --git a/Demos/gamebox/icon1.png b/Demos/gamebox/icon1.png new file mode 100644 index 00000000..f2433a98 Binary files /dev/null and b/Demos/gamebox/icon1.png differ diff --git a/Demos/gamebox/resource.h b/Demos/gamebox/resource.h new file mode 100644 index 00000000..96147db3 Binary files /dev/null and b/Demos/gamebox/resource.h differ diff --git a/Demos/gamebox/stb_image.c b/Demos/gamebox/stb_image.c new file mode 100644 index 00000000..ebb11195 --- /dev/null +++ b/Demos/gamebox/stb_image.c @@ -0,0 +1,4677 @@ +#define STBI_NO_STDIO +#define STBI_NO_WRITE +#define STBI_NO_HDR + +/* stbi-1.33 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c + when you control the images you're loading + no warranty implied; use at your own risk + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline (no JPEG progressive) + PNG 8-bit only + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - overridable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) + + Latest revisions: + 1.33 (2011-07-14) minor fixes suggested by Dave Moore + 1.32 (2011-07-13) info support for all filetypes (SpartanJ) + 1.31 (2011-06-19) a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) added ability to load files via io callbacks (Ben Wenger) + 1.29 (2010-08-16) various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) cast-to-uint8 to fix warnings (Laurent Gomila) + allow trailing 0s at end of image data (Laurent Gomila) + 1.26 (2010-07-24) fix bug in file buffering for PNG reported by SpartanJ + + See end of file for full revision history. + + TODO: + stbi_info support for BMP,PSD,HDR,PIC + + + ============================ Contributors ========================= + + Image formats Optimizations & bugfixes + Sean Barrett (jpeg, png, bmp) Fabian "ryg" Giesen + Nicolas Schulz (hdr, psd) + Jonathan Dummer (tga) Bug fixes & warning fixes + Jean-Marc Lienher (gif) Marc LeBlanc + Tom Seddon (pic) Christpher Lloyd + Thatcher Ulrich (psd) Dave Moore + Won Chun + the Horde3D community + Extensions, features Janez Zemva + Jetro Lauha (stbi_info) Jonathan Blow + James "moose2000" Brown (iPhone PNG) Laurent Gomila + Ben "Disch" Wenger (io callbacks) Aruelien Pocheville + Martin "SpartanJ" Golini Ryamond Barbiero + David Woo + + + If your name should be here but isn't, let Sean know. + +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// To get a header file for this, either cut and paste the header, +// or create stb_image.h, #define STBI_HEADER_FILE_ONLY, and +// then include stb_image.c from it. + +//// begin header file //////////////////////////////////////////////////// +// +// Limitations: +// - no jpeg progressive support +// - non-HDR formats support 8-bit samples only (jpeg, png) +// - no delayed line count (jpeg) -- IJG doesn't support either +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to easily see if it's opaque. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB; nominally they +// would silently load as BGR, except the existing code should have just +// failed on such iPhone PNGs. But you can disable this conversion by +// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through. +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). + + +#ifndef STBI_NO_STDIO + +#if defined(_MSC_VER) && _MSC_VER >= 0x1400 +#define _CRT_SECURE_NO_WARNINGS // suppress bogus warnings about fopen() +#endif + +#include +#endif + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,unsigned n); // skip the next 'n' bytes + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +extern stbi_uc *stbi_load_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_HDR + extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif + + extern float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + extern void stbi_hdr_to_ldr_gamma(float gamma); + extern void stbi_hdr_to_ldr_scale(float scale); + + extern void stbi_ldr_to_hdr_gamma(float gamma); + extern void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_HDR + +// stbi_is_hdr is always defined +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename); +extern int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +extern const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +extern void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +extern int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +extern void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +extern void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + + +// ZLIB client - used by PNG, available for other purposes + +extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +// define faster low-level operations (typically SIMD support) +#ifdef STBI_SIMD +typedef void (*stbi_idct_8x8)(stbi_uc *out, int out_stride, short data[64], unsigned short *dequantize); +// compute an integer IDCT on "input" +// input[x] = data[x] * dequantize[x] +// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' +// CLAMP results to 0..255 +typedef void (*stbi_YCbCr_to_RGB_run)(stbi_uc *output, stbi_uc const *y, stbi_uc const *cb, stbi_uc const *cr, int count, int step); +// compute a conversion from YCbCr to RGB +// 'count' pixels +// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B +// y: Y input channel +// cb: Cb input channel; scale/biased to be 0..255 +// cr: Cr input channel; scale/biased to be 0..255 + +extern void stbi_install_idct(stbi_idct_8x8 func); +extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); +#endif // STBI_SIMD + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifndef STBI_HEADER_FILE_ONLY + +#ifndef STBI_NO_HDR +#include // ldexp +#include // strcmp, strtok +#endif + +#ifndef STBI_NO_STDIO +#include +#endif +#include +#include +#include +#include + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +// implementation: +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; +typedef unsigned int uint; + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(uint32)==4 ? 1 : -1]; + +#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE) +#define STBI_NO_WRITE +#endif + +#define STBI_NOTUSED(v) (void)sizeof(v) + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +/////////////////////////////////////////////// +// +// stbi struct and start_xxx functions + +// stbi structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + uint8 buffer_start[128]; + + uint8 *img_buffer, *img_buffer_end; + uint8 *img_buffer_original; +} stbi; + + +static void refill_buffer(stbi *s); + +// initialize a memory-decode context +static void start_mem(stbi *s, uint8 const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (uint8 *) buffer; + s->img_buffer_end = (uint8 *) buffer+len; +} + +// initialize a callback-based context +static void start_callbacks(stbi *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + refill_buffer(s); +} + +#ifndef STBI_NO_STDIO + +static int stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stdio_skip(void *user, unsigned n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi_stdio_callbacks = +{ + stdio_read, + stdio_skip, + stdio_eof, +}; + +static void start_file(stbi *s, FILE *f) +{ + start_callbacks(s, &stbi_stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi_rewind(stbi *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; +} + +static int stbi_jpeg_test(stbi *s); +static stbi_uc *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp); +static int stbi_png_test(stbi *s); +static stbi_uc *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_png_info(stbi *s, int *x, int *y, int *comp); +static int stbi_bmp_test(stbi *s); +static stbi_uc *stbi_bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_test(stbi *s); +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_info(stbi *s, int *x, int *y, int *comp); +static int stbi_psd_test(stbi *s); +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_hdr_test(stbi *s); +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_pic_test(stbi *s); +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_test(stbi *s); +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp); + + +// this is not threadsafe +static const char *failure_reason; + +const char *stbi_failure_reason(void) +{ + return failure_reason; +} + +static int e(const char *str) +{ + failure_reason = str; + return 0; +} + +// e - error +// epf - error returning pointer to float +// epuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define e(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define e(x,y) e(y) +#else + #define e(x,y) e(x) +#endif + +#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) +#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) + +void stbi_image_free(void *retval_from_stbi_load) +{ + free(retval_from_stbi_load); +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static unsigned char *stbi_load_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + if (stbi_jpeg_test(s)) return stbi_jpeg_load(s,x,y,comp,req_comp); + if (stbi_png_test(s)) return stbi_png_load(s,x,y,comp,req_comp); + if (stbi_bmp_test(s)) return stbi_bmp_load(s,x,y,comp,req_comp); + if (stbi_gif_test(s)) return stbi_gif_load(s,x,y,comp,req_comp); + if (stbi_psd_test(s)) return stbi_psd_load(s,x,y,comp,req_comp); + if (stbi_pic_test(s)) return stbi_pic_load(s,x,y,comp,req_comp); + + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) { + float *hdr = stbi_hdr_load(s, x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + // test tga last because it's a crappy test! + if (stbi_tga_test(s)) + return stbi_tga_load(s,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + unsigned char *result; + if (!f) return epuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_load_main(&s,x,y,comp,req_comp); +} +#endif //!STBI_NO_STDIO + +unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_HDR + +float *stbi_loadf_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) + return stbi_hdr_load(s,x,y,comp,req_comp); + #endif + data = stbi_load_main(s, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} + +float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + float *result; + if (!f) return epf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_HDR + +// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is +// defined, for API simplicity; if STBI_NO_HDR is defined, it always +// reports false! + +int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi s; + start_mem(&s,buffer,len); + return stbi_hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename) +{ + FILE *f = fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +extern int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi s; + start_file(&s,f); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} + +#ifndef STBI_NO_HDR +static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; +static float l2h_gamma=2.2f, l2h_scale=1.0f; + +void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } +void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } + +void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } +void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + SCAN_load=0, + SCAN_type, + SCAN_header +}; + +static void refill_buffer(stbi *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_end-1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static int get8(stbi *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int at_eof(stbi *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +stbi_inline static uint8 get8u(stbi *s) +{ + return (uint8) get8(s); +} + +static void skip(stbi *s, int n) +{ + if (s->io.read) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int getn(stbi *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int get16(stbi *s) +{ + int z = get8(s); + return (z << 8) + get8(s); +} + +static uint32 get32(stbi *s) +{ + uint32 z = get16(s); + return (z << 16) + get16(s); +} + +static int get16le(stbi *s) +{ + int z = get8(s); + return z + (get8(s) << 8); +} + +static uint32 get32le(stbi *s) +{ + uint32 z = get16le(s); + return z + (get16le(s) << 16); +} + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static uint8 compute_y(int r, int g, int b) +{ + return (uint8) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, uint x, uint y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + assert(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) malloc(req_comp * x * y); + if (good == NULL) { + free(data); + return epuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: assert(0); + } + #undef CASE + } + + free(data); + return good; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) malloc(x * y * comp * sizeof(float)); + if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + free(data); + return output; +} + +#define float2int(x) ((int) (x)) +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) malloc(x * y * comp); + if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + } + free(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) +// +// simple implementation +// - channel subsampling of at most 2 in each dimension +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - uses a lot of intermediate memory, could cache poorly +// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 +// stb_jpeg: 1.34 seconds (MSVC6, default release build) +// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) +// IJL11.dll: 1.08 seconds (compiled by intel) +// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) +// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + uint8 fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + uint16 code[256]; + uint8 values[256]; + uint8 size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} huffman; + +typedef struct +{ + #ifdef STBI_SIMD + unsigned short dequant2[4][64]; + #endif + stbi *s; + huffman huff_dc[4]; + huffman huff_ac[4]; + uint8 dequant[4][64]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + uint8 *data; + void *raw_data; + uint8 *linebuf; + } img_comp[4]; + + uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int scan_n, order[4]; + int restart_interval, todo; +} jpeg; + +static int build_huffman(huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (uint8) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (uint16) (code++); + if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (uint8) i; + } + } + } + return 1; +} + +static void grow_buffer_unsafe(jpeg *j) +{ + do { + int b = j->nomore ? 0 : get8(j->s); + if (b == 0xff) { + int c = get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int decode(jpeg *j, huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & bmask[k]) + h->delta[k]; + assert((((j->code_buffer) >> (32 - h->size[c])) & bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +stbi_inline static int extend_receive(jpeg *j, int n) +{ + unsigned int m = 1 << (n-1); + unsigned int k; + if (j->code_bits < n) grow_buffer_unsafe(j); + + #if 1 + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~bmask[n]; + k &= bmask[n]; + j->code_bits -= n; + #else + k = (j->code_buffer >> (32 - n)) & bmask[n]; + j->code_bits -= n; + j->code_buffer <<= n; + #endif + // the following test is probably a random branch that won't + // predict well. I tried to table accelerate it but failed. + // maybe it's compiling as a conditional move? + if (k < m) + return (-1 << n) + k + 1; + else + return k; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static uint8 dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int b) +{ + int diff,dc,k; + int t = decode(j, hdc); + if (t < 0) return e("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) dc; + + // decode AC components, see JPEG spec + k = 1; + do { + int r,s; + int rs = decode(j, hac); + if (rs < 0) return e("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + data[dezigzag[k++]] = (short) extend_receive(j,s); + } + } while (k < 64); + return 1; +} + +// take a -128..127 value and clamp it and convert to 0..255 +stbi_inline static uint8 clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (uint8) x; +} + +#define f2f(x) (int) (((x) * 4096 + 0.5)) +#define fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * f2f(0.5411961f); \ + t2 = p1 + p3*f2f(-1.847759065f); \ + t3 = p1 + p2*f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = fsh(p2+p3); \ + t1 = fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*f2f( 1.175875602f); \ + t0 = t0*f2f( 0.298631336f); \ + t1 = t1*f2f( 2.053119869f); \ + t2 = t2*f2f( 3.072711026f); \ + t3 = t3*f2f( 1.501321110f); \ + p1 = p5 + p1*f2f(-0.899976223f); \ + p2 = p5 + p2*f2f(-2.562915447f); \ + p3 = p3*f2f(-1.961570560f); \ + p4 = p4*f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +#ifdef STBI_SIMD +typedef unsigned short stbi_dequantize_t; +#else +typedef uint8 stbi_dequantize_t; +#endif + +// .344 seconds on 3*anemones.jpg +static void idct_block(uint8 *out, int out_stride, short data[64], stbi_dequantize_t *dequantize) +{ + int i,val[64],*v=val; + stbi_dequantize_t *dq = dequantize; + uint8 *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SIMD +static stbi_idct_8x8 stbi_idct_installed = idct_block; + +void stbi_install_idct(stbi_idct_8x8 func) +{ + stbi_idct_installed = func; +} +#endif + +#define MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static uint8 get_marker(jpeg *j) +{ + uint8 x; + if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; } + x = get8u(j->s); + if (x != 0xff) return MARKER_none; + while (x == 0xff) + x = get8u(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, reset the entropy decoder and +// the dc prediction +static void reset(jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int parse_entropy_coded_data(jpeg *z) +{ + reset(z); + if (z->scan_n == 1) { + int i,j; + #ifdef STBI_SIMD + __declspec(align(16)) + #endif + short data[64]; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } else { // interleaved! + int i,j,k,x,y; + short data[64]; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } + return 1; +} + +static int process_marker(jpeg *z, int m) +{ + int L; + switch (m) { + case MARKER_none: // no marker found + return e("expected marker","Corrupt JPEG"); + + case 0xC2: // SOF - progressive + return e("progressive jpeg","JPEG format not supported (progressive)"); + + case 0xDD: // DRI - specify restart interval + if (get16(z->s) != 4) return e("bad DRI len","Corrupt JPEG"); + z->restart_interval = get16(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = get16(z->s)-2; + while (L > 0) { + int q = get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return e("bad DQT type","Corrupt JPEG"); + if (t > 3) return e("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][dezigzag[i]] = get8u(z->s); + #ifdef STBI_SIMD + for (i=0; i < 64; ++i) + z->dequant2[t][i] = z->dequant[t][i]; + #endif + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = get16(z->s)-2; + while (L > 0) { + uint8 *v; + int sizes[16],i,m=0; + int q = get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = get8(z->s); + m += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < m; ++i) + v[i] = get8u(z->s); + L -= m; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + skip(z->s, get16(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int process_scan_header(jpeg *z) +{ + int i; + int Ls = get16(z->s); + z->scan_n = get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return e("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = get8(z->s), which; + int q = get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + get8(z->s); // should be 63, but might be 0 + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + + return 1; +} + +static int process_frame_header(jpeg *z, int scan) +{ + stbi *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG + p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = get16(s); if (s->img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = get16(s); if (s->img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires + c = get8(s); + if (c != 3 && c != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return e("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return e("bad component ID","Corrupt JPEG"); + q = get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); + z->img_comp[i].tq = get8(s); if (z->img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); + } + + if (scan != SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + free(z->img_comp[i].raw_data); + z->img_comp[i].data = NULL; + } + return e("outofmem", "Out of memory"); + } + // align blocks for installable-idct using mmx/sse + z->img_comp[i].data = (uint8*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define DNL(x) ((x) == 0xdc) +#define SOI(x) ((x) == 0xd8) +#define EOI(x) ((x) == 0xd9) +#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) +#define SOS(x) ((x) == 0xda) + +static int decode_jpeg_header(jpeg *z, int scan) +{ + int m; + z->marker = MARKER_none; // initialize cached marker to empty + m = get_marker(z); + if (!SOI(m)) return e("no SOI","Corrupt JPEG"); + if (scan == SCAN_type) return 1; + m = get_marker(z); + while (!SOF(m)) { + if (!process_marker(z,m)) return 0; + m = get_marker(z); + while (m == MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (at_eof(z->s)) return e("no SOF", "Corrupt JPEG"); + m = get_marker(z); + } + } + if (!process_frame_header(z, scan)) return 0; + return 1; +} + +static int decode_jpeg_image(jpeg *j) +{ + int m; + j->restart_interval = 0; + if (!decode_jpeg_header(j, SCAN_load)) return 0; + m = get_marker(j); + while (!EOI(m)) { + if (SOS(m)) { + if (!process_scan_header(j)) return 0; + if (!parse_entropy_coded_data(j)) return 0; + if (j->marker == MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!at_eof(j->s)) { + int x = get8(j->s); + if (x == 255) { + j->marker = get8u(j->s); + break; + } else if (x != 0) { + return 0; + } + } + // if we reach eof without hitting a marker, get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!process_marker(j, m)) return 0; + } + m = get_marker(j); + } + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1, + int w, int hs); + +#define div4(x) ((uint8) ((x) >> 2)) + +static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + uint8 *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = div4(n+input[i-1]); + out[i*2+1] = div4(n+input[i+1]); + } + out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define div16(x) ((uint8) ((x) >> 4)) + +static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = div16(3*t0 + t1 + 8); + out[i*2 ] = div16(3*t1 + t0 + 8); + } + out[w*2-1] = div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + in_far = in_far; + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) + +// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) +// VC6 without processor=Pro is generating multiple LEAs per multiply! +static void YCbCr_to_RGB_row(uint8 *out, const uint8 *y, const uint8 *pcb, const uint8 *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (uint8)r; + out[1] = (uint8)g; + out[2] = (uint8)b; + out[3] = 255; + out += step; + } +} + +#ifdef STBI_SIMD +static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; + +void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) +{ + stbi_YCbCr_installed = func; +} +#endif + + +// clean up the temporary component buffers +static void cleanup_jpeg(jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].data) { + free(j->img_comp[i].raw_data); + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].linebuf) { + free(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + uint8 *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi_resample; + +static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + // validate req_comp + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + z->s->img_n = 0; + + // load a jpeg image from whichever source + if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + uint i,j; + uint8 *output; + uint8 *coutput[4]; + + stbi_resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (uint8 *) malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; + else r->resample = resample_row_generic; + } + + // can't error after this so, this is safe + output = (uint8 *) malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + uint8 *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + uint8 *y = coutput[0]; + if (z->s->img_n == 3) { + #ifdef STBI_SIMD + stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s.img_x, n); + #else + YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s->img_x, n); + #endif + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + uint8 *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + jpeg j; + j.s = s; + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +static int stbi_jpeg_test(stbi *s) +{ + int r; + jpeg j; + j.s = s; + r = decode_jpeg_header(&j, SCAN_type); + stbi_rewind(s); + return r; +} + +static int stbi_jpeg_info_raw(jpeg *j, int *x, int *y, int *comp) +{ + if (!decode_jpeg_header(j, SCAN_header)) { + stbi_rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp) +{ + jpeg j; + j.s = s; + return stbi_jpeg_info_raw(&j, x, y, comp); +} + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define ZFAST_BITS 9 // accelerate all cases in default tables +#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + uint16 fast[1 << ZFAST_BITS]; + uint16 firstcode[16]; + int maxcode[17]; + uint16 firstsymbol[16]; + uint8 size[288]; + uint16 value[288]; +} zhuffman; + +stbi_inline static int bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int bit_reverse(int v, int bits) +{ + assert(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return bitreverse16(v) >> (16-bits); +} + +static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 255, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + assert(sizes[i] <= (1 << i)); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (uint16) code; + z->firstsymbol[i] = (uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + z->size[c] = (uint8)s; + z->value[c] = (uint16)i; + if (s <= ZFAST_BITS) { + int k = bit_reverse(next_code[s],s); + while (k < (1 << ZFAST_BITS)) { + z->fast[k] = (uint16) c; + k += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + uint8 *zbuffer, *zbuffer_end; + int num_bits; + uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + zhuffman z_length, z_distance; +} zbuf; + +stbi_inline static int zget8(zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void fill_bits(zbuf *z) +{ + do { + assert(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int zreceive(zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +stbi_inline static int zhuffman_decode(zbuf *a, zhuffman *z) +{ + int b,s,k; + if (a->num_bits < 16) fill_bits(a); + b = z->fast[a->code_buffer & ZFAST_MASK]; + if (b < 0xffff) { + s = z->size[b]; + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; + } + + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = bit_reverse(a->code_buffer, 16); + for (s=ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + assert(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +static int expand(zbuf *z, int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + if (!z->z_expandable) return e("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) realloc(z->zout_start, limit); + if (q == NULL) return e("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int length_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int length_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int dist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int parse_huffman_block(zbuf *a) +{ + for(;;) { + int z = zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes + if (a->zout >= a->zout_end) if (!expand(a, 1)) return 0; + *a->zout++ = (char) z; + } else { + uint8 *p; + int len,dist; + if (z == 256) return 1; + z -= 257; + len = length_base[z]; + if (length_extra[z]) len += zreceive(a, length_extra[z]); + z = zhuffman_decode(a, &a->z_distance); + if (z < 0) return e("bad huffman code","Corrupt PNG"); + dist = dist_base[z]; + if (dist_extra[z]) dist += zreceive(a, dist_extra[z]); + if (a->zout - a->zout_start < dist) return e("bad dist","Corrupt PNG"); + if (a->zout + len > a->zout_end) if (!expand(a, len)) return 0; + p = (uint8 *) (a->zout - dist); + while (len--) + *a->zout++ = *p++; + } + } +} + +static int compute_huffman_codes(zbuf *a) +{ + static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + zhuffman z_codelength; + uint8 lencodes[286+32+137];//padding for maximum single op + uint8 codelength_sizes[19]; + int i,n; + + int hlit = zreceive(a,5) + 257; + int hdist = zreceive(a,5) + 1; + int hclen = zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (uint8) s; + } + if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = zhuffman_decode(a, &z_codelength); + assert(c >= 0 && c < 19); + if (c < 16) + lencodes[n++] = (uint8) c; + else if (c == 16) { + c = zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + assert(c == 18); + c = zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); + if (!zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int parse_uncompressed_block(zbuf *a) +{ + uint8 header[4]; + int len,nlen,k; + if (a->num_bits & 7) + zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (uint8) (a->code_buffer & 255); // wtf this warns? + a->code_buffer >>= 8; + a->num_bits -= 8; + } + assert(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = (uint8) zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return e("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!expand(a, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int parse_zlib_header(zbuf *a) +{ + int cmf = zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = zget8(a); + if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static uint8 default_length[288], default_distance[32]; +static void init_defaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) default_length[i] = 8; + for ( ; i <= 255; ++i) default_length[i] = 9; + for ( ; i <= 279; ++i) default_length[i] = 7; + for ( ; i <= 287; ++i) default_length[i] = 8; + + for (i=0; i <= 31; ++i) default_distance[i] = 5; +} + +int stbi_png_partial; // a quick hack to only allow decoding some of a PNG... I should implement real streaming support instead +static int parse_zlib(zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = zreceive(a,1); + type = zreceive(a,2); + if (type == 0) { + if (!parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!default_distance[31]) init_defaults(); + if (!zbuild_huffman(&a->z_length , default_length , 288)) return 0; + if (!zbuild_huffman(&a->z_distance, default_distance, 32)) return 0; + } else { + if (!compute_huffman_codes(a)) return 0; + } + if (!parse_huffman_block(a)) return 0; + } + if (stbi_png_partial && a->zout - a->zout_start > 65536) + break; + } while (!final); + return 1; +} + +static int do_zlib(zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return parse_zlib(a, parse_header); +} + +char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer+len; + if (do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + + +typedef struct +{ + uint32 length; + uint32 type; +} chunk; + +#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static chunk get_chunk_header(stbi *s) +{ + chunk c; + c.length = get32(s); + c.type = get32(s); + return c; +} + +static int check_png_header(stbi *s) +{ + static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (get8u(s) != png_sig[i]) return e("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi *s; + uint8 *idata, *expanded, *out; +} png; + + +enum { + F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, + F_avg_first, F_paeth_first +}; + +static uint8 first_row_filter[5] = +{ + F_none, F_sub, F_none, F_avg_first, F_paeth_first +}; + +static int paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +// create the png data from post-deflated data +static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, uint32 x, uint32 y) +{ + stbi *s = a->s; + uint32 i,j,stride = x*out_n; + int k; + int img_n = s->img_n; // copy it into a local for later + assert(out_n == s->img_n || out_n == s->img_n+1); + if (stbi_png_partial) y = 1; + a->out = (uint8 *) malloc(x * y * out_n); + if (!a->out) return e("outofmem", "Out of memory"); + if (!stbi_png_partial) { + if (s->img_x == x && s->img_y == y) { + if (raw_len != (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } + } + for (j=0; j < y; ++j) { + uint8 *cur = a->out + stride*j; + uint8 *prior = cur - stride; + int filter = *raw++; + if (filter > 4) return e("invalid filter","Corrupt PNG"); + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + // handle first pixel explicitly + for (k=0; k < img_n; ++k) { + switch (filter) { + case F_none : cur[k] = raw[k]; break; + case F_sub : cur[k] = raw[k]; break; + case F_up : cur[k] = raw[k] + prior[k]; break; + case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; + case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break; + case F_avg_first : cur[k] = raw[k]; break; + case F_paeth_first: cur[k] = raw[k]; break; + } + } + if (img_n != out_n) cur[img_n] = 255; + raw += img_n; + cur += out_n; + prior += out_n; + // this is a little gross, so that we don't switch per-pixel or per-component + if (img_n == out_n) { + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; + } + #undef CASE + } else { + assert(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + return 1; +} + +static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n, int interlaced) +{ + uint8 *final; + int p; + int save; + if (!interlaced) + return create_png_image_raw(a, raw, raw_len, out_n, a->s->img_x, a->s->img_y); + save = stbi_png_partial; + stbi_png_partial = 0; + + // de-interlacing + final = (uint8 *) malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + if (!create_png_image_raw(a, raw, raw_len, out_n, x, y)) { + free(final); + return 0; + } + for (j=0; j < y; ++j) + for (i=0; i < x; ++i) + memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*out_n, + a->out + (j*x+i)*out_n, out_n); + free(a->out); + raw += (x*out_n+1)*y; + raw_len -= (x*out_n+1)*y; + } + } + a->out = final; + + stbi_png_partial = save; + return 1; +} + +static int compute_transparency(png *z, uint8 tc[3], int out_n) +{ + stbi *s = z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + assert(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int expand_palette(png *a, uint8 *palette, int len, int pal_img_n) +{ + uint32 i, pixel_count = a->s->img_x * a->s->img_y; + uint8 *p, *temp_out, *orig = a->out; + + p = (uint8 *) malloc(pixel_count * pal_img_n); + if (p == NULL) return e("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + free(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi_unpremultiply_on_load = 0; +static int stbi_de_iphone_flag = 0; + +void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi_unpremultiply_on_load = flag_true_if_should_unpremultiply; +} +void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi_de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi_de_iphone(png *z) +{ + stbi *s = z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + assert(s->img_out_n == 4); + if (stbi_unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + uint8 a = p[3]; + uint8 t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +static int parse_png_file(png *z, int scan, int req_comp) +{ + uint8 palette[1024], pal_img_n=0; + uint8 has_trans=0, tc[3]; + uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, iphone=0; + stbi *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!check_png_header(s)) return 0; + + if (scan == SCAN_type) return 1; + + for (;;) { + chunk c = get_chunk_header(s); + switch (c.type) { + case PNG_TYPE('C','g','B','I'): + iphone = stbi_de_iphone_flag; + skip(s, c.length); + break; + case PNG_TYPE('I','H','D','R'): { + int depth,color,comp,filter; + if (!first) return e("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); + s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); + s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); + depth = get8(s); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); + color = get8(s); if (color > 6) return e("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); + comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG"); + filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG"); + interlace = get8(s); if (interlace>1) return e("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + if (scan == SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return e("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case PNG_TYPE('P','L','T','E'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = get8u(s); + palette[i*4+1] = get8u(s); + palette[i*4+2] = get8u(s); + palette[i*4+3] = 255; + } + break; + } + + case PNG_TYPE('t','R','N','S'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (z->idata) return e("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = get8u(s); + } else { + if (!(s->img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); + if (c.length != (uint32) s->img_n*2) return e("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (uint8) get16(s); // non 8-bit images will be larger + } + break; + } + + case PNG_TYPE('I','D','A','T'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); + if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; } + if (ioff + c.length > idata_limit) { + uint8 *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); + z->idata = p; + } + if (!getn(s, z->idata+ioff,c.length)) return e("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case PNG_TYPE('I','E','N','D'): { + uint32 raw_len; + if (first) return e("first not IHDR", "Corrupt PNG"); + if (scan != SCAN_load) return 1; + if (z->idata == NULL) return e("no IDAT","Corrupt PNG"); + z->expanded = (uint8 *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !iphone); + if (z->expanded == NULL) return 0; // zlib should set error + free(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!create_png_image(z, z->expanded, raw_len, s->img_out_n, interlace)) return 0; + if (has_trans) + if (!compute_transparency(z, tc, s->img_out_n)) return 0; + if (iphone && s->img_out_n > 2) + stbi_de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!expand_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + free(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return e("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX chunk not known"; + invalid_chunk[0] = (uint8) (c.type >> 24); + invalid_chunk[1] = (uint8) (c.type >> 16); + invalid_chunk[2] = (uint8) (c.type >> 8); + invalid_chunk[3] = (uint8) (c.type >> 0); + #endif + return e(invalid_chunk, "PNG not supported: unknown chunk type"); + } + skip(s, c.length); + break; + } + // end of chunk, read and skip CRC + get32(s); + } +} + +static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + if (parse_png_file(p, SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + free(p->out); p->out = NULL; + free(p->expanded); p->expanded = NULL; + free(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + png p; + p.s = s; + return do_png(&p, x,y,comp,req_comp); +} + +static int stbi_png_test(stbi *s) +{ + int r; + r = check_png_header(s); + stbi_rewind(s); + return r; +} + +static int stbi_png_info_raw(png *p, int *x, int *y, int *comp) +{ + if (!parse_png_file(p, SCAN_header, 0)) { + stbi_rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi_png_info(stbi *s, int *x, int *y, int *comp) +{ + png p; + p.s = s; + return stbi_png_info_raw(&p, x, y, comp); +} + +// Microsoft/Windows BMP image + +static int bmp_test(stbi *s) +{ + int sz; + if (get8(s) != 'B') return 0; + if (get8(s) != 'M') return 0; + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + get32le(s); // discard data offset + sz = get32le(s); + if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; + return 0; +} + +static int stbi_bmp_test(stbi *s) +{ + int r = bmp_test(s); + stbi_rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *out; + unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (get8(s) != 'B' || get8(s) != 'M') return epuc("not BMP", "Corrupt BMP"); + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + offset = get32le(s); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = get16le(s); + s->img_y = get16le(s); + } else { + s->img_x = get32le(s); + s->img_y = get32le(s); + } + if (get16le(s) != 1) return epuc("bad BMP", "bad BMP"); + bpp = get16le(s); + if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = get32le(s); + if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); + get32le(s); // discard sizeof + get32le(s); // discard hres + get32le(s); // discard vres + get32le(s); // discard colorsused + get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + get32le(s); + get32le(s); + get32le(s); + get32le(s); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xffu << 16; + mg = 0xffu << 8; + mb = 0xffu << 0; + ma = 0xffu << 24; + fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 + } else { + mr = 31u << 10; + mg = 31u << 5; + mb = 31u << 0; + } + } else if (compress == 3) { + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return epuc("bad BMP", "bad BMP"); + } + } else + return epuc("bad BMP", "bad BMP"); + } + } else { + assert(hsz == 108); + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + ma = get32le(s); + get32le(s); // discard color space + for (i=0; i < 12; ++i) + get32le(s); // discard color space parameters + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) malloc(target * s->img_x * s->img_y); + if (!out) return epuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + if (hsz != 12) get8(s); + pal[i][3] = 255; + } + skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (s->img_x + 1) >> 1; + else if (bpp == 8) width = s->img_x; + else { free(out); return epuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=get8(s),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { free(out); return epuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = high_bit(mr)-7; rcount = bitcount(mr); + gshift = high_bit(mg)-7; gcount = bitcount(mr); + bshift = high_bit(mb)-7; bcount = bitcount(mr); + ashift = high_bit(ma)-7; acount = bitcount(mr); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + int a; + out[z+2] = get8u(s); + out[z+1] = get8u(s); + out[z+0] = get8u(s); + z += 3; + a = (easy == 2 ? get8(s) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } else { + for (i=0; i < (int) s->img_x; ++i) { + uint32 v = (bpp == 16 ? get16le(s) : get32le(s)); + int a; + out[z++] = (uint8) shiftsigned(v & mr, rshift, rcount); + out[z++] = (uint8) shiftsigned(v & mg, gshift, gcount); + out[z++] = (uint8) shiftsigned(v & mb, bshift, bcount); + a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } + skip(s, pad); + } + } + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} + +static stbi_uc *stbi_bmp_load(stbi *s,int *x, int *y, int *comp, int req_comp) +{ + return bmp_load(s, x,y,comp,req_comp); +} + + +// Targa Truevision - TGA +// by Jonathan Dummer + +static int tga_info(stbi *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp; + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if( sz > 1 ) { + stbi_rewind(s); + return 0; // only RGB or indexed allowed + } + sz = get8u(s); // image type + // only RGB or grey allowed, +/- RLE + if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; + skip(s,9); + tga_w = get16le(s); + if( tga_w < 1 ) { + stbi_rewind(s); + return 0; // test width + } + tga_h = get16le(s); + if( tga_h < 1 ) { + stbi_rewind(s); + return 0; // test height + } + sz = get8(s); // bits per pixel + // only RGB or RGBA or grey allowed + if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { + stbi_rewind(s); + return 0; + } + tga_comp = sz; + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp / 8; + return 1; // seems to have passed everything +} + +int stbi_tga_info(stbi *s, int *x, int *y, int *comp) +{ + return tga_info(s, x, y, comp); +} + +static int tga_test(stbi *s) +{ + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if ( sz > 1 ) return 0; // only RGB or indexed allowed + sz = get8u(s); // image type + if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + get16(s); // discard palette start + get16(s); // discard palette length + get8(s); // discard bits per palette color entry + get16(s); // discard x origin + get16(s); // discard y origin + if ( get16(s) < 1 ) return 0; // test width + if ( get16(s) < 1 ) return 0; // test height + sz = get8(s); // bits per pixel + if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed + return 1; // seems to have passed everything +} + +static int stbi_tga_test(stbi *s) +{ + int res = tga_test(s); + stbi_rewind(s); + return res; +} + +static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = get8u(s); + int tga_indexed = get8u(s); + int tga_image_type = get8u(s); + int tga_is_RLE = 0; + int tga_palette_start = get16le(s); + int tga_palette_len = get16le(s); + int tga_palette_bits = get8u(s); + int tga_x_origin = get16le(s); + int tga_y_origin = get16le(s); + int tga_width = get16le(s); + int tga_height = get16le(s); + int tga_bits_per_pixel = get8u(s); + int tga_inverted = get8u(s); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + unsigned char trans_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if ( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA + } + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) + { + tga_bits_per_pixel = tga_palette_bits; + } + + // tga info + *x = tga_width; + *y = tga_height; + if ( (req_comp < 1) || (req_comp > 4) ) + { + // just use whatever the file was + req_comp = tga_bits_per_pixel / 8; + *comp = req_comp; + } else + { + // force a new number of components + *comp = tga_bits_per_pixel/8; + } + tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); + if (!tga_data) return epuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + skip(s, tga_offset ); + // do I need to load a palette? + if ( tga_indexed ) + { + // any data to skip? (offset usually = 0) + skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); + if (!tga_palette) return epuc("outofmem", "Out of memory"); + if (!getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { + free(tga_data); + free(tga_palette); + return epuc("bad palette", "Corrupt TGA"); + } + } + // load the data + trans_data[0] = trans_data[1] = trans_data[2] = trans_data[3] = 0; + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE chunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = get8u(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = get8u(s); + if ( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = get8u(s); + } + } + // convert raw to the intermediate format + switch (tga_bits_per_pixel) + { + case 8: + // Luminous => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 16: + // Luminous,Alpha => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[1]; + break; + case 24: + // BGR => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 32: + // BGRA => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[3]; + break; + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + // convert to final format + switch (req_comp) + { + case 1: + // RGBA => Luminance + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + break; + case 2: + // RGBA => Luminance,Alpha + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + tga_data[i*req_comp+1] = trans_data[3]; + break; + case 3: + // RGBA => RGB + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + break; + case 4: + // RGBA => RGBA + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + tga_data[i*req_comp+3] = trans_data[3]; + break; + } + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * req_comp; + int index2 = (tga_height - 1 - j) * tga_width * req_comp; + for (i = tga_width * req_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + free( tga_palette ); + } + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} + +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return tga_load(s,x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +static int psd_test(stbi *s) +{ + if (get32(s) != 0x38425053) return 0; // "8BPS" + else return 1; +} + +static int stbi_psd_test(stbi *s) +{ + int r = psd_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + uint8 *out; + + // Check identifier + if (get32(s) != 0x38425053) // "8BPS" + return epuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (get16(s) != 1) + return epuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) + return epuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = get32(s); + w = get32(s); + + // Make sure the depth is 8 bits. + if (get16(s) != 8) + return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (get16(s) != 3) + return epuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + skip(s,get32(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + skip(s, get32(s) ); + + // Skip the reserved data. + skip(s, get32(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = get16(s); + if (compression > 1) + return epuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) malloc(4 * w*h); + if (!out) return epuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = get8u(s); + p += 4; + len--; + } + } else if (len > 128) { + uint8 val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = get8u(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + for (i = 0; i < pixelCount; i++) + *p = get8u(s), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + out = convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} + +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return psd_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +static int pic_is4(stbi *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int pic_test(stbi *s) +{ + int i; + + if (!pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + get8(s); + + if (!pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} pic_packet_t; + +static stbi_uc *pic_readval(stbi *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (at_eof(s)) return epuc("bad file","PIC file too short"); + dest[i]=get8u(s); + } + } + + return dest; +} + +static void pic_copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *pic_load2(stbi *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + pic_packet_t packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return epuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + + act_comp |= packet->channel; + + if (at_eof(s)) return epuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return epuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return epuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=get8u(s); + if (at_eof(s)) return epuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (uint8) left; + + if (!pic_readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = get8(s), i; + if (at_eof(s)) return epuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + int i; + + if (count==128) + count = get16(s); + else + count -= 127; + if (count > left) + return epuc("bad file","scanline overrun"); + + if (!pic_readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return epuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *pic_load(stbi *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + get8(s); + + x = get16(s); + y = get16(s); + if (at_eof(s)) return epuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return epuc("too large", "Image too large to decode"); + + get32(s); //skip `ratio' + get16(s); //skip `fields' + get16(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!pic_load2(s,x,y,comp, result)) { + free(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi_pic_test(stbi *s) +{ + int r = pic_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return pic_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb +typedef struct stbi_gif_lzw_struct { + int16 prefix; + uint8 first; + uint8 suffix; +} stbi_gif_lzw; + +typedef struct stbi_gif_struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags; + uint8 pal[256][4]; + uint8 lpal[256][4]; + stbi_gif_lzw codes[4096]; + uint8 *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi_gif; + +static int gif_test(stbi *s) +{ + int sz; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') return 0; + sz = get8(s); + if (sz != '9' && sz != '7') return 0; + if (get8(s) != 'a') return 0; + return 1; +} + +static int stbi_gif_test(stbi *s) +{ + int r = gif_test(s); + stbi_rewind(s); + return r; +} + +static void stbi_gif_parse_colortable(stbi *s, uint8 pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + pal[i][3] = transp ? 0 : 255; + } +} + +static int stbi_gif_header(stbi *s, stbi_gif *g, int *comp, int is_info) +{ + uint8 version; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') + return e("not GIF", "Corrupt GIF"); + + version = get8u(s); + if (version != '7' && version != '9') return e("not GIF", "Corrupt GIF"); + if (get8(s) != 'a') return e("not GIF", "Corrupt GIF"); + + failure_reason = ""; + g->w = get16le(s); + g->h = get16le(s); + g->flags = get8(s); + g->bgindex = get8(s); + g->ratio = get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi_gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi_gif_info_raw(stbi *s, int *x, int *y, int *comp) +{ + stbi_gif g; + if (!stbi_gif_header(s, &g, comp, 1)) { + stbi_rewind( s ); + return 0; + } + if (x) *x = g.w; + if (y) *y = g.h; + return 1; +} + +static void stbi_out_gif_code(stbi_gif *g, uint16 code) +{ + uint8 *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi_out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static uint8 *stbi_process_gif_raster(stbi *s, stbi_gif *g) +{ + uint8 lzw_cs; + int32 len, code; + uint32 first; + int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi_gif_lzw *p; + + lzw_cs = get8u(s); + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (code = 0; code < clear; code++) { + g->codes[code].prefix = -1; + g->codes[code].first = (uint8) code; + g->codes[code].suffix = (uint8) code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (int32) get8(s) << valid_bits; + valid_bits += 8; + } else { + int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + skip(s, len); + while ((len = get8(s)) > 0) + skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return epuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return epuc("too many codes", "Corrupt GIF"); + p->prefix = (int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return epuc("illegal code in raster", "Corrupt GIF"); + + stbi_out_gif_code(g, (uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return epuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi_fill_gif_background(stbi_gif *g) +{ + int i; + uint8 *c = g->pal[g->bgindex]; + // @OPTIMIZE: write a dword at a time + for (i = 0; i < g->w * g->h * 4; i += 4) { + uint8 *p = &g->out[i]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static uint8 *stbi_gif_load_next(stbi *s, stbi_gif *g, int *comp, int req_comp) +{ + int i; + uint8 *old_out = 0; + + if (g->out == 0) { + if (!stbi_gif_header(s, g, comp,0)) return 0; // failure_reason set by stbi_gif_header + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + stbi_fill_gif_background(g); + } else { + // animated-gif-only path + if (((g->eflags & 0x1C) >> 2) == 3) { + old_out = g->out; + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + memcpy(g->out, old_out, g->w*g->h*4); + } + } + + for (;;) { + switch (get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int32 x, y, w, h; + uint8 *o; + + x = get16le(s); + y = get16le(s); + w = get16le(s); + h = get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return epuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi_gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (uint8 *) g->lpal; + } else if (g->flags & 0x80) { + for (i=0; i < 256; ++i) // @OPTIMIZE: reset only the previous transparent + g->pal[i][3] = 255; + if (g->transparent >= 0 && (g->eflags & 0x01)) + g->pal[g->transparent][3] = 0; + g->color_table = (uint8 *) g->pal; + } else + return epuc("missing color table", "Corrupt GIF"); + + o = stbi_process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (req_comp && req_comp != 4) + o = convert_format(o, 4, req_comp, g->w, g->h); + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (get8(s) == 0xF9) { // Graphic Control Extension. + len = get8(s); + if (len == 4) { + g->eflags = get8(s); + get16le(s); // delay + g->transparent = get8(s); + } else { + skip(s, len); + break; + } + } + while ((len = get8(s)) != 0) + skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (uint8 *) 1; + + default: + return epuc("unknown code", "Corrupt GIF"); + } + } +} + +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *u = 0; + stbi_gif g={0}; + + u = stbi_gif_load_next(s, &g, comp, req_comp); + if (u == (void *) 1) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + } + + return u; +} + +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp) +{ + return stbi_gif_info_raw(s,x,y,comp); +} + + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int hdr_test(stbi *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi_hdr_test(stbi* s) +{ + int r = hdr_test(s); + stbi_rewind(s); + return r; +} + +#define HDR_BUFLEN 1024 +static char *hdr_gettoken(stbi *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) get8(z); + + while (!at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == HDR_BUFLEN-1) { + // flush to end of line + while (!at_eof(z) && get8(z) != '\n') + ; + break; + } + c = (char) get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return epf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return epf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = strtol(token, NULL, 10); + + *x = width; + *y = height; + + *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + getn(s, rgbe, 4); + hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = get8(s); + c2 = get8(s); + len = get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + uint8 rgbe[4]; + rgbe[0] = (uint8) c1; + rgbe[1] = (uint8) c2; + rgbe[2] = (uint8) len; + rgbe[3] = (uint8) get8u(s); + hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + free(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= get8(s); + if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = get8u(s); + if (count > 128) { + // Run + value = get8u(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = get8u(s); + } + } + } + for (i=0; i < width; ++i) + hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + free(scanline); + } + + return hdr_data; +} + +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return hdr_load(s,x,y,comp,req_comp); +} + +static int stbi_hdr_info(stbi *s, int *x, int *y, int *comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { + stbi_rewind( s ); + return 0; + } + + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi_rewind( s ); + return 0; + } + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *y = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *x = strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +static int stbi_bmp_info(stbi *s, int *x, int *y, int *comp) +{ + int hsz; + if (get8(s) != 'B' || get8(s) != 'M') { + stbi_rewind( s ); + return 0; + } + skip(s,12); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) { + stbi_rewind( s ); + return 0; + } + if (hsz == 12) { + *x = get16le(s); + *y = get16le(s); + } else { + *x = get32le(s); + *y = get32le(s); + } + if (get16le(s) != 1) { + stbi_rewind( s ); + return 0; + } + *comp = get16le(s) / 8; + return 1; +} + +static int stbi_psd_info(stbi *s, int *x, int *y, int *comp) +{ + int channelCount; + if (get32(s) != 0x38425053) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 1) { + stbi_rewind( s ); + return 0; + } + skip(s, 6); + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) { + stbi_rewind( s ); + return 0; + } + *y = get32(s); + *x = get32(s); + if (get16(s) != 8) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 3) { + stbi_rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi_pic_info(stbi *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + pic_packet_t packets[10]; + + skip(s, 92); + + *x = get16(s); + *y = get16(s); + if (at_eof(s)) return 0; + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi_rewind( s ); + return 0; + } + + skip(s, 8); + + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + act_comp |= packet->channel; + + if (at_eof(s)) { + stbi_rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi_rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} + +static int stbi_info_main(stbi *s, int *x, int *y, int *comp) +{ + if (stbi_jpeg_info(s, x, y, comp)) + return 1; + if (stbi_png_info(s, x, y, comp)) + return 1; + if (stbi_gif_info(s, x, y, comp)) + return 1; + if (stbi_bmp_info(s, x, y, comp)) + return 1; + if (stbi_psd_info(s, x, y, comp)) + return 1; + if (stbi_pic_info(s, x, y, comp)) + return 1; + #ifndef STBI_NO_HDR + if (stbi_hdr_info(s, x, y, comp)) + return 1; + #endif + // test tga last because it's a crappy test! + if (stbi_tga_info(s, x, y, comp)) + return 1; + return e("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = fopen(filename, "rb"); + int result; + if (!f) return e("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi s; + long pos = ftell(f); + start_file(&s, f); + r = stbi_info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_info_main(&s,x,y,comp); +} + +int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi_info_main(&s,x,y,comp); +} + +#endif // STBI_HEADER_FILE_ONLY + +/* + revision history: + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-uint8 to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.e. Janez (U+017D)emva) + 1.21 fix use of 'uint8' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 2008-08-02 + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 first released version +*/ diff --git a/Demos/qqsetup/ControlEx.h b/Demos/qqsetup/ControlEx.h new file mode 100644 index 00000000..264c3184 --- /dev/null +++ b/Demos/qqsetup/ControlEx.h @@ -0,0 +1,17 @@ +#ifndef __CONTROLEX_H__ +#define __CONTROLEX_H__ + +#include +#include + + +class CDialogBuilderCallbackEx : public IDialogBuilderCallback +{ +public: + CControlUI* CreateControl(LPCTSTR pstrClass) + { + return NULL; + } +}; + +#endif __CONTROLEX_H__ diff --git a/Demos/qqsetup/Res/qqsetup.ico b/Demos/qqsetup/Res/qqsetup.ico new file mode 100644 index 00000000..683ea4d3 Binary files /dev/null and b/Demos/qqsetup/Res/qqsetup.ico differ diff --git a/Demos/qqsetup/Res/qqsetup.zip b/Demos/qqsetup/Res/qqsetup.zip new file mode 100644 index 00000000..c2193b10 Binary files /dev/null and b/Demos/qqsetup/Res/qqsetup.zip differ diff --git a/Demos/qqsetup/StdAfx.cpp b/Demos/qqsetup/StdAfx.cpp new file mode 100644 index 00000000..79228f61 --- /dev/null +++ b/Demos/qqsetup/StdAfx.cpp @@ -0,0 +1,15 @@ +// stdafx.cpp : source file that includes just the standard includes +// App.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +#if defined _M_IX86 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_IA64 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_X64 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#else +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif diff --git a/Demos/qqsetup/StdAfx.h b/Demos/qqsetup/StdAfx.h new file mode 100644 index 00000000..99dd11e6 --- /dev/null +++ b/Demos/qqsetup/StdAfx.h @@ -0,0 +1,35 @@ + +#if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) +#define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_ + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include + +#include "..\..\DuiLib\UIlib.h" + +using namespace DuiLib; + +#ifdef _DEBUG +# ifdef _UNICODE +# pragma comment(lib, "..\\..\\lib\\DuiLib_d.lib") +# else +# pragma comment(lib, "..\\..\\lib\\DuiLib_d.lib") +# endif +#else +# ifdef _UNICODE +# pragma comment(lib, "..\\..\\lib\\DuiLib.lib") +# else +# pragma comment(lib, "..\\..\\lib\\DuiLib.lib") +# endif +#endif + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) diff --git a/Demos/qqsetup/qqsetup.cpp b/Demos/qqsetup/qqsetup.cpp new file mode 100644 index 00000000..0a052967 --- /dev/null +++ b/Demos/qqsetup/qqsetup.cpp @@ -0,0 +1,301 @@ +#include "stdafx.h" +#include +#include +#include "ControlEx.h" +#include "resource.h" +#include +class CMainFrame : public CWindowWnd, public INotifyUI, public CWebBrowserEventHandler +{ +public: + CMainFrame() { + + }; + +public: + void Init() { + m_pCloseBtn = static_cast(m_pm.FindControl(_T("closebtn"))); + m_pMaxBtn = static_cast(m_pm.FindControl(_T("maxbtn"))); + m_pRestoreBtn = static_cast(m_pm.FindControl(_T("restorebtn"))); + m_pMinBtn = static_cast(m_pm.FindControl(_T("minbtn"))); + } + + void OnPrepare() { + + } + + + virtual HRESULT STDMETHODCALLTYPE UpdateUI( void) + { + return S_OK; + } + +public: + LPCTSTR GetWindowClassName() const { return _T("UIMainFrame"); }; + UINT GetClassStyle() const { return CS_DBLCLKS; }; + void OnFinalMessage(HWND /*hWnd*/) { delete this; }; + + void Notify(TNotifyUI& msg) + { + if( msg.sType == _T("windowinit") ) OnPrepare(); + else if( msg.sType == _T("click") ) { + if( msg.pSender == m_pCloseBtn ) { + PostQuitMessage(0); + return; + } + else if( msg.pSender == m_pMinBtn ) { + SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0); return; } + else if( msg.pSender == m_pMaxBtn ) { + SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, 0); return; } + else if( msg.pSender == m_pRestoreBtn ) { + SendMessage(WM_SYSCOMMAND, SC_RESTORE, 0); return; } + + // ťϢ + OnLClick(msg.pSender); + } + else if(msg.sType==_T("selectchanged")) + { + CDuiString name = msg.pSender->GetName(); + } + } + + void OnLClick(CControlUI *pControl) + { + CDuiString sName = pControl->GetName(); + if(sName.CompareNoCase(_T("homepage_btn")) == 0) + { + ShellExecute(NULL, _T("open"), _T("http://www.winradar.com/?f=duidemo"), NULL, NULL, SW_SHOW); + } + } + + LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + // ICONS ͼ + SetIcon(IDR_MAINFRAME); + + LONG styleValue = ::GetWindowLong(*this, GWL_STYLE); + styleValue &= ~WS_CAPTION; + ::SetWindowLong(*this, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); + + m_pm.Init(m_hWnd); + CDialogBuilder builder; + CDialogBuilderCallbackEx cb; + CControlUI* pRoot = builder.Create(_T("skin.xml"), (UINT)0, &cb, &m_pm); + ASSERT(pRoot && "Failed to parse XML"); + m_pm.AttachDialog(pRoot); + m_pm.AddNotifier(this); + + Init(); + return 0; + } + + LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + bHandled = FALSE; + return 0; + } + + LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + ::PostQuitMessage(0L); + + bHandled = FALSE; + return 0; + } + + LRESULT OnNcActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + if( ::IsIconic(*this) ) bHandled = FALSE; + return (wParam == 0) ? TRUE : FALSE; + } + + LRESULT OnNcCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + return 0; + } + + LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + return 0; + } + + LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + POINT pt; pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam); + ::ScreenToClient(*this, &pt); + + RECT rcClient; + ::GetClientRect(*this, &rcClient); + + if( !::IsZoomed(*this) ) { + RECT rcSizeBox = m_pm.GetSizeBox(); + if( pt.y < rcClient.top + rcSizeBox.top ) { + if( pt.x < rcClient.left + rcSizeBox.left ) return HTTOPLEFT; + if( pt.x > rcClient.right - rcSizeBox.right ) return HTTOPRIGHT; + return HTTOP; + } + else if( pt.y > rcClient.bottom - rcSizeBox.bottom ) { + if( pt.x < rcClient.left + rcSizeBox.left ) return HTBOTTOMLEFT; + if( pt.x > rcClient.right - rcSizeBox.right ) return HTBOTTOMRIGHT; + return HTBOTTOM; + } + if( pt.x < rcClient.left + rcSizeBox.left ) return HTLEFT; + if( pt.x > rcClient.right - rcSizeBox.right ) return HTRIGHT; + } + + RECT rcCaption = m_pm.GetCaptionRect(); + if( pt.x >= rcClient.left + rcCaption.left && pt.x < rcClient.right - rcCaption.right \ + && pt.y >= rcCaption.top && pt.y < rcCaption.bottom ) { + CControlUI* pControl = static_cast(m_pm.FindControl(pt)); + if( pControl && _tcscmp(pControl->GetClass(), _T("ButtonUI")) != 0 && + _tcscmp(pControl->GetClass(), _T("OptionUI")) != 0 && + _tcscmp(pControl->GetClass(), _T("TextUI")) != 0 ) + return HTCAPTION; + } + + return HTCLIENT; + } + + LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + SIZE szRoundCorner = m_pm.GetRoundCorner(); + if( !::IsIconic(*this) && (szRoundCorner.cx != 0 || szRoundCorner.cy != 0) ) { + CDuiRect rcWnd; + ::GetWindowRect(*this, &rcWnd); + rcWnd.Offset(-rcWnd.left, -rcWnd.top); + rcWnd.right++; rcWnd.bottom++; + HRGN hRgn = ::CreateRoundRectRgn(rcWnd.left, rcWnd.top, rcWnd.right, rcWnd.bottom, szRoundCorner.cx, szRoundCorner.cy); + ::SetWindowRgn(*this, hRgn, TRUE); + ::DeleteObject(hRgn); + } + + bHandled = FALSE; + return 0; + } + + LRESULT OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + MONITORINFO oMonitor = {}; + oMonitor.cbSize = sizeof(oMonitor); + ::GetMonitorInfo(::MonitorFromWindow(*this, MONITOR_DEFAULTTOPRIMARY), &oMonitor); + CDuiRect rcWork = oMonitor.rcWork; + rcWork.Offset(-rcWork.left, -rcWork.top); + + LPMINMAXINFO lpMMI = (LPMINMAXINFO) lParam; + lpMMI->ptMaxPosition.x = rcWork.left; + lpMMI->ptMaxPosition.y = rcWork.top; + lpMMI->ptMaxSize.x = rcWork.right; + lpMMI->ptMaxSize.y = rcWork.bottom; + + bHandled = FALSE; + return 0; + } + + LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + // ʱյWM_NCDESTROYյwParamΪSC_CLOSEWM_SYSCOMMAND + if( wParam == SC_CLOSE ) { + ::PostQuitMessage(0L); + bHandled = TRUE; + return 0; + } + BOOL bZoomed = ::IsZoomed(*this); + LRESULT lRes = CWindowWnd::HandleMessage(uMsg, wParam, lParam); + if( ::IsZoomed(*this) != bZoomed ) { + if( !bZoomed ) { + CControlUI* pControl = static_cast(m_pm.FindControl(_T("maxbtn"))); + if( pControl ) pControl->SetVisible(false); + pControl = static_cast(m_pm.FindControl(_T("restorebtn"))); + if( pControl ) pControl->SetVisible(true); + } + else { + CControlUI* pControl = static_cast(m_pm.FindControl(_T("maxbtn"))); + if( pControl ) pControl->SetVisible(true); + pControl = static_cast(m_pm.FindControl(_T("restorebtn"))); + if( pControl ) pControl->SetVisible(false); + } + } + return lRes; + } + + LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + LRESULT lRes = 0; + BOOL bHandled = TRUE; + switch( uMsg ) { + case WM_CREATE: lRes = OnCreate(uMsg, wParam, lParam, bHandled); break; + case WM_CLOSE: lRes = OnClose(uMsg, wParam, lParam, bHandled); break; + case WM_DESTROY: lRes = OnDestroy(uMsg, wParam, lParam, bHandled); break; + case WM_NCACTIVATE: lRes = OnNcActivate(uMsg, wParam, lParam, bHandled); break; + case WM_NCCALCSIZE: lRes = OnNcCalcSize(uMsg, wParam, lParam, bHandled); break; + case WM_NCPAINT: lRes = OnNcPaint(uMsg, wParam, lParam, bHandled); break; + case WM_NCHITTEST: lRes = OnNcHitTest(uMsg, wParam, lParam, bHandled); break; + case WM_SIZE: lRes = OnSize(uMsg, wParam, lParam, bHandled); break; + case WM_GETMINMAXINFO: lRes = OnGetMinMaxInfo(uMsg, wParam, lParam, bHandled); break; + case WM_SYSCOMMAND: lRes = OnSysCommand(uMsg, wParam, lParam, bHandled); break; + default: + bHandled = FALSE; + } + if( bHandled ) return lRes; + if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes; + return CWindowWnd::HandleMessage(uMsg, wParam, lParam); + } + +public: + CPaintManagerUI m_pm; + +private: + CButtonUI* m_pCloseBtn; + CButtonUI* m_pMaxBtn; + CButtonUI* m_pRestoreBtn; + CButtonUI* m_pMinBtn; +}; + +static LPBYTE resource_zip_buffer_ = NULL; + +//#define USE_EMBEDED_RESOURCE + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow) +{ + CPaintManagerUI::SetInstance(hInstance); + +#ifdef USE_EMBEDED_RESOURCE + HRSRC hResource = ::FindResource(CPaintManagerUI::GetResourceDll(), MAKEINTRESOURCE(IDR_ZIPRES), _T("ZIPRES")); + if( hResource == NULL ) + return 0L; + DWORD dwSize = 0; + HGLOBAL hGlobal = ::LoadResource(CPaintManagerUI::GetResourceDll(), hResource); + if( hGlobal == NULL ) { + FreeResource(hResource); + return 0L; + } + dwSize = ::SizeofResource(CPaintManagerUI::GetResourceDll(), hResource); + if( dwSize == 0 ) + return 0L; + resource_zip_buffer_ = new BYTE[ dwSize ]; + if (resource_zip_buffer_ != NULL) + { + ::CopyMemory(resource_zip_buffer_, (LPBYTE)::LockResource(hGlobal), dwSize); + } + + ::FreeResource(hResource); + CPaintManagerUI::SetResourceZip(resource_zip_buffer_, dwSize); +#else + CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath() + _T("skin\\qqsetup"));// + //CPaintManagerUI::SetResourceZip(_T("gamebox.zip")); +#endif + + + HRESULT Hr = ::CoInitialize(NULL); + if( FAILED(Hr) ) return 0; + + CMainFrame* pFrame = new CMainFrame(); + if( pFrame == NULL ) return 0; + pFrame->Create(NULL, _T("ѡ"), UI_WNDSTYLE_FRAME, 0L, 0, 0, 800, 572); + pFrame->CenterWindow(); + ::ShowWindow(*pFrame, SW_SHOW); + + CPaintManagerUI::MessageLoop(); + + ::CoUninitialize(); + return 0; +} \ No newline at end of file diff --git a/Demos/qqsetup/qqsetup.rc b/Demos/qqsetup/qqsetup.rc new file mode 100644 index 00000000..773abaa4 Binary files /dev/null and b/Demos/qqsetup/qqsetup.rc differ diff --git a/Demos/qqsetup/qqsetup.vcxproj b/Demos/qqsetup/qqsetup.vcxproj new file mode 100644 index 00000000..55c9b772 --- /dev/null +++ b/Demos/qqsetup/qqsetup.vcxproj @@ -0,0 +1,189 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + qqsetup + {797901AA-9447-45C6-AF89-5D5DA54C4159} + 360 + + + + + + + + + + + + Application + false + Unicode + + + Application + false + Unicode + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)bin\ + $(SolutionDir)temp\$(ProjectName)\$(Configuration)\ + true + $(SolutionDir)bin\ + $(SolutionDir)temp\$(ProjectName)\$(Configuration)\ + false + AllRules.ruleset + + + AllRules.ruleset + + + $(ProjectName)_d + $(ProjectName) + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/qqsetup.tlb + + + + + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + Use + stdafx.h + true + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x0406 + + + true + true + true + Windows + MachineX86 + + + true + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/qqsetup.tlb + + + + + MinSpace + OnlyExplicitInline + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreaded + true + NotUsing + stdafx.h + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0406 + + + true + Windows + MachineX86 + libcmt.lib + + + true + + + + + + %(PreprocessorDefinitions) + Create + %(PreprocessorDefinitions) + Create + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demos/qqsetup/qqsetup.vcxproj.filters b/Demos/qqsetup/qqsetup.vcxproj.filters new file mode 100644 index 00000000..db89f03a --- /dev/null +++ b/Demos/qqsetup/qqsetup.vcxproj.filters @@ -0,0 +1,123 @@ + + + + + {c75b9d7c-452a-4867-a585-e8aee4a05f51} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {249f81db-516d-4a60-9882-d3fd674e412c} + h;hpp;hxx;hm;inl + + + {5323114f-c462-496c-8a26-5f0586629382} + + + {495d8e73-881d-481f-83d1-3ca5ee46318e} + + + {d2f13808-77fd-462b-bab2-501843e85c61} + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + Resources + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\images + + + Resources\xmls + + + Resources + + + + + Resources + + + \ No newline at end of file diff --git a/Demos/qqsetup/qqsetup.vcxproj.user b/Demos/qqsetup/qqsetup.vcxproj.user new file mode 100644 index 00000000..ace9a86a --- /dev/null +++ b/Demos/qqsetup/qqsetup.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Demos/qqsetup/resource.h b/Demos/qqsetup/resource.h new file mode 100644 index 00000000..d1f8f097 Binary files /dev/null and b/Demos/qqsetup/resource.h differ diff --git a/Demos/xlgamebox/AnimationHelper.cpp b/Demos/xlgamebox/AnimationHelper.cpp new file mode 100644 index 00000000..25d2f1d8 --- /dev/null +++ b/Demos/xlgamebox/AnimationHelper.cpp @@ -0,0 +1,1201 @@ +#include "stdafx.h" +#include "AnimationHelper.h" + +/////////////////////////////////////////////////////////////////////////////////////// +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened +typedef DWORD ZRESULT; +typedef struct +{ + int index; // index of this file within the zip + char name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; +typedef struct +{ + int index; // index of this file within the zip + TCHAR name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; +#define OpenZip OpenZipU +#define CloseZip(hz) CloseZipU(hz) +extern HZIP OpenZipU(void *z,unsigned int len,DWORD flags); +extern ZRESULT CloseZipU(HZIP hz); +#ifdef _UNICODE +#define ZIPENTRY ZIPENTRYW +#define GetZipItem GetZipItemW +#define FindZipItem FindZipItemW +#else +#define GetZipItem GetZipItemA +#define FindZipItem FindZipItemA +#endif +extern ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze); +extern ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *ze); +extern ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze); +extern ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *ze); +extern ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags); +/////////////////////////////////////////////////////////////////////////////////////// + +extern "C" +{ + extern unsigned char *stbi_load_from_memory(unsigned char const *buffer, int len, int *x, int *y, \ + int *comp, int req_comp); + extern void stbi_image_free(void *retval_from_stbi_load); + +}; + +namespace DuiLib +{ + /************************************************************************/ + /* CAnimation */ + /************************************************************************/ + + ////////////////////////////////////////////////////////////////////// + // Nested structures member functions + ////////////////////////////////////////////////////////////////////// + inline int CAnimation::TGIFControlExt::GetPackedValue(enum ControlExtValues Value) + { + int nRet = (int) m_cPacked; + switch(Value) + { + case GCX_PACKED_DISPOSAL: + nRet = (nRet & 28) >> 2; + break; + + case GCX_PACKED_USERINPUT: + nRet = (nRet & 2) >> 1; + break; + + case GCX_PACKED_TRANSPCOLOR: + nRet &= 1; + break; + }; + + return nRet; + } + + inline int CAnimation::TGIFLSDescriptor::GetPackedValue(enum LSDPackedValues Value) + { + int nRet = (int) m_cPacked; + + switch(Value) + { + case LSD_PACKED_GLOBALCT: + nRet = nRet >> 7; + break; + + case LSD_PACKED_CRESOLUTION: + nRet = ((nRet & 0x70) >> 4) + 1; + break; + + case LSD_PACKED_SORT: + nRet = (nRet & 8) >> 3; + break; + + case LSD_PACKED_GLOBALCTSIZE: + nRet &= 7; + break; + }; + + return nRet; + } + + inline int CAnimation::TGIFImageDescriptor::GetPackedValue(enum IDPackedValues Value) + { + int nRet = (int) m_cPacked; + + switch(Value) + { + case ID_PACKED_LOCALCT: + nRet >>= 7; + break; + + case ID_PACKED_INTERLACE: + nRet = ((nRet & 0x40) >> 6); + break; + + case ID_PACKED_SORT: + nRet = (nRet & 0x20) >> 5; + break; + + case ID_PACKED_LOCALCTSIZE: + nRet &= 7; + break; + }; + + return nRet; + } + + ////////////////////////////////////////////////////////////////////// + // ctor && dtor + ////////////////////////////////////////////////////////////////////// + CAnimation::CAnimation(IAnimationCallback* pCallback) : m_pCallback(pCallback) + { + // check structures size + assert(sizeof(TGIFImageDescriptor) == 10); + assert(sizeof(TGIFAppExtension) == 14); + assert(sizeof(TGIFPlainTextExt) == 15); + assert(sizeof(TGIFLSDescriptor) == 7); + assert(sizeof(TGIFControlExt) == 8); + assert(sizeof(TGIFCommentExt) == 2); + assert(sizeof(TGIFHeader) == 6); + + m_pRawData = NULL; + m_nDataSize = 0; + m_pGIFHeader = NULL; + m_pGIFLSDescriptor = NULL; + m_nGlobalCTSize = 0; + m_PictureSize.cx = m_PictureSize.cy = 0; + m_clrBackground = RGB(255, 255, 255); // white by default + m_nCurrFrame = 0; + m_nCurrOffset = 0; + m_hThread = NULL; + m_bExitThread = true; + + m_hDrawEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); + assert(m_hDrawEvent); + m_pvFrames = new VTFRAME; + assert(m_pvFrames); + } + + CAnimation::~CAnimation() + { + UnLoad(); + delete m_pvFrames; + m_pvFrames = NULL; + CloseHandle(m_hDrawEvent); + m_hDrawEvent = NULL; + } + + const TImageInfo* CAnimation::LoadGIF(LPCTSTR bitmap, LPCTSTR type/* = NULL*/, DWORD mask/* = 0*/) + { + LPBYTE pData = NULL; + DWORD dwSize = 0; + if( type != NULL && isdigit(*bitmap) ) + { + LPTSTR pstr = NULL; + int iIndex = _tcstol(bitmap, &pstr, 10); + HRSRC hPicture = ::FindResource(CPaintManagerUI::GetResourceDll(), MAKEINTRESOURCE(iIndex), type); + if (!hPicture) + return NULL; + HGLOBAL hResData; + if (!(hResData = ::LoadResource(CPaintManagerUI::GetResourceDll(), hPicture))) + { + ::FreeResource(hPicture); + return NULL; + }; + dwSize = ::SizeofResource(CPaintManagerUI::GetResourceDll(),hPicture); + if( dwSize < sizeof(TGIFHeader)) + { + ::FreeResource(hPicture); + return NULL; + } + pData = static_cast( malloc(dwSize*sizeof(BYTE)) ); + if (!pData) + { + ::FreeResource(hPicture); + return NULL; + } + LPBYTE pSrc = static_cast( ::LockResource(hResData) ); + if (!pSrc) + { + free(pData); + ::FreeResource(hPicture); + return NULL; + } + ::CopyMemory(pData, pSrc, dwSize); + ::FreeResource(hPicture); + } + else + { + CDuiString sFile = CPaintManagerUI::GetResourcePath(); + if( CPaintManagerUI::GetResourceZip().IsEmpty() ) { + sFile += bitmap; + HANDLE hFile = INVALID_HANDLE_VALUE; + DWORD dwRead=0; + hFile = ::CreateFile(sFile.GetData(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, \ + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if(INVALID_HANDLE_VALUE == hFile) + return NULL; + dwSize = ::GetFileSize(hFile, NULL); + if (dwSize < sizeof(TGIFHeader)) + { + CloseHandle(hFile); + return NULL; + } + pData = static_cast( malloc(dwSize*sizeof(BYTE)) ); + if (!pData) + { + CloseHandle(hFile); + return NULL; + } + ::ReadFile(hFile, pData, dwSize, &dwRead, NULL); + ::CloseHandle(hFile); + } + else + { + sFile += CPaintManagerUI::GetResourceZip(); + HZIP hz = NULL; + if( CPaintManagerUI::IsCachedResourceZip() ) hz = (HZIP)CPaintManagerUI::GetResourceZipHandle(); + else hz = OpenZip((void*)sFile.GetData(), 0, 2); + if( hz == NULL ) + return NULL; + ZIPENTRY ze; + int i; + if( FindZipItem(hz, bitmap, true, &i, &ze) != 0 ) + { + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + return NULL; + } + dwSize = ze.unc_size; + if (dwSize < sizeof(TGIFHeader)) + { + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + return NULL; + } + pData = static_cast( malloc(dwSize*sizeof(BYTE)) ); + if (!pData) + { + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + return NULL; + } + int res = UnzipItem(hz, i, pData, dwSize, 3); + if( res != 0x00000000 && res != 0x00000600) { + free(pData); + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + return NULL; + } + if( !CPaintManagerUI::IsCachedResourceZip() ) + CloseZip(hz); + } + } + + const TImageInfo* data = LoadGIF(pData, dwSize); + free(pData); + return data; + } + + const TImageInfo* CAnimation::LoadGIF(const LPBYTE pData, DWORD dwSize, DWORD mask/* = 0*/) + { + if (!pData || dwSize==0) + return NULL; + + UnLoad(); + + if(!(m_pRawData = const_cast(pData))) + return NULL; + + m_nDataSize = dwSize; + m_pGIFHeader = (TGIFHeader*)m_pRawData; + + if((memcmp(&m_pGIFHeader->m_cSignature, "GIF", 3) != 0) + && ((memcmp(&m_pGIFHeader->m_cVersion, "87a", 3) != 0) + || (memcmp(&m_pGIFHeader->m_cVersion, "89a", 3) != 0))) + { + // it's neither GIF87a nor GIF89a + // do nothing + // clear GIF variables + m_pRawData = NULL; + m_pGIFHeader = NULL; + m_nDataSize = 0; + + return NULL; + } + + m_pGIFLSDescriptor = (TGIFLSDescriptor*)(m_pRawData + sizeof(TGIFHeader)); + if(m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT) == 1) + { + // calculate the globat color table size + m_nGlobalCTSize = static_cast(3 * (1 + << (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE) + + 1))); + // get the background color if GCT is present + unsigned char * pBkClr = m_pRawData + + sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + + 3 * m_pGIFLSDescriptor->m_cBkIndex; + m_clrBackground = RGB(pBkClr[0], pBkClr[1], pBkClr[2]); + }; + + // store the picture's size + m_PictureSize.cx = m_pGIFLSDescriptor->m_wWidth; + m_PictureSize.cy = m_pGIFLSDescriptor->m_wHeight; + + // determine frame count for this picture + UINT nFrameCount = 0; + ResetDataPointer(); + while(SkipNextGraphicBlock()) + nFrameCount++; + +#ifdef GIF_TRACING + OutputDebugString(_T(" -= GIF encountered\n" + "Logical Screen dimensions = %dx%d\n" + "Global color table = %d\n" + "Color depth = %d\n" + "Sort flag = %d\n" + "Size of Global Color Table = %d\n" + "Background color index = %d\n" + "Pixel aspect ratio = %d\n" + "Frame count = %d\n" + "Background color = %06Xh\n\n"), + m_pGIFLSDescriptor->m_wWidth, + m_pGIFLSDescriptor->m_wHeight, + m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT), + m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_CRESOLUTION), + m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_SORT), + m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE), + m_pGIFLSDescriptor->m_cBkIndex, + m_pGIFLSDescriptor->m_cPixelAspect, + nFrameCount, + m_clrBackground); + EnumGIFBlocks(); +#endif + + if(nFrameCount <= 1) + { + // now check the frame count + // if there's an empty GIF or only one frame, + // no need to animate this GIF + // therefore, treat it like any other pic + m_pRawData = NULL; + return NULL; + } + + // if, on the contrary, there are several frames + // then store separate frames in an array + TFrame frame; + UINT nBlockLen = 0; + LPBYTE pFrameData = NULL; + UINT nCurFrame = 0; + ////////////////////////////////////////////////////////////////////////// + // Before rendering a frame we should take care of what's + // behind that frame. TFrame::m_nDisposal will be our guide: + // 0 - no disposal specified (do nothing) + // 1 - do not dispose (again, do nothing) + // 2 - restore to background color (m_clrBackground) + // 3 - restore to previous + // background color for first frame + LPBYTE pDispData = static_cast(malloc(m_PictureSize.cx * m_PictureSize.cy * sizeof(COLORREF))); + if (!pDispData) + { + m_pRawData = NULL; + return NULL; + } + for (int n = 0; n < m_PictureSize.cx*m_PictureSize.cy; n++) + ((COLORREF*)pDispData)[n] = m_clrBackground; + + ResetDataPointer(); + while(pFrameData = GetNextGraphicBlock(&nBlockLen, + &frame.m_nDelay, + &frame.m_frameSize, + &frame.m_frameOffset, + &frame.m_nDisposal)) + { +#ifdef GIF_TRACING + ////////////////////////////////////////////// + // uncomment the following strings if you want + // to write separate frames on disk + // + // CString szName; + // szName.Format(_T("%.4d.gif"),nCurFrame); + // WriteDataOnDisk(szName,hFrameData,nBlockLen); + // nCurFrame++; +#endif // GIF_TRACING + if (!pFrameData) + continue; + + LPBYTE pImage = NULL; + int x,y,n; + pImage = stbi_load_from_memory(pFrameData, nBlockLen, &x, &y, &n, 4); + free(pFrameData); + pFrameData = NULL; + BITMAPINFO bmi; + ::ZeroMemory(&bmi, sizeof(BITMAPINFO)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = x; + bmi.bmiHeader.biHeight = -y; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = x * y * 4; + + bool bAlphaChannel = false; + LPBYTE pDest = NULL; + HBITMAP hBitmap = ::CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**)&pDest, NULL, 0); + if( !hBitmap ) + { + stbi_image_free(pImage); + continue; + } + + for( int i = 0; i < x * y; i++ ) + { + DWORD dwPixel = pImage[i*4+3]==BYTE(0) ? ((DWORD*)pDispData)[i] : ((DWORD*)pImage)[i];; + LPBYTE pPixel = (LPBYTE)(&dwPixel); + pDest[i*4 + 3] = pPixel[3]; + if( pDest[i*4 + 3] < 255 ) + { + pDest[i*4] = (BYTE)(DWORD(pPixel[2])*pPixel[3]/255); + pDest[i*4 + 1] = (BYTE)(DWORD(pPixel[1])*pPixel[3]/255); + pDest[i*4 + 2] = (BYTE)(DWORD(pPixel[0])*pPixel[3]/255); + bAlphaChannel = true; + } + else + { + pDest[i*4] = pPixel[2]; + pDest[i*4 + 1] = pPixel[1]; + pDest[i*4 + 2] = pPixel[0]; + } + + if( *(DWORD*)(&pDest[i*4]) == mask ) { + pDest[i*4] = (BYTE)0; + pDest[i*4 + 1] = (BYTE)0; + pDest[i*4 + 2] = (BYTE)0; + pDest[i*4 + 3] = (BYTE)0; + bAlphaChannel = true; + } + } + stbi_image_free(pImage); + + switch (frame.m_nDisposal) + { + case 0: + memset(pDispData, 0, m_PictureSize.cx*m_PictureSize.cy*sizeof(COLORREF)); + break; + case 1: + memcpy(pDispData, pDest, x * y * 4); + break; + case 2: + for (int n = 0; n < m_PictureSize.cx*m_PictureSize.cy; n++) + ((COLORREF*)pDispData)[n] = m_clrBackground; + break; + case 3: + break; + } + + frame.m_pImage = new TImageInfo; + frame.m_pImage->hBitmap = hBitmap; + frame.m_pImage->nX = x; + frame.m_pImage->nY = y; + frame.m_pImage->alphaChannel = bAlphaChannel; + + nCurFrame++; + // everything went well, add this frame + m_pvFrames->push_back(frame); + }; + + // clean after ourselves + free(pDispData); pDispData = NULL; + m_pRawData = NULL; + m_nDataSize = 0; + m_pGIFHeader = NULL; + m_pGIFLSDescriptor = NULL; + m_nGlobalCTSize = 0; + m_nCurrOffset = 0; + + return m_pvFrames->empty() ? NULL : ((*m_pvFrames)[0]).m_pImage; + } + + void CAnimation::UnLoad() + { + Stop(); + + std::vector::iterator it; + for(it = m_pvFrames->begin();it < m_pvFrames->end();it++) + { + if ((*it).m_pImage) + { + ::DeleteObject((*it).m_pImage->hBitmap); + delete (*it).m_pImage; + } + } + m_pvFrames->clear(); + m_PictureSize.cx = m_PictureSize.cy = 0; + m_clrBackground = RGB(255, 255, 255); // white by default + m_nCurrFrame = 0; + } + + bool CAnimation::Play() + { + if(!m_bExitThread) + return false; + + if (m_hThread != NULL) + { + ::WaitForSingleObject(m_hThread, 5000); + ::CloseHandle(m_hThread); + m_hThread = NULL; + } + + DWORD nDummy; + m_bExitThread = false; + m_hThread = (HANDLE) ::CreateThread(NULL, + 0, + _ThreadAnimation, + (void *)this, + CREATE_SUSPENDED, + &nDummy); + if(!m_hThread) + { + m_bExitThread = true; + return false; + } + else + ::ResumeThread(m_hThread); + + return true; + } + + void CAnimation::Stop() + { + m_bExitThread = true; + ::SetEvent(m_hDrawEvent); + // we'll wait for 5 seconds then continue execution + ::WaitForSingleObject(m_hThread, 5000); + ::CloseHandle(m_hThread); + m_hThread = NULL; + } + + bool CAnimation::IsPlaying() const + { + return !m_bExitThread; + } + + SIZE CAnimation::GetSize() const + { + return m_PictureSize; + } + + int CAnimation::GetFrameCount() const + { + if (m_pvFrames) + return m_pvFrames->size(); + else + return 0; + } + + COLORREF CAnimation::GetBkColor() const + { + return m_clrBackground; + } + + const TImageInfo* CAnimation::GetCurImage() + { + TImageInfo* data = ((*m_pvFrames)[m_nCurrFrame]).m_pImage; + if (!IsPlaying()) + Play(); + ::SetEvent(m_hDrawEvent); + return data; + } + + ////////////////////////////////////////////////////////////////////// + // protected methods + ////////////////////////////////////////////////////////////////////// + int CAnimation::GetNextBlockLen() const + { + GIFBlockTypes nBlock = GetNextBlock(); + + int nTmp; + + switch(nBlock) + { + case BLOCK_UNKNOWN: + return -1; + break; + + case BLOCK_TRAILER: + return 1; + break; + + case BLOCK_APPEXT: + nTmp = GetSubBlocksLen(m_nCurrOffset + sizeof(TGIFAppExtension)); + if(nTmp > 0) + return sizeof(TGIFAppExtension) + nTmp; + break; + + case BLOCK_COMMEXT: + nTmp = GetSubBlocksLen(m_nCurrOffset + sizeof(TGIFCommentExt)); + if(nTmp > 0) + return sizeof(TGIFCommentExt) + nTmp; + break; + + case BLOCK_CONTROLEXT: + return sizeof(TGIFControlExt); + break; + + case BLOCK_PLAINTEXT: + nTmp = GetSubBlocksLen(m_nCurrOffset + sizeof(TGIFPlainTextExt)); + if(nTmp > 0) + return sizeof(TGIFPlainTextExt) + nTmp; + break; + + case BLOCK_IMAGE: + TGIFImageDescriptor* pIDescr = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + int nLCTSize = (int) + (pIDescr->GetPackedValue(ID_PACKED_LOCALCT) * 3 * (1 + << (pIDescr->GetPackedValue(ID_PACKED_LOCALCTSIZE) + + 1))); + + int nTmp = GetSubBlocksLen(m_nCurrOffset + sizeof(TGIFImageDescriptor) + nLCTSize + 1); + if(nTmp > 0) + return sizeof(TGIFImageDescriptor) + nLCTSize + 1 + nTmp; + break; + }; + + return 0; + } + + BOOL CAnimation::SkipNextBlock() + { + if(!m_pRawData) + return FALSE; + + int nLen = GetNextBlockLen(); + if((nLen <= 0) || ((m_nCurrOffset + nLen) > m_nDataSize)) + return FALSE; + + m_nCurrOffset += nLen; + return TRUE; + } + + BOOL CAnimation::SkipNextGraphicBlock() + { + if(!m_pRawData) + return FALSE; + + // GIF header + LSDescriptor [+ GCT] [+ Control block] + Data + + enum GIFBlockTypes nBlock; + + nBlock = GetNextBlock(); + + while((nBlock != BLOCK_CONTROLEXT) + && (nBlock != BLOCK_IMAGE) + && (nBlock != BLOCK_PLAINTEXT) + && (nBlock != BLOCK_UNKNOWN) + && (nBlock != BLOCK_TRAILER)) + { + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + }; + + if((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER)) + return FALSE; + + // it's either a control ext.block, an image or a plain text + + if(GetNextBlockLen() <= 0) + return FALSE; + + if(nBlock == BLOCK_CONTROLEXT) + { + if(!SkipNextBlock()) + return FALSE; + nBlock = GetNextBlock(); + + // skip everything until we meet an image block or a plain-text block + while((nBlock != BLOCK_IMAGE) + && (nBlock != BLOCK_PLAINTEXT) + && (nBlock != BLOCK_UNKNOWN) + && (nBlock != BLOCK_TRAILER)) + { + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + }; + + if((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER)) + return FALSE; + }; + + // skip the found data block (image or plain-text) + if(!SkipNextBlock()) + return FALSE; + + return TRUE; + } + + void CAnimation::ResetDataPointer() + { + // skip header and logical screen descriptor + m_nCurrOffset = sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + m_nGlobalCTSize; + } + + enum CAnimation::GIFBlockTypes CAnimation::GetNextBlock() const + { + switch(m_pRawData[m_nCurrOffset]) + { + case 0x21: + // extension block + switch(m_pRawData[m_nCurrOffset + 1]) + { + case 0x01: + // plain text extension + return BLOCK_PLAINTEXT; + break; + + case 0xF9: + // graphic control extension + return BLOCK_CONTROLEXT; + break; + + case 0xFE: + // comment extension + return BLOCK_COMMEXT; + break; + + case 0xFF: + // application extension + return BLOCK_APPEXT; + break; + }; + break; + + case 0x3B: + // trailer + return BLOCK_TRAILER; + break; + + case 0x2C: + // image data + return BLOCK_IMAGE; + break; + }; + + return BLOCK_UNKNOWN; + } + + UINT CAnimation::GetSubBlocksLen(UINT nStartingOffset) const + { + UINT nRet = 0; + UINT nCurOffset = nStartingOffset; + + while(m_pRawData[nCurOffset] != 0 ) + { + nRet += m_pRawData[nCurOffset] + 1; + nCurOffset += m_pRawData[nCurOffset] + 1; + }; + + return nRet + 1; + } + + LPBYTE CAnimation::GetNextGraphicBlock(UINT* pBlockLen, UINT* pDelay, SIZE* pBlockSize, SIZE* pBlockOffset, UINT* pDisposal) + { + if(!m_pRawData) + return NULL; + + // GIF header + LSDescriptor [+ GCT] [+ Control block] + Data + + *pDisposal = 0; + enum GIFBlockTypes nBlock; + nBlock = GetNextBlock(); + + while((nBlock != BLOCK_CONTROLEXT) + && (nBlock != BLOCK_IMAGE) + && (nBlock != BLOCK_PLAINTEXT) + && (nBlock != BLOCK_UNKNOWN) + && (nBlock != BLOCK_TRAILER)) + { + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + }; + + if((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER)) + return NULL; + + // it's either a control ext.block, an image or a plain text + + int nStart = m_nCurrOffset; + int nBlockLen = GetNextBlockLen(); + + if(nBlockLen <= 0) + return NULL; + + if(nBlock == BLOCK_CONTROLEXT) + { + // get the following data + TGIFControlExt* pControl = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + // store delay time + *pDelay = pControl->m_wDelayTime; + // store disposal method + *pDisposal = pControl->GetPackedValue(GCX_PACKED_DISPOSAL); + + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + + // skip everything until we find data to display + // (image block or plain-text block) + + while((nBlock != BLOCK_IMAGE) + && (nBlock != BLOCK_PLAINTEXT) + && (nBlock != BLOCK_UNKNOWN) + && (nBlock != BLOCK_TRAILER)) + { + if(!SkipNextBlock()) + return NULL; + nBlock = GetNextBlock(); + nBlockLen += GetNextBlockLen(); + }; + + if((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER)) + return NULL; + nBlockLen += GetNextBlockLen(); + } + else + *pDelay = -1; // to indicate that there was no delay value + + if(nBlock == BLOCK_IMAGE) + { + // store size and offsets + TGIFImageDescriptor* pImage = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + pBlockSize->cx = pImage->m_wWidth; + pBlockSize->cy = pImage->m_wHeight; + pBlockOffset->cx = pImage->m_wLeftPos; + pBlockOffset->cy = pImage->m_wTopPos; + }; + + if(!SkipNextBlock()) + return NULL; + + LPBYTE pData = static_cast(malloc(sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + m_nGlobalCTSize + nBlockLen + 1)); + if (!pData) + return NULL; + + int nOffset = 0; + + CopyMemory(pData, m_pRawData, sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + m_nGlobalCTSize); + nOffset += sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + m_nGlobalCTSize; + + CopyMemory(pData + nOffset, &m_pRawData[nStart], nBlockLen); + nOffset += nBlockLen; + + pData[nOffset] = 0x3B; // trailer + nOffset++; + + *pBlockLen = nOffset; + + return pData; + } + +#ifdef GIF_TRACING + void CAnimation::WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize) + { + CFile file; + + if(!file.Open(szFileName, CFile::modeCreate | CFile::modeWrite | CFile::shareDenyNone)) + { + OutputDebugString(_T("WriteData: Error creating file %s\n"), szFileName); + return; + }; + + char* pData = reinterpret_cast(GlobalLock(hData)); + if(!pData) + { + OutputDebugString(_T("WriteData: Error locking memory\n")); + return; + }; + + TRY + { + file.Write(pData, dwSize); + } + CATCH(CFileException, e); + { + OutputDebugString(_T("WriteData: An exception occured while writing to the file %s\n"), szFileName); + e->Delete(); + GlobalUnlock(hData); + file.Close(); + return; + } + END_CATCH GlobalUnlock(hData); + file.Close(); + } + + void CAnimation::EnumGIFBlocks() + { + enum GIFBlockTypes nBlock; + ResetDataPointer(); + while(m_nCurrOffset < m_nDataSize) + { + nBlock = GetNextBlock(); + switch(nBlock) + { + case BLOCK_UNKNOWN: + OutputDebugString(_T("- Unknown block\n")); + return; + break; + case BLOCK_TRAILER: + OutputDebugString(_T("- Trailer block\n")); + break; + case BLOCK_APPEXT: + OutputDebugString(_T("- Application extension block\n")); + break; + case BLOCK_COMMEXT: + OutputDebugString(_T("- Comment extension block\n")); + break; + case BLOCK_CONTROLEXT: + { + TGIFControlExt* pControl = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + OutputDebugString(_T("- Graphic control extension block (delay %d, disposal %d)\n"), + pControl->m_wDelayTime, + pControl->GetPackedValue(GCX_PACKED_DISPOSAL)); + }; + break; + case BLOCK_PLAINTEXT: + OutputDebugString(_T("- Plain text extension block\n")); + break; + case BLOCK_IMAGE: + TGIFImageDescriptor* pIDescr = reinterpret_cast(&m_pRawData[m_nCurrOffset]); + OutputDebugString(_T("- Image data block (%dx%d %d,%d)\n"), + pIDescr->m_wWidth, + pIDescr->m_wHeight, + pIDescr->m_wLeftPos, + pIDescr->m_wTopPos); + break; + }; + SkipNextBlock(); + }; + OutputDebugString(_T("\n")); + } +#endif // GIF_TRACING + + DWORD CAnimation::ThreadAnimation() + { + while(!m_bExitThread) + { + if (m_pCallback) m_pCallback->OnFrame(); + // wait until currframe is drawn. if currframe is not drawn over 1min + // this ani may be no use in the future. so exit this thread to stop + // the animation playing by herself. + if (::WaitForSingleObject(m_hDrawEvent, 60*1000) != WAIT_OBJECT_0) + { + m_bExitThread = true; + continue; + } + + // if the delay time is too short (like in old GIFs), wait for 100ms + if((*m_pvFrames)[m_nCurrFrame].m_nDelay < 5) + Sleep(100); + else + Sleep(10 * (*m_pvFrames)[m_nCurrFrame].m_nDelay); + + ::InterlockedIncrement(&m_nCurrFrame); + if(m_nCurrFrame == m_pvFrames->size()) + ::InterlockedExchange(&m_nCurrFrame, (LONG)0); + } + + return 0; + } + + DWORD __stdcall CAnimation::_ThreadAnimation(LPVOID pParam) + { + assert(pParam); + CAnimation* pAni = static_cast(pParam); + if (!pAni) + return 1; + + return pAni->ThreadAnimation(); + } + + //************************************************************************/ + //* CEUIAniHelper */ + //************************************************************************/ + + + bool CEUIAniHelper::DrawAniImage(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, const CDuiString& sImageName, \ + const CDuiString& sImageResType, RECT rcItem, RECT rcBmpPart, RECT rcCorner, DWORD dwMask, BYTE bFade, \ + bool bHole, bool bTiledX, bool bTiledY) + { + const TImageInfo* data = NULL; + if ( !(data=GetAniImage((LPCTSTR)sImageName)) && !(data=pManager->GetImage((LPCTSTR)sImageName)) ) + { + // new Image + // try GIF first + data = AddAniImage(pManager->GetPaintDC(), (LPCTSTR)sImageName, (LPCTSTR)sImageResType, dwMask); + if (!data) + { + // may be not GIF + if( sImageResType.IsEmpty() ) { + data = pManager->GetImageEx((LPCTSTR)sImageName, NULL, dwMask); + } + else { + data = pManager->GetImageEx((LPCTSTR)sImageName, (LPCTSTR)sImageResType, dwMask); + } + } + } + + if (!data) + return false; + + if( rcBmpPart.left == 0 && rcBmpPart.right == 0 && rcBmpPart.top == 0 && rcBmpPart.bottom == 0 ) { + rcBmpPart.right = data->nX; + rcBmpPart.bottom = data->nY; + } + if (rcBmpPart.right > data->nX) rcBmpPart.right = data->nX; + if (rcBmpPart.bottom > data->nY) rcBmpPart.bottom = data->nY; + + RECT rcTemp; + if( !::IntersectRect(&rcTemp, &rcItem, &rc) ) return true; + if( !::IntersectRect(&rcTemp, &rcItem, &rcPaint) ) return true; + + CRenderEngine::DrawImage(hDC, data->hBitmap, rcItem, rcPaint, rcBmpPart, rcCorner, data->alphaChannel, bFade, bHole, bTiledX, bTiledY); + + return true; + } + + bool CEUIAniHelper::DrawImageEx(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, + LPCTSTR pStrImage, LPCTSTR pStrModify) + { + if ((pManager == NULL) || (hDC == NULL)) return false; + + // 1aaa.jpg + // 2file='aaa.jpg' res='' restype='0' dest='0,0,0,0' source='0,0,0,0' corner='0,0,0,0' + // mask='#FF0000' fade='255' hole='false' xtiled='false' ytiled='false' + + CDuiString sImageName = pStrImage; + CDuiString sImageResType; + RECT rcItem = rc; + RECT rcBmpPart = {0}; + RECT rcCorner = {0}; + DWORD dwMask = 0; + BYTE bFade = 0xFF; + bool bHole = false; + bool bTiledX = false; + bool bTiledY = false; + + int image_count = 0; + + CDuiString sItem; + CDuiString sValue; + LPTSTR pstr = NULL; + + for( int i = 0; i < 2; ++i,image_count = 0 ) { + if( i == 1) + pStrImage = pStrModify; + + if( !pStrImage ) continue; + + while( *pStrImage != _T('\0') ) { + sItem.Empty(); + sValue.Empty(); + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + while( *pStrImage != _T('\0') && *pStrImage != _T('=') && *pStrImage > _T(' ') ) { + LPTSTR pstrTemp = ::CharNext(pStrImage); + while( pStrImage < pstrTemp) { + sItem += *pStrImage++; + } + } + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + if( *pStrImage++ != _T('=') ) break; + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + if( *pStrImage++ != _T('\'') ) break; + while( *pStrImage != _T('\0') && *pStrImage != _T('\'') ) { + LPTSTR pstrTemp = ::CharNext(pStrImage); + while( pStrImage < pstrTemp) { + sValue += *pStrImage++; + } + } + if( *pStrImage++ != _T('\'') ) break; + if( !sValue.IsEmpty() ) { + if( sItem == _T("file") || sItem == _T("res") ) { + if( image_count > 0 ) + DrawAniImage(hDC, pManager, rc, rcPaint, sImageName, sImageResType, + rcItem, rcBmpPart, rcCorner, dwMask, bFade, bHole, bTiledX, bTiledY); + + sImageName = sValue; + if( sItem == _T("file") ) + ++image_count; + } + else if( sItem == _T("restype") ) { + if( image_count > 0 ) + DrawAniImage(hDC, pManager, rc, rcPaint, sImageName, sImageResType, + rcItem, rcBmpPart, rcCorner, dwMask, bFade, bHole, bTiledX, bTiledY); + + sImageResType = sValue; + ++image_count; + } + else if( sItem == _T("dest") ) { + rcItem.left = rc.left + _tcstol(sValue.GetData(), &pstr, 10); ASSERT(pstr); + rcItem.top = rc.top + _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcItem.right = rc.left + _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + if (rcItem.right > rc.right) rcItem.right = rc.right; + rcItem.bottom = rc.top + _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + if (rcItem.bottom > rc.bottom) rcItem.bottom = rc.bottom; + } + else if( sItem == _T("source") ) { + rcBmpPart.left = _tcstol(sValue.GetData(), &pstr, 10); ASSERT(pstr); + rcBmpPart.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcBmpPart.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcBmpPart.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + } + else if( sItem == _T("corner") ) { + rcCorner.left = _tcstol(sValue.GetData(), &pstr, 10); ASSERT(pstr); + rcCorner.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcCorner.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcCorner.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + } + else if( sItem == _T("mask") ) { + if( sValue[0] == _T('#')) dwMask = _tcstoul(sValue.GetData() + 1, &pstr, 16); + else dwMask = _tcstoul(sValue.GetData(), &pstr, 16); + } + else if( sItem == _T("fade") ) { + bFade = (BYTE)_tcstoul(sValue.GetData(), &pstr, 10); + } + else if( sItem == _T("hole") ) { + bHole = (_tcscmp(sValue.GetData(), _T("true")) == 0); + } + else if( sItem == _T("xtiled") ) { + bTiledX = (_tcscmp(sValue.GetData(), _T("true")) == 0); + } + else if( sItem == _T("ytiled") ) { + bTiledY = (_tcscmp(sValue.GetData(), _T("true")) == 0); + } + } + if( *pStrImage++ != _T(' ') ) break; + } + } + + DrawAniImage(hDC, pManager, rc, rcPaint, sImageName, sImageResType, + rcItem, rcBmpPart, rcCorner, dwMask, bFade, bHole, bTiledX, bTiledY); + + return true; + } + + const TImageInfo* CEUIAniHelper::GetAniImage(LPCTSTR bitmap) + { + CAnimation* data = static_cast(m_mAniHash.Find(bitmap)); + if (data) + return data->GetCurImage(); + else + return NULL; + } + + const TImageInfo* CEUIAniHelper::AddAniImage(HDC hDC, LPCTSTR bitmap, LPCTSTR type/* = NULL*/, DWORD mask/* = 0*/) + { + CAnimation* pAniImage = new CAnimation(this); + if (!pAniImage->LoadGIF(bitmap, type, mask)) + { + delete pAniImage; + return NULL; + } + if ( !m_mAniHash.Insert(bitmap, pAniImage) ) + { + delete pAniImage; + return NULL; + } + + pAniImage->Play(); + return pAniImage->GetCurImage(); + } +} diff --git a/Demos/xlgamebox/AnimationHelper.h b/Demos/xlgamebox/AnimationHelper.h new file mode 100644 index 00000000..a1bd8aa8 --- /dev/null +++ b/Demos/xlgamebox/AnimationHelper.h @@ -0,0 +1,229 @@ +#ifndef _EUI_ANIMATION_HELPER__H__ +#define _EUI_ANIMATION_HELPER__H__ + +#include + +namespace DuiLib +{ + class IAnimationCallback + { + public: + virtual void OnFrame() = 0; + }; + + class CAnimation + { +#pragma pack(1) // turn byte alignment on + enum GIFBlockTypes + { + BLOCK_UNKNOWN, + BLOCK_APPEXT, + BLOCK_COMMEXT, + BLOCK_CONTROLEXT, + BLOCK_PLAINTEXT, + BLOCK_IMAGE, + BLOCK_TRAILER + }; + + enum ControlExtValues // graphic control extension packed field values + { + GCX_PACKED_DISPOSAL, // disposal method + GCX_PACKED_USERINPUT, + GCX_PACKED_TRANSPCOLOR + }; + + enum LSDPackedValues // logical screen descriptor packed field values + { + LSD_PACKED_GLOBALCT, + LSD_PACKED_CRESOLUTION, + LSD_PACKED_SORT, + LSD_PACKED_GLOBALCTSIZE + }; + + enum IDPackedValues // image descriptor packed field values + { + ID_PACKED_LOCALCT, + ID_PACKED_INTERLACE, + ID_PACKED_SORT, + ID_PACKED_LOCALCTSIZE + }; + + struct TGIFHeader // GIF header + { + char m_cSignature[3]; // Signature - Identifies the GIF Data Stream + // This field contains the fixed value 'GIF' + char m_cVersion[3]; // Version number. May be one of the following: + // "87a" or "89a" + }; + + struct TGIFLSDescriptor // Logical Screen Descriptor + { + WORD m_wWidth; // 2 bytes. Logical screen width + WORD m_wHeight; // 2 bytes. Logical screen height + unsigned char m_cPacked; // packed field + unsigned char m_cBkIndex; // 1 byte. Background color index + unsigned char m_cPixelAspect; // 1 byte. Pixel aspect ratio + inline int GetPackedValue(enum LSDPackedValues Value); + }; + + struct TGIFAppExtension // application extension block + { + unsigned char m_cExtIntroducer; // extension introducer (0x21) + unsigned char m_cExtLabel; // app. extension label (0xFF) + unsigned char m_cBlockSize; // fixed value of 11 + char m_cAppIdentifier[8]; // application identifier + char m_cAppAuth[3]; // application authentication code + }; + + struct TGIFControlExt // graphic control extension block + { + unsigned char m_cExtIntroducer; // extension introducer (0x21) + unsigned char m_cControlLabel; // control extension label (0xF9) + unsigned char m_cBlockSize; // fixed value of 4 + unsigned char m_cPacked; // packed field + WORD m_wDelayTime; // delay time + unsigned char m_cTColorIndex; // transparent color index + unsigned char m_cBlockTerm; // block terminator (0x00) + inline int GetPackedValue(enum ControlExtValues Value); + }; + + struct TGIFCommentExt // comment extension block + { + unsigned char m_cExtIntroducer; // extension introducer (0x21) + unsigned char m_cCommentLabel; // comment extension label (0xFE) + }; + + struct TGIFPlainTextExt // plain text extension block + { + unsigned char m_cExtIntroducer; // extension introducer (0x21) + unsigned char m_cPlainTextLabel; // text extension label (0x01) + unsigned char m_cBlockSize; // fixed value of 12 + WORD m_wLeftPos; // text grid left position + WORD m_wTopPos; // text grid top position + WORD m_wGridWidth; // text grid width + WORD m_wGridHeight; // text grid height + unsigned char m_cCellWidth; // character cell width + unsigned char m_cCellHeight; // character cell height + unsigned char m_cFgColor; // text foreground color index + unsigned char m_cBkColor; // text background color index + }; + + struct TGIFImageDescriptor // image descriptor block + { + unsigned char m_cImageSeparator; // image separator byte (0x2C) + WORD m_wLeftPos; // image left position + WORD m_wTopPos; // image top position + WORD m_wWidth; // image width + WORD m_wHeight; // image height + unsigned char m_cPacked; // packed field + inline int GetPackedValue(enum IDPackedValues Value); + }; +#pragma pack() // turn byte alignment off + struct TFrame // structure that keeps a single frame info + { + TImageInfo* m_pImage; // pointer to one frame image + SIZE m_frameSize; + SIZE m_frameOffset; + UINT m_nDelay; // delay (in 1/100s of a second) + UINT m_nDisposal; // disposal method + }; + typedef std::vector VTFRAME; + + public: + CAnimation(IAnimationCallback* pCallback); + ~CAnimation(); + + const TImageInfo* GetCurImage(); + // loads a picture from a file or a program resource + const TImageInfo* LoadGIF(LPCTSTR bitmap, LPCTSTR type = NULL, DWORD mask = 0); + // loads a picture from a memory block (allocated by malloc) + // Warning: this function DOES NOT free the memory, pointed to by pData + const TImageInfo* LoadGIF(const LPBYTE pData, DWORD dwSize, DWORD mask = 0); + SIZE GetSize() const; + int GetFrameCount() const; + COLORREF GetBkColor() const; + + bool Play(); // play the animation (just change m_nCurrFrame) + void Stop(); // stops animation + void UnLoad(); // stops animation plus releases all resources + bool IsPlaying() const; + + protected: + int GetNextBlockLen() const; + BOOL SkipNextBlock(); + BOOL SkipNextGraphicBlock(); + void ResetDataPointer(); + enum GIFBlockTypes GetNextBlock() const; + UINT GetSubBlocksLen(UINT nStartingOffset) const; + // create a single-frame GIF from one of frames, and put in a memeory block (allocated by malloc) + // Warning: we should free the memeory block, pointed to by return value + LPBYTE GetNextGraphicBlock(UINT* pBlockLen, UINT* pDelay, SIZE* pBlockSize, SIZE* pBlockOffset, UINT* pDisposal); +#ifdef GIF_TRACING + void EnumGIFBlocks(); + void WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize); +#endif // GIF_TRACING + + private: + unsigned char* m_pRawData; // ʱݴ涯ļ Ч + UINT m_nDataSize; // GIFļС + TGIFHeader* m_pGIFHeader; // GIFļͷ + TGIFLSDescriptor* m_pGIFLSDescriptor; // ߼Ļʶ + UINT m_nGlobalCTSize; // ȫɫбС + SIZE m_PictureSize; // ͼߴ + COLORREF m_clrBackground; // ɫ + volatile long m_nCurrFrame; // ǰֵ֡ + UINT m_nCurrOffset; // ȡƫ + VTFRAME* m_pvFrames; // ֡ + + HANDLE m_hThread; + HANDLE m_hDrawEvent; // ͼ¼ ǰ֡Ѿȡڻ ֹȾٶ֡ + volatile bool m_bExitThread; + IAnimationCallback* m_pCallback; + + DWORD ThreadAnimation(); + static DWORD __stdcall _ThreadAnimation(LPVOID pParam); + }; + + class CEUIAniHelper : public IAnimationCallback + { + public: + ~CEUIAniHelper() + { + CAnimation* data = NULL; + while (m_mAniHash.GetSize() > 0) + { + if(LPCTSTR key = m_mAniHash.GetAt(0)) { + data = static_cast(m_mAniHash.Find(key)); + if (data) + { + delete data; + data = NULL; + } + m_mAniHash.Remove(key); + } + } + } + /** + * ͼǿ ԭCControlUI::DrawImage CRenderEngine::DrawImageString + * Note: PaintXXImage + */ + bool DrawImageEx(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, + LPCTSTR pStrImage, LPCTSTR pStrModify = NULL); + /** + * ͼǿ ԭDuiLib::DrawImage + * Note: жļ͡ʼȡGIFݵ + */ + bool DrawAniImage(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, const CDuiString& sImageName, \ + const CDuiString& sImageResType, RECT rcItem, RECT rcBmpPart, RECT rcCorner, DWORD dwMask, BYTE bFade, \ + bool bHole, bool bTiledX, bool bTiledY); + + protected: + const TImageInfo* GetAniImage(LPCTSTR bitmap); + const TImageInfo* AddAniImage(HDC hDC, LPCTSTR bitmap, LPCTSTR type = NULL, DWORD mask = 0); + + protected: + CStdStringPtrMap m_mAniHash; + }; +} + +#endif diff --git a/Demos/xlgamebox/ControlEx.h b/Demos/xlgamebox/ControlEx.h new file mode 100644 index 00000000..f07d445d --- /dev/null +++ b/Demos/xlgamebox/ControlEx.h @@ -0,0 +1,341 @@ +#ifndef __CONTROLEX_H__ +#define __CONTROLEX_H__ + +#include +#include +#include "LableEx.h" +#include "GameItemUI.h" +#include "ShortCutUI.h" + +inline double CalculateDelay(double state) { + return pow(state, 2); +} + +// category(0)->game(1) +class GameListUI : public CListUI +{ +public: + enum { SCROLL_TIMERID = 10 }; + + struct NodeData + { + int id; + int _level; + bool _expand; + CDuiString _text; + CListLabelElementUI* _pListElement; + }; + + class Node + { + typedef std::vector Children; + Children _children; + Node* _parent; + NodeData _data; + + private: + void set_parent(Node* parent) { _parent = parent; } + + public: + Node() : _parent (NULL) {} + explicit Node(NodeData t) : _data (t), _parent (NULL) {} + Node(NodeData t, Node* parent) : _data (t), _parent (parent) {} + ~Node() + { + for (int i = 0; i < num_children(); i++) + delete _children[i]; + } + NodeData& data() { return _data; } + int num_children() const { return _children.size(); } + Node* child(int i) { return _children[i]; } + Node* parent() { return ( _parent); } + bool has_children() const { return num_children() > 0; } + void add_child(Node* child) + { + child->set_parent(this); + _children.push_back(child); + } + void remove_child(Node* child) + { + Children::iterator iter = _children.begin(); + for( ; iter < _children.end(); ++iter ) + { + if( *iter == child ) + { + _children.erase(iter); + return; + } + } + } + Node* get_last_child() + { + if( has_children() ) + { + return child(num_children() - 1)->get_last_child(); + } + else return this; + } + }; + + GameListUI() : _root(NULL), m_dwDelayDeltaY(0), m_dwDelayNum(0), m_dwDelayLeft(0) + { + SetItemShowHtml(true); + + _root = new Node; + _root->data()._level = -1; + _root->data()._expand = true; + _root->data()._pListElement = NULL; + } + + ~GameListUI() { if(_root) delete _root; } + + bool Add(CControlUI* pControl) + { + if( !pControl ) return false; + if( _tcscmp(pControl->GetClass(), _T("ListLabelElementUI")) != 0 ) return false; + return CListUI::Add(pControl); + } + + bool AddAt(CControlUI* pControl, int iIndex) + { + if( !pControl ) return false; + if( _tcscmp(pControl->GetClass(), _T("ListLabelElementUI")) != 0 ) return false; + return CListUI::AddAt(pControl, iIndex); + } + + bool Remove(CControlUI* pControl) + { + if( !pControl ) return false; + if( _tcscmp(pControl->GetClass(), _T("ListLabelElementUI")) != 0 ) return false; + + if (reinterpret_cast(static_cast(pControl->GetInterface(_T("ListLabelElement")))->GetTag()) == NULL) + return CListUI::Remove(pControl); + else + return RemoveNode(reinterpret_cast(static_cast(pControl->GetInterface(_T("ListLabelElement")))->GetTag())); + } + + bool RemoveAt(int iIndex) + { + CControlUI* pControl = GetItemAt(iIndex); + if( !pControl ) return false; + if( _tcscmp(pControl->GetClass(), _T("ListLabelElementUI")) != 0 ) return false; + + if (reinterpret_cast(static_cast(pControl->GetInterface(_T("ListLabelElement")))->GetTag()) == NULL) + return CListUI::RemoveAt(iIndex); + else + return RemoveNode(reinterpret_cast(static_cast(pControl->GetInterface(_T("ListLabelElement")))->GetTag())); + } + + void RemoveAll() + { + CListUI::RemoveAll(); + for (int i = 0; i < _root->num_children(); ++i) + { + Node* child = _root->child(i); + RemoveNode(child); + } + delete _root; + _root = new Node; + _root->data()._level = -1; + _root->data()._expand = true; + _root->data()._pListElement = NULL; + } + void SetVisible(bool bVisible = true) + { + if( m_bVisible == bVisible ) return; + CControlUI::SetVisible(bVisible); + } + + void SetInternVisible(bool bVisible = true) + { + CControlUI::SetInternVisible(bVisible); + } + + void DoEvent(TEventUI& event) + { + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pParent != NULL ) m_pParent->DoEvent(event); + else CVerticalLayoutUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_TIMER && event.wParam == SCROLL_TIMERID ) { + if( m_dwDelayLeft > 0 ) { + --m_dwDelayLeft; + SIZE sz = GetScrollPos(); + LONG lDeltaY = (LONG)(CalculateDelay((double)m_dwDelayLeft / m_dwDelayNum) * m_dwDelayDeltaY); + if( (lDeltaY > 0 && sz.cy != 0) || (lDeltaY < 0 && sz.cy != GetScrollRange().cy ) ) { + sz.cy -= lDeltaY; + SetScrollPos(sz); + return; + } + } + m_dwDelayDeltaY = 0; + m_dwDelayNum = 0; + m_dwDelayLeft = 0; + m_pManager->KillTimer(this, SCROLL_TIMERID); + return; + } + if( event.Type == UIEVENT_SCROLLWHEEL ) { + LONG lDeltaY = 0; + if( m_dwDelayNum > 0 ) lDeltaY = (LONG)(CalculateDelay((double)m_dwDelayLeft / m_dwDelayNum) * m_dwDelayDeltaY); + switch( LOWORD(event.wParam) ) { + case SB_LINEUP: + if( m_dwDelayDeltaY >= 0 ) m_dwDelayDeltaY = lDeltaY + 8; + else m_dwDelayDeltaY = lDeltaY + 12; + break; + case SB_LINEDOWN: + if( m_dwDelayDeltaY <= 0 ) m_dwDelayDeltaY = lDeltaY - 8; + else m_dwDelayDeltaY = lDeltaY - 12; + break; + } + if( m_dwDelayDeltaY > 100 ) m_dwDelayDeltaY = 100; + else if( m_dwDelayDeltaY < -100 ) m_dwDelayDeltaY = -100; + m_dwDelayNum = (DWORD)sqrt((double)abs(m_dwDelayDeltaY)) * 5; + m_dwDelayLeft = m_dwDelayNum; + m_pManager->SetTimer(this, SCROLL_TIMERID, 50U); + return; + } + + CListUI::DoEvent(event); + } + + Node* GetRoot() { return _root; } + + Node* AddNode(LPCTSTR text, int id, Node* parent = NULL) + { + if( !parent ) parent = _root; + + CListLabelElementUI* pListElement = new CListLabelElementUI; + Node* node = new Node; + node->data().id = id; + node->data()._level = parent->data()._level + 1; + if( node->data()._level == 0 ) node->data()._expand = true; + else node->data()._expand = false; + node->data()._text = text; + node->data()._pListElement = pListElement; + + if( parent != _root ) { + if( !(parent->data()._expand && parent->data()._pListElement->IsVisible()) ) + pListElement->SetInternVisible(false); + } + + CDuiString html_text; + html_text += _T(""); + for( int i = 0; i < node->data()._level; ++i ) { + html_text += _T(""); + } + if( node->data()._level < 1 ) { + if( node->data()._expand ) html_text += _T(""); + else html_text += _T(""); + } + html_text += node->data()._text; + pListElement->SetText(html_text); + pListElement->SetTag((UINT_PTR)node); + if( node->data()._level == 0 ) { + pListElement->SetBkImage(_T("file='tree_top.png' corner='2,1,2,1' fade='100'")); + } + + int index = 0; + if( parent->has_children() ) { + Node* prev = parent->get_last_child(); + index = prev->data()._pListElement->GetIndex() + 1; + } + else { + if( parent == _root ) index = 0; + else index = parent->data()._pListElement->GetIndex() + 1; + } + if( !CListUI::AddAt(pListElement, index) ) { + delete pListElement; + delete node; + node = NULL; + } + parent->add_child(node); + return node; + } + + bool RemoveNode(Node* node) + { + if( !node || node == _root ) return false; + for( int i = 0; i < node->num_children(); ++i ) { + Node* child = node->child(i); + RemoveNode(child); + } + CListUI::Remove(node->data()._pListElement); + node->parent()->remove_child(node); + delete node; + return true; + } + + void ExpandNode(Node* node, bool expand) + { + if( !node || node == _root ) return; + + if( node->data()._expand == expand ) return; + node->data()._expand = expand; + + CDuiString html_text; + html_text += _T(""); + for( int i = 0; i < node->data()._level; ++i ) { + html_text += _T(""); + } + if( node->data()._level < 1 ) { + if( node->data()._expand ) html_text += _T(""); + else html_text += _T(""); + } + html_text += node->data()._text; + node->data()._pListElement->SetText(html_text); + + if( !node->data()._pListElement->IsVisible() ) return; + if( !node->has_children() ) return; + + Node* begin = node->child(0); + Node* end = node->get_last_child(); + int nFirst = begin->data()._pListElement->GetIndex(); + for( int i = nFirst; i <= end->data()._pListElement->GetIndex(); ++i ) { + CControlUI* control = GetItemAt(i); + if( _tcscmp(control->GetClass(), _T("ListLabelElementUI")) == 0 ) { + + Node* local_parent = ((GameListUI::Node*)control->GetTag())->parent(); + control->SetInternVisible(local_parent->data()._expand && local_parent->data()._pListElement->IsVisible()); + } + } + + NeedUpdate(); + } + + SIZE GetExpanderSizeX(Node* node) const + { + if( !node || node == _root ) return CSize(); + if( node->data()._level >= 3 ) return CSize(); + + SIZE szExpander = {0}; + szExpander.cx = 6 + 24 * node->data()._level - 4/*ʵŴһ*/; + szExpander.cy = szExpander.cx + 16 + 8/*ʵŴһ*/; + return szExpander; + } + +private: + Node* _root; + + LONG m_dwDelayDeltaY; + DWORD m_dwDelayNum; + DWORD m_dwDelayLeft; +}; + +class CDialogBuilderCallbackEx : public IDialogBuilderCallback +{ +public: + CControlUI* CreateControl(LPCTSTR pstrClass) + { + if( _tcsicmp(pstrClass, _T("GameList")) == 0 ) return new GameListUI; + else if( _tcsicmp(pstrClass, _T("GameItem")) == 0 ) return new CGameItemUI; + else if( _tcsicmp(pstrClass, _T("ShortCut")) == 0 ) return new CShortCutUI; + else if( _tcsicmp(pstrClass, _T("LabelEx")) == 0 ) return new CLabelEx; + else if( _tcsicmp(pstrClass, _T("LabelMutiline")) == 0 ) return new CLabelMutiline; + return NULL; + } +}; + + +#endif __CONTROLEX_H__ diff --git a/Demos/xlgamebox/Debug/XLGameBox.bsc b/Demos/xlgamebox/Debug/XLGameBox.bsc new file mode 100644 index 00000000..63b6bf70 Binary files /dev/null and b/Demos/xlgamebox/Debug/XLGameBox.bsc differ diff --git a/Demos/xlgamebox/GameItemUI.cpp b/Demos/xlgamebox/GameItemUI.cpp new file mode 100644 index 00000000..626550f8 --- /dev/null +++ b/Demos/xlgamebox/GameItemUI.cpp @@ -0,0 +1,52 @@ +#include "StdAfx.h" +#include "GameItemUI.h" + +CGameItemUI::CGameItemUI() +{ +} + + +CGameItemUI::~CGameItemUI(void) +{ + if(m_hIcon != NULL) + ::DestroyIcon(m_hIcon); +} + +void CGameItemUI::DoEvent(DuiLib::TEventUI& event) +{ + if(event.Type == DuiLib::UIEVENT_DBLCLICK) + { + m_pManager->SendNotify(this, _T("ldbclick"), event.wParam, event.lParam); + m_pManager->ReleaseCapture(); + } + + if(event.Type == DuiLib::UIEVENT_SETCURSOR) + { + ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); + return; + } + + return COptionUI::DoEvent(event); +} + +void CGameItemUI::PaintStatusImage(HDC hDC) +{ + COptionUI::PaintStatusImage(hDC); + if(m_hIcon != NULL) + { + ICONINFO ii; + if(::GetIconInfo(m_hIcon, &ii)) + { + BITMAP bmp; + if(::GetObject(ii.hbmColor, sizeof(bmp), &bmp)) + ::DrawIcon(hDC, m_rcItem.left+(m_rcItem.right-m_rcItem.left-bmp.bmWidth)/2, m_rcItem.top+4, m_hIcon); + ::DeleteObject(ii.hbmColor); + ::DeleteObject(ii.hbmMask); + } + } +} + +void CGameItemUI::SetIcon(HICON hIcon) +{ + m_hIcon = hIcon; +} diff --git a/Demos/xlgamebox/GameItemUI.h b/Demos/xlgamebox/GameItemUI.h new file mode 100644 index 00000000..b090277b --- /dev/null +++ b/Demos/xlgamebox/GameItemUI.h @@ -0,0 +1,20 @@ +#pragma once +#include "LableEx.h" + +class CGameItemUI : public DuiLib::COptionUI +{ +public: + CGameItemUI(); + ~CGameItemUI(void); + +public: + void DoEvent(DuiLib::TEventUI& event); + virtual void PaintStatusImage(HDC hDC); + +public: + void SetIcon(HICON hIcon); + +private: + HICON m_hIcon; +}; + diff --git a/Demos/xlgamebox/LableEx.h b/Demos/xlgamebox/LableEx.h new file mode 100644 index 00000000..c0ee3a93 --- /dev/null +++ b/Demos/xlgamebox/LableEx.h @@ -0,0 +1,130 @@ +#ifndef __LABELEX_H__ +#define __LABELEX_H__ +#include "AnimationHelper.h" + +namespace DuiLib +{ + class CLabelEx : public CLabelUI, public CEUIAniHelper + { + public: + void CLabelEx::PaintBkImage(HDC hDC) + { + if( m_sBkImage.IsEmpty() ) + return; + if( !DrawImageEx(hDC, m_pManager, m_rcItem, m_rcPaint, (LPCTSTR)m_sBkImage) ) + m_sBkImage.Empty(); + } + + void CLabelEx::OnFrame() + { + Invalidate(); + } + + void PaintText(HDC hDC) + { + if( m_dwTextColor == 0 ) m_dwTextColor = m_pManager->GetDefaultFontColor(); + if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor(); + + if( m_sText.IsEmpty() ) return; + int nLinks = 0; + RECT rc = m_rcItem; + rc.left += m_rcTextPadding.left; + rc.right -= m_rcTextPadding.right; + rc.top += m_rcTextPadding.top; + rc.bottom -= m_rcTextPadding.bottom; + if( IsEnabled() ) { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + NULL, NULL, nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + m_iFont, m_uTextStyle); + } + else { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + NULL, NULL, nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + m_iFont, m_uTextStyle); + } + } + }; + + class CLabelIcon: public DuiLib::CLabelUI + { + public: + CLabelIcon() + { + m_hIcon = NULL; + } + ~CLabelIcon(void) + { + if(m_hIcon != NULL) + ::DestroyIcon(m_hIcon); + } + + public: + virtual void PaintStatusImage(HDC hDC) + { + CLabelUI::PaintStatusImage(hDC); + if(m_hIcon != NULL) + { + ICONINFO ii; + if(::GetIconInfo(m_hIcon, &ii)) + { + BITMAP bmp; + if(::GetObject(ii.hbmColor, sizeof(bmp), &bmp)) + ::DrawIcon(hDC, m_rcItem.left+(m_rcItem.right-m_rcItem.left-bmp.bmWidth)/2, m_rcItem.top+4, m_hIcon); + ::DeleteObject(ii.hbmColor); + ::DeleteObject(ii.hbmMask); + } + } + } + + public: + void SetIcon(HICON hIcon) + { + m_hIcon = hIcon; + } + + private: + HICON m_hIcon; + }; + + class CLabelMutiline: public CLabelUI + { + public: + void PaintText(HDC hDC) + { + if( m_dwTextColor == 0 ) m_dwTextColor = m_pManager->GetDefaultFontColor(); + if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor(); + + if( m_sText.IsEmpty() ) return; + int nLinks = 0; + RECT rc = m_rcItem; + rc.left += m_rcTextPadding.left; + rc.right -= m_rcTextPadding.right; + rc.top += m_rcTextPadding.top; + rc.bottom -= m_rcTextPadding.bottom; + if( IsEnabled() ) { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + NULL, NULL, nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + m_iFont, m_uTextStyle); + } + else { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + NULL, NULL, nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + m_iFont, m_uTextStyle); + } + } + }; +} + +#endif __LABELEX_H__ diff --git a/Demos/xlgamebox/MainWnd.cpp b/Demos/xlgamebox/MainWnd.cpp new file mode 100644 index 00000000..0bf634c6 --- /dev/null +++ b/Demos/xlgamebox/MainWnd.cpp @@ -0,0 +1,348 @@ +#include "StdAfx.h" +#include "MainWnd.h" + +////////////////////////////////////////////////////////////////////////// +/// + +DUI_BEGIN_MESSAGE_MAP(CMainPage, CNotifyPump) + DUI_ON_MSGTYPE(DUI_MSGTYPE_CLICK,OnClick) + DUI_ON_MSGTYPE(DUI_MSGTYPE_SELECTCHANGED,OnSelectChanged) + DUI_ON_MSGTYPE(DUI_MSGTYPE_ITEMCLICK,OnItemClick) +DUI_END_MESSAGE_MAP() + +CMainPage::CMainPage() +{ + m_pPaintManager = NULL; +} + +void CMainPage::SetPaintMagager(CPaintManagerUI* pPaintMgr) +{ + m_pPaintManager = pPaintMgr; +} + +void CMainPage::OnClick(TNotifyUI& msg) +{ + if(msg.pSender->GetName() == _T("down_ico")) + { + CControlUI *find_ctrl =m_pPaintManager->FindSubControlByName(msg.pSender->GetParent()->GetParent(), _T("down_name")); + + if(find_ctrl) + { + MessageBox(NULL, + find_ctrl->GetText()+_T(" ʾδѡеİťðťҵlistcontainer.."), + _T("DUILIB DEMO"), MB_OK); + ((CLabelUI *)find_ctrl)->SetText(_T("ɳ̬ú...")); + } + } + else if(msg.pSender->GetName() == _T("down_del")) + { + CListUI *down_list = + static_cast(m_pPaintManager->FindControl(_T("down_list_tab"))); + if(!down_list) + return; + + down_list->RemoveAt(down_list->GetCurSel()); + } + else if(msg.pSender->GetName() == _T("down_new")) + { + CListUI *down_list = static_cast(m_pPaintManager->FindControl(_T("down_list_tab"))); + if(!down_list) + return; + + CListContainerElementUI *new_node = new CListContainerElementUI; + new_node->ApplyAttributeList(_T("height=\"45\"")); + + CHorizontalLayoutUI *new_h_lay = new CHorizontalLayoutUI; + new_h_lay->ApplyAttributeList(_T("float=\"false\" ")\ + _T("childpadding=\"10\" inset=\"3,5,3,5\"")); + + CButtonUI *new_btn_1 = new CButtonUI; + new_btn_1->ApplyAttributeList( + _T("name=\"down_ico\" float=\"false\" ")\ + _T("bordersize=\"0\" width=\"32\" maxheight=\"26\" ")\ + _T("bkimage=\"downlist_app.png\" ")\ + _T("normalimage=\"file='downlist_run.png' dest='20,14,32,26'\"")); + + CVerticalLayoutUI *new_v_lay = new CVerticalLayoutUI; + new_h_lay->Add(new_btn_1); + new_h_lay->Add(new_v_lay); + + CLabelUI *new_label = new CLabelUI; + new_label->ApplyAttributeList(_T("textcolor=\"#FFAAAAAA\" showhtml=\"true\"")); + new_label->SetText(_T("new added item.exe")); + new_label->SetName(_T("down_name")); + CProgressUI *new_progress = new CProgressUI; + new_progress->SetMinValue(0); + new_progress->SetMaxValue(100); + new_progress->SetValue(1); + new_progress->SetMaxWidth(200); + new_progress->SetMaxHeight(7); + new_progress->SetForeImage(_T("progress_fore.png")); + new_progress->SetName(_T("down_progress")); + new_v_lay->Add(new_label); + new_v_lay->Add(new_progress); + + CLabelUI *new_label2 = new CLabelUI; + CLabelUI *new_label3 = new CLabelUI; + CVerticalLayoutUI *new_v_lay2 = new CVerticalLayoutUI; + new_h_lay->Add(new_v_lay2); + new_v_lay2->Add(new_label2); + new_v_lay2->Add(new_label3); + new_label2->ApplyAttributeList( + _T("align=\"right\" text=\"\" textcolor=\"#FFAAAAAA\" showhtml=\"true\"")); + new_label3->ApplyAttributeList( + _T("align=\"right\" text=\"0.00K/34.33M \" textcolor=\"#FFAAAAAA\" showhtml=\"true\"")); + + new_node->Add(new_h_lay); + down_list->Add(new_node); + } +} + +void CMainPage::OnSelectChanged( TNotifyUI &msg ) +{ + +} + +void CMainPage::OnItemClick( TNotifyUI &msg ) +{ + +} + +////////////////////////////////////////////////////////////////////////// +/// + +DUI_BEGIN_MESSAGE_MAP(CMainWnd, WindowImplBase) + DUI_ON_MSGTYPE(DUI_MSGTYPE_CLICK,OnClick) + DUI_ON_MSGTYPE(DUI_MSGTYPE_SELECTCHANGED,OnSelectChanged) + DUI_ON_MSGTYPE(DUI_MSGTYPE_ITEMCLICK,OnItemClick) +DUI_END_MESSAGE_MAP() + +CMainWnd::CMainWnd(void) +{ + m_MainPage.SetPaintMagager(&m_PaintManager); + + AddVirtualWnd(_T("mainpage"),&m_MainPage); +} + +CMainWnd::~CMainWnd(void) +{ + RemoveVirtualWnd(_T("mainpage")); +} + +void CMainWnd::OnFinalMessage( HWND hWnd) +{ + __super::OnFinalMessage(hWnd); + delete this; +} + +DuiLib::CDuiString CMainWnd::GetSkinFolder() +{ +#ifdef _DEBUG + return _T("skin\\XLGameBox\\"); +#else + return _T("skin\\"); +#endif + +} + +DuiLib::CDuiString CMainWnd::GetSkinFile() +{ + return _T("main.xml"); +} + +UILIB_RESOURCETYPE CMainWnd::GetResourceType() const +{ +#ifdef _DEBUG + return UILIB_ZIPRESOURCE; +#else + return UILIB_ZIP; +#endif +} + +DuiLib::CDuiString CMainWnd::GetZIPFileName() const +{ + return _T("skin.zip"); +} + +LPCTSTR CMainWnd::GetWindowClassName( void ) const +{ + return _T("MainWnd"); +} + +void CMainWnd::OnClick( TNotifyUI &msg ) +{ + CDuiString sName = msg.pSender->GetName(); + sName.MakeLower(); + + if( msg.pSender == m_pCloseBtn ) + { + PostQuitMessage(0); + return; + }else if( msg.pSender == m_pMinBtn ) + { + SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0); + return; + }else if( msg.pSender == m_pMaxBtn ) + { + SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, 0); + return; + }else if( msg.pSender == m_pRestoreBtn ) + { + SendMessage(WM_SYSCOMMAND, SC_RESTORE, 0); + return; + } + else if( msg.pSender->GetName() == _T("quitbtn") ) + { + PostQuitMessage(0); + } + else if(sName == _T("gamehome") || sName == _T("gamesearch") || sName == _T("gamephone")) + { + CControlUI* pTabLeft = m_PaintManager.FindControl(_T("body_main_left")); + pTabLeft->SetVisible(sName != _T("gamephone")); + CTabLayoutUI* pTabLayout = static_cast(m_PaintManager.FindControl(_T("body_main_tablayout"))); + CControlUI* pItem = m_PaintManager.FindControl(msg.pSender->GetUserData()); + pTabLayout->SelectItem(pItem); + + } +} + +void CMainWnd::OnSelectChanged( TNotifyUI &msg ) +{ + if(msg.pSender->GetName() == _T("down_list")) + { + static_cast(m_PaintManager.FindControl(_T("tab_main")))->SelectItem(0); + } + else if(msg.pSender->GetName() == _T("down_his")) + { + static_cast(m_PaintManager.FindControl(_T("tab_main")))->SelectItem(1); + } +} + +void CMainWnd::OnItemClick( TNotifyUI &msg ) +{ + TCHAR alert_msg[64] = {0}; + int index = ((CListContainerElementUI *)msg.pSender)->GetIndex(); + wsprintf(alert_msg, _T("ѡ%d, ұڵĿ..."), index); + MessageBox(NULL, alert_msg, _T("DUILIB DEMO"), MB_OK); + + CControlUI *find_ctrl =m_PaintManager.FindSubControlByName(msg.pSender, _T("down_name")); + + if(find_ctrl) + { + MessageBox(NULL, + find_ctrl->GetText()+_T(" ѡеĿ.."), + _T("DUILIB DEMO"), MB_OK); + ((CLabelUI *)find_ctrl)->SetText(_T("ɳ̬ú...")); + } + else + { + MessageBox(NULL, _T("δΪؼnameҲҪĿؼ"), + _T("DUILIB DEMO"), MB_OK); + } + + find_ctrl =m_PaintManager.FindSubControlByName(msg.pSender, _T("down_progress")); + + if(find_ctrl) + { + TCHAR alert_msg[256] = {0}; + wsprintf(alert_msg, _T("ֵ:%d"), ((CProgressUI *)find_ctrl)->GetValue()); + MessageBox(NULL, alert_msg, _T("DUILIB DEMO"), MB_OK); + + ((CProgressUI *)find_ctrl)->SetValue(30); + MessageBox(NULL, _T("޸˽ֵ"), _T("DUILIB DEMO"), MB_OK); + } +} + +void CMainWnd::Notify( TNotifyUI &msg ) +{ + return WindowImplBase::Notify(msg); +} + +LRESULT CMainWnd::OnMouseWheel( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) +{ + // ieؼղϢ + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + ::ScreenToClient(m_PaintManager.GetPaintWindow(), &pt); + CControlUI* pControl = static_cast(m_PaintManager.FindControl(_T("ie"))); + if( pControl && pControl->IsVisible() ) { + RECT rc = pControl->GetPos(); + if( ::PtInRect(&rc, pt) ) { + return CWindowWnd::HandleMessage(uMsg, wParam, lParam); + } + } + + bHandled = FALSE; + return 0; +} + +LRESULT CMainWnd::OnSysCommand( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) +{ + // ʱյWM_NCDESTROYյwParamΪSC_CLOSEWM_SYSCOMMAND + if( wParam == SC_CLOSE ) { + ::PostQuitMessage(0L); + bHandled = TRUE; + return 0; + } + BOOL bZoomed = ::IsZoomed(*this); + LRESULT lRes = CWindowWnd::HandleMessage(uMsg, wParam, lParam); + if( ::IsZoomed(*this) != bZoomed ) { + if( !bZoomed ) { + CControlUI* pControl = static_cast(m_PaintManager.FindControl(_T("maxbtn"))); + if( pControl ) pControl->SetVisible(false); + pControl = static_cast(m_PaintManager.FindControl(_T("restorebtn"))); + if( pControl ) pControl->SetVisible(true); + } + else { + CControlUI* pControl = static_cast(m_PaintManager.FindControl(_T("maxbtn"))); + if( pControl ) pControl->SetVisible(true); + pControl = static_cast(m_PaintManager.FindControl(_T("restorebtn"))); + if( pControl ) pControl->SetVisible(false); + } + } + return lRes; +} + +void CMainWnd::InitWindow() +{ + m_pCloseBtn = static_cast(m_PaintManager.FindControl(_T("closebtn"))); + m_pMaxBtn = static_cast(m_PaintManager.FindControl(_T("maxbtn"))); + m_pRestoreBtn = static_cast(m_PaintManager.FindControl(_T("restorebtn"))); + m_pMinBtn = static_cast(m_PaintManager.FindControl(_T("minbtn"))); + + CWebBrowserUI* pWebHome = static_cast(m_PaintManager.FindControl(_T("main_web_home"))); + CWebBrowserUI* pWebSearch = static_cast(m_PaintManager.FindControl(_T("main_web_search"))); + CWebBrowserUI* pWebPhone = static_cast(m_PaintManager.FindControl(_T("main_web_phone"))); + pWebHome->Navigate2(_T("http://v2.boxpage.niu.xunlei.com/v3/index.html?timestamp=1399907545")); + pWebSearch->Navigate2(_T("http://v2.boxpage.niu.xunlei.com/v3/index.html?timestamp=1399907545")); + pWebPhone->Navigate2(_T("http://v2.boxpage.niu.xunlei.com/v3/index.html?timestamp=1399907545")); +} + +LRESULT CMainWnd::OnMouseHover(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + CControlUI* pHover = m_PaintManager.FindControl(pt); + if( pHover == NULL ) return 0; + /*ʾͣбͼʱ̬任ͼ״̬ʾ*/ + if(pHover->GetName() == _T("down_ico")) + { + MessageBox(NULL, _T("ijؼ簴ťͣ󣬶Ŀؼı״̬ͼС"), _T("DUILIB DEMO"), MB_OK); + ((CButtonUI *)pHover)->ApplyAttributeList( + _T("normalimage=\"file='downlist_pause.png' dest='15,9,32,26'\"")); + } + return 0; +} + +LRESULT CMainWnd::OnChar( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) +{ + /*ʾϢĴ*/ + TCHAR press_char = (TCHAR)wParam; + if(press_char == VK_BACK) + { + MessageBox(NULL, _T("˻˼"), _T("DUILIB DEMO"), MB_OK); + } + else + { + bHandled = FALSE; + } + return 0; +} diff --git a/Demos/xlgamebox/MainWnd.h b/Demos/xlgamebox/MainWnd.h new file mode 100644 index 00000000..9712b43f --- /dev/null +++ b/Demos/xlgamebox/MainWnd.h @@ -0,0 +1,58 @@ +#pragma once + +////////////////////////////////////////////////////////////////////////// +/// + +class CMainPage : public CNotifyPump +{ +public: + CMainPage(); + void SetPaintMagager(CPaintManagerUI* pPaintMgr); + + DUI_DECLARE_MESSAGE_MAP() + virtual void OnClick(TNotifyUI& msg); + virtual void OnSelectChanged( TNotifyUI &msg ); + virtual void OnItemClick( TNotifyUI &msg ); +private: + CPaintManagerUI* m_pPaintManager; +}; + +////////////////////////////////////////////////////////////////////////// +/// + +class CMainWnd : public WindowImplBase +{ +public: + CMainWnd(void); + ~CMainWnd(void); + + virtual void OnFinalMessage( HWND ); + virtual CDuiString GetSkinFolder(); + virtual CDuiString GetSkinFile(); + + virtual LPCTSTR GetWindowClassName( void ) const; + virtual void Notify( TNotifyUI &msg ); + virtual LRESULT OnMouseWheel( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + virtual LRESULT OnSysCommand( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ); + virtual void InitWindow(); + virtual LRESULT OnMouseHover( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ); + virtual LRESULT OnChar( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ); + virtual UILIB_RESOURCETYPE GetResourceType() const; + virtual LPCTSTR GetResourceID() const + { + return _T("IDR_RES_SKIN1"); + } + virtual CDuiString GetZIPFileName() const; + + DUI_DECLARE_MESSAGE_MAP() + virtual void OnClick(TNotifyUI& msg); + virtual void OnSelectChanged( TNotifyUI &msg ); + virtual void OnItemClick( TNotifyUI &msg ); + +private: + CButtonUI* m_pCloseBtn; + CButtonUI* m_pMaxBtn; + CButtonUI* m_pRestoreBtn; + CButtonUI* m_pMinBtn; + CMainPage m_MainPage; +}; diff --git a/Demos/xlgamebox/Res/XLGameBox.ico b/Demos/xlgamebox/Res/XLGameBox.ico new file mode 100644 index 00000000..75c24464 Binary files /dev/null and b/Demos/xlgamebox/Res/XLGameBox.ico differ diff --git a/Demos/xlgamebox/Res/xlgamebox.zip b/Demos/xlgamebox/Res/xlgamebox.zip new file mode 100644 index 00000000..46915425 Binary files /dev/null and b/Demos/xlgamebox/Res/xlgamebox.zip differ diff --git a/Demos/xlgamebox/SearchWnd.h b/Demos/xlgamebox/SearchWnd.h new file mode 100644 index 00000000..50697e77 --- /dev/null +++ b/Demos/xlgamebox/SearchWnd.h @@ -0,0 +1,132 @@ +#ifndef __SEARCHWND_H__ +#define __SEARCHWND_H__ + +class CSearchWnd : public CWindowWnd, public INotifyUI +{ +public: + CSearchWnd(CControlUI *pOwner) : m_pOwner(pOwner) { } + +public: + void ShowWindow(bool bShow /* = true */, bool bTakeFocus = true ) + { + CWindowWnd::ShowWindow(bShow, bTakeFocus); + AdjustPos(); + } + + void AddItem(CDuiString sItem) + { + CListUI* pGameList = static_cast(m_pm.FindControl(_T("searchlist"))); + if(pGameList != NULL) + { + CDialogBuilder builder; + CListContainerElementUI* pGameItem = static_cast(builder.Create(_T("searchitem.xml"), (UINT)0)); + CControlUI *pText = pGameItem->FindSubControl(_T("searchitem_text")); + pGameList->Add(pGameItem); + pText->SetText(sItem); + + AdjustPos(); + } + } + + void RemoveAll() + { + CListUI* pGameList = static_cast(m_pm.FindControl(_T("searchlist"))); + if(pGameList != NULL) + { + pGameList->RemoveAll(); + AdjustPos(); + } + } + + void AdjustPos() + { + CListUI* pGameList = static_cast(m_pm.FindControl(_T("searchlist"))); + if(pGameList != NULL) + { + int nHeight = 0; + for (int i = 0; i< pGameList->GetCount(); i++) + { + CControlUI *pItem = pGameList->GetItemAt(i); + + if(pItem != NULL) nHeight += pItem->GetFixedHeight(); + } + + if(nHeight > 200) nHeight = 200; + RECT rcWnd; + GetClientRect(m_hWnd, &rcWnd); + SetWindowPos(m_hWnd, NULL, 0, 0, rcWnd.right - rcWnd.left, nHeight, SWP_NOACTIVATE | SWP_NOMOVE); + } + } +public: + LPCTSTR GetWindowClassName() const { return _T("MenuWnd"); } + void OnFinalMessage(HWND /*hWnd*/) { delete this; } + + void Notify(TNotifyUI& msg) + { + if( msg.sType == _T("itemselect") ) { + ShowWindow(false); + CListUI* pGameList = static_cast(m_pm.FindControl(_T("searchlist"))); + if(pGameList != NULL) + { + if( m_pOwner ) m_pOwner->GetManager()->SendNotify(pGameList->GetItemAt(pGameList->GetCurSel()), _T("itemselect"), pGameList->GetCurSel(), 0, true); + } + } + } + + HWND Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x = CW_USEDEFAULT, int y = CW_USEDEFAULT, int cx = CW_USEDEFAULT, int cy = CW_USEDEFAULT, HMENU hMenu = NULL) + { + dwExStyle |= WS_EX_TOOLWINDOW; + return CWindowWnd::Create(hwndParent, pstrName, dwStyle, dwExStyle, x, y, cx, cy, hMenu); + } + + LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + m_pm.Init(m_hWnd); + CDialogBuilder builder; + CControlUI* pRoot = builder.Create(_T("search.xml"), (UINT)0, NULL, &m_pm); + ASSERT(pRoot && "Failed to parse XML"); + m_pm.AttachDialog(pRoot); + m_pm.AddNotifier(this); + m_pm.SetRoundCorner(3, 3); + + return 0; + } + + LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + if( (HWND)wParam == m_hWnd ) bHandled = TRUE; + else { + bHandled = FALSE; + } + return 0; + } + + LRESULT OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + if( wParam == VK_ESCAPE ) Close(); + return 0; + } + + LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + LRESULT lRes = 0; + BOOL bHandled = TRUE; + switch( uMsg ) { + case WM_CREATE: lRes = OnCreate(uMsg, wParam, lParam, bHandled); break; + case WM_KILLFOCUS: lRes = OnKillFocus(uMsg, wParam, lParam, bHandled); break; + case WM_KEYDOWN: lRes = OnKeyDown(uMsg, wParam, lParam, bHandled); break; + default: + bHandled = FALSE; + } + if( bHandled ) return lRes; + if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes; + return CWindowWnd::HandleMessage(uMsg, wParam, lParam); + } + +public: + CPaintManagerUI m_pm; + CControlUI* m_pOwner; + POINT m_ptPos; +}; + +#endif __SEARCHWND_H__ diff --git a/Demos/xlgamebox/ShortCutUI.cpp b/Demos/xlgamebox/ShortCutUI.cpp new file mode 100644 index 00000000..0b7c50db --- /dev/null +++ b/Demos/xlgamebox/ShortCutUI.cpp @@ -0,0 +1,41 @@ +#include "StdAfx.h" +#include "ShortCutUI.h" + + +CShortCutUI::CShortCutUI() +{ +} + + +CShortCutUI::~CShortCutUI(void) +{ + if(m_hIcon != NULL) + ::DestroyIcon(m_hIcon); +} + +void CShortCutUI::PaintStatusImage(HDC hDC) +{ + CButtonUI::PaintStatusImage(hDC); + if(m_hIcon != NULL) + { + ICONINFO ii; + if(::GetIconInfo(m_hIcon, &ii)) + { + BITMAP bmp; + if(::GetObject(ii.hbmColor, sizeof(bmp), &bmp)) + ::DrawIcon(hDC, m_rcItem.left, m_rcItem.top, m_hIcon); + ::DeleteObject(ii.hbmColor); + ::DeleteObject(ii.hbmMask); + } + } +} + +void CShortCutUI::SetText(LPCTSTR pstrText) +{ + m_sText = pstrText; +} + +void CShortCutUI::SetIcon(HICON hIcon) +{ + m_hIcon = hIcon; +} diff --git a/Demos/xlgamebox/ShortCutUI.h b/Demos/xlgamebox/ShortCutUI.h new file mode 100644 index 00000000..d8f1aaa3 --- /dev/null +++ b/Demos/xlgamebox/ShortCutUI.h @@ -0,0 +1,19 @@ +#pragma once + +class CShortCutUI : public DuiLib::CButtonUI +{ +public: + CShortCutUI(); + ~CShortCutUI(void); + +public: + virtual void PaintStatusImage(HDC hDC); + virtual void SetText(LPCTSTR pstrText); + +public: + void SetIcon(HICON hIcon); + +private: + HICON m_hIcon; +}; + diff --git a/Demos/xlgamebox/StdAfx.cpp b/Demos/xlgamebox/StdAfx.cpp new file mode 100644 index 00000000..79228f61 --- /dev/null +++ b/Demos/xlgamebox/StdAfx.cpp @@ -0,0 +1,15 @@ +// stdafx.cpp : source file that includes just the standard includes +// App.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +#if defined _M_IX86 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_IA64 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_X64 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#else +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif diff --git a/Demos/xlgamebox/StdAfx.h b/Demos/xlgamebox/StdAfx.h new file mode 100644 index 00000000..0230dea5 --- /dev/null +++ b/Demos/xlgamebox/StdAfx.h @@ -0,0 +1,36 @@ + +#if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) +#define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_ + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include +#include +#include + +#include "..\..\DuiLib\UIlib.h" +using namespace DuiLib; + +#ifdef _DEBUG +# ifdef _UNICODE +# pragma comment(lib, "..\\..\\lib\\DuiLib_ud.lib") +# else +# pragma comment(lib, "..\\..\\lib\\DuiLib_d.lib") +# endif +#else +# ifdef _UNICODE +# pragma comment(lib, "..\\..\\lib\\DuiLib_u.lib") +# else +# pragma comment(lib, "..\\..\\lib\\DuiLib.lib") +# endif +#endif + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) diff --git a/Demos/xlgamebox/XLGameBox.cpp b/Demos/xlgamebox/XLGameBox.cpp new file mode 100644 index 00000000..e50530e8 --- /dev/null +++ b/Demos/xlgamebox/XLGameBox.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "resource.h" +#include "MainWnd.h" + +static LPBYTE resource_zip_buffer_ = NULL; + +//#define USE_EMBEDED_RESOURCE + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow) +{ + CPaintManagerUI::SetInstance(hInstance); + + HRESULT Hr = ::CoInitialize(NULL); + if( FAILED(Hr) ) return 0; + + CMainWnd* pFrame = new CMainWnd(); + if( pFrame == NULL ) return 0; + pFrame->Create(NULL, _T("ѸϷ"), UI_WNDSTYLE_FRAME, 0L, 0, 0, 990,690); + pFrame->CenterWindow(); + + CPaintManagerUI::MessageLoop(); + + ::CoUninitialize(); + return 0; +} \ No newline at end of file diff --git a/Demos/xlgamebox/XLGameBox.rc b/Demos/xlgamebox/XLGameBox.rc new file mode 100644 index 00000000..0cff4e81 Binary files /dev/null and b/Demos/xlgamebox/XLGameBox.rc differ diff --git a/Demos/xlgamebox/XLGameBox.vcxproj b/Demos/xlgamebox/XLGameBox.vcxproj new file mode 100644 index 00000000..797276db --- /dev/null +++ b/Demos/xlgamebox/XLGameBox.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + XLGameBox + {0278E09B-92AA-4C18-ACB5-EEDF958395B1} + 360 + + + + + + + + + + + + Application + false + Unicode + + + Application + false + Unicode + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)bin\ + $(SolutionDir)temp\XLGameBox\release\ + true + $(SolutionDir)bin\ + $(SolutionDir)temp\XLGameBox\release\ + false + AllRules.ruleset + + + AllRules.ruleset + + + $(ProjectName)_d + $(ProjectName) + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/XLGameBox.tlb + + + + + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + Use + stdafx.h + true + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x0406 + + + true + true + true + Windows + MachineX86 + + + true + .\Debug/XLGameBox.bsc + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/XLGameBox.tlb + + + + + MinSpace + OnlyExplicitInline + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreaded + true + Use + stdafx.h + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0406 + + + true + Windows + MachineX86 + libcmt.lib + + + true + .\Release/XLGameBox.bsc + + + + + + + + + + NotUsing + NotUsing + + + %(PreprocessorDefinitions) + Create + %(PreprocessorDefinitions) + Create + + + NotUsing + NotUsing + + + + + + + + + + + + + + + + Designer + + + + + + + + + + + \ No newline at end of file diff --git a/Demos/xlgamebox/XLGameBox.vcxproj.filters b/Demos/xlgamebox/XLGameBox.vcxproj.filters new file mode 100644 index 00000000..30dfcc34 --- /dev/null +++ b/Demos/xlgamebox/XLGameBox.vcxproj.filters @@ -0,0 +1,88 @@ + + + + + {c75b9d7c-452a-4867-a585-e8aee4a05f51} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {249f81db-516d-4a60-9882-d3fd674e412c} + h;hpp;hxx;hm;inl + + + {057557f5-517e-47cf-a695-8b482beaf54a} + + + {effdb570-d6be-45fe-9ac5-7dedf6a14a93} + + + + + Source Files + + + Source Files + + + MyControl + + + MyControl + + + MyControl + + + MyControl + + + MyControl + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + MyControl + + + MyControl + + + MyControl + + + MyControl + + + MyControl + + + Header Files + + + + + Resources + + + Resources + + + + + + Resources + + + \ No newline at end of file diff --git a/Demos/xlgamebox/XLGameBox.vcxproj.user b/Demos/xlgamebox/XLGameBox.vcxproj.user new file mode 100644 index 00000000..ace9a86a --- /dev/null +++ b/Demos/xlgamebox/XLGameBox.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Demos/xlgamebox/XUnzip.cpp b/Demos/xlgamebox/XUnzip.cpp new file mode 100644 index 00000000..2f7b40a8 --- /dev/null +++ b/Demos/xlgamebox/XUnzip.cpp @@ -0,0 +1,4445 @@ +// XUnzip.cpp Version 1.3 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich@gmail.com +// +// Version 1.3: - Corrected size bug introduced by 1.2 +// +// Version 1.2: - Many bug fixes. See CodeProject article for list. +// +// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd() +// - Changed file names to avoid conflicts with Lucian's files +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by Info-ZIP. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + + +//#define _USE_32BIT_TIME_T //+++1.2 + + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include + +#pragma warning(disable : 4996) // disable bogus deprecation warning + +// THIS FILE is almost entirely based upon code by Jean-loup Gailly +// and Mark Adler. It has been modified by Lucian Wischik. +// The original code may be found at http://www.gzip.org/zlib/ +// The original copyright text follows. +// +// +// +// zlib.h -- interface of the 'zlib' general purpose compression library +// version 1.1.3, July 9th, 1998 +// +// Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Jean-loup Gailly Mark Adler +// jloup@gzip.org madler@alumni.caltech.edu +// +// +// The data format used by the zlib library is described by RFCs (Request for +// Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt +// (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +// +// +// The 'zlib' compression library provides in-memory compression and +// decompression functions, including integrity checks of the uncompressed +// data. This version of the library supports only one compression method +// (deflation) but other algorithms will be added later and will have the same +// stream interface. +// +// Compression can be done in a single step if the buffers are large +// enough (for example if an input file is mmap'ed), or can be done by +// repeated calls of the compression function. In the latter case, the +// application must provide more input and/or consume the output +// (providing more output space) before each call. +// +// The library also supports reading and writing files in gzip (.gz) format +// with an interface similar to that of stdio. +// +// The library does not install any signal handler. The decoder checks +// the consistency of the compressed data, so the library should never +// crash even in case of corrupted input. +// +// for more info about .ZIP format, see ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip +// PkWare has also a specification at ftp://ftp.pkware.com/probdesc.zip + +#define zmalloc(len) malloc(len) + +#define zfree(p) free(p) + +/* +void *zmalloc(unsigned int len) +{ char *buf = new char[len+32]; + for (int i=0; i<16; i++) + { buf[i]=i; + buf[len+31-i]=i; + } + *((unsigned int*)buf) = len; + char c[1000]; wsprintf(c,"malloc 0x%lx - %lu",buf+16,len); + OutputDebugString(c); + return buf+16; +} + +void zfree(void *buf) +{ char c[1000]; wsprintf(c,"free 0x%lx",buf); + OutputDebugString(c); + char *p = ((char*)buf)-16; + unsigned int len = *((unsigned int*)p); + bool blown=false; + for (int i=0; i<16; i++) + { char lo = p[i]; + char hi = p[len+31-i]; + if (hi!=i || (lo!=i && i>4)) blown=true; + } + if (blown) + { OutputDebugString("BLOWN!!!"); + } + delete[] p; +} +*/ + +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened + +typedef DWORD ZRESULT; +// return codes from any of the zip functions. Listed later. + +#define ZIP_HANDLE 1 +#define ZIP_FILENAME 2 +#define ZIP_MEMORY 3 + +typedef struct +{ int index; // index of this file within the zip +char name[MAX_PATH]; // filename within the zip +DWORD attr; // attributes, as in GetFileAttributes. +FILETIME atime,ctime,mtime;// access, create, modify filetimes +long comp_size; // sizes of item, compressed and uncompressed. These +long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; + +typedef struct +{ int index; // index of this file within the zip +TCHAR name[MAX_PATH]; // filename within the zip +DWORD attr; // attributes, as in GetFileAttributes. +FILETIME atime,ctime,mtime;// access, create, modify filetimes +long comp_size; // sizes of item, compressed and uncompressed. These +long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; + +// These are the result codes: +#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, +#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. +// The following come from general system stuff (e.g. files not openable) +#define ZR_GENMASK 0x0000FF00 +#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle +#define ZR_NOFILE 0x00000200 // couldn't create/open the file +#define ZR_NOALLOC 0x00000300 // failed to allocate some resource +#define ZR_WRITE 0x00000400 // a general error writing to the file +#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip +#define ZR_MORE 0x00000600 // there's still more data to be unzipped +#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile +#define ZR_READ 0x00000800 // a general error reading the file +// The following come from mistakes on the part of the caller +#define ZR_CALLERMASK 0x00FF0000 +#define ZR_ARGS 0x00010000 // general mistake with the arguments +#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't +#define ZR_MEMSIZE 0x00030000 // the memory size is too small +#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function +#define ZR_ENDED 0x00050000 // the zip creation has already been closed +#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken +#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped +#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip +// The following come from bugs within the zip library itself +#define ZR_BUGMASK 0xFF000000 +#define ZR_NOTINITED 0x01000000 // initialisation didn't work +#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file +#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed +#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code + +#pragma warning(disable : 4702) // unreachable code + +static ZRESULT zopenerror = ZR_OK; //+++1.2 + +typedef struct tm_unz_s +{ unsigned int tm_sec; // seconds after the minute - [0,59] + unsigned int tm_min; // minutes after the hour - [0,59] + unsigned int tm_hour; // hours since midnight - [0,23] + unsigned int tm_mday; // day of the month - [1,31] + unsigned int tm_mon; // months since January - [0,11] + unsigned int tm_year; // years - [1980..2044] +} tm_unz; + + +// unz_global_info structure contain global data about the ZIPfile +typedef struct unz_global_info_s +{ unsigned long number_entry; // total number of entries in the central dir on this disk + unsigned long size_comment; // size of the global comment of the zipfile +} unz_global_info; + +// unz_file_info contain information about a file in the zipfile +typedef struct unz_file_info_s +{ unsigned long version; // version made by 2 bytes + unsigned long version_needed; // version needed to extract 2 bytes + unsigned long flag; // general purpose bit flag 2 bytes + unsigned long compression_method; // compression method 2 bytes + unsigned long dosDate; // last mod file date in Dos fmt 4 bytes + unsigned long crc; // crc-32 4 bytes + unsigned long compressed_size; // compressed size 4 bytes + unsigned long uncompressed_size; // uncompressed size 4 bytes + unsigned long size_filename; // filename length 2 bytes + unsigned long size_file_extra; // extra field length 2 bytes + unsigned long size_file_comment; // file comment length 2 bytes + unsigned long disk_num_start; // disk number start 2 bytes + unsigned long internal_fa; // internal file attributes 2 bytes + unsigned long external_fa; // external file attributes 4 bytes + tm_unz tmu_date; +} unz_file_info; + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + + + + + + + +#define ZLIB_VERSION "1.1.3" + + +// Allowed flush values; see deflate() for details +#define Z_NO_FLUSH 0 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 + + +// compression levels +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) + +// compression strategy; see deflateInit2() for details +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +// Possible values of the data_type field +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 + +// The deflate compression method (the only one supported in this version) +#define Z_DEFLATED 8 + +// for initializing zalloc, zfree, opaque +#define Z_NULL 0 + +// case sensitivity when searching for filenames +#define CASE_SENSITIVE 1 +#define CASE_INSENSITIVE 2 + + +// Return codes for the compression/decompression functions. Negative +// values are errors, positive values are used for special but normal events. +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) + + + +// Basic data types +typedef unsigned char Byte; // 8 bits +typedef unsigned int uInt; // 16 bits or more +typedef unsigned long uLong; // 32 bits or more +typedef void *voidpf; +typedef void *voidp; +typedef long z_off_t; + + + + + + + + + + + + +typedef voidpf (*alloc_func) (voidpf opaque, uInt items, uInt size); +typedef void (*free_func) (voidpf opaque, voidpf address); + +struct internal_state; + +typedef struct z_stream_s { + Byte *next_in; // next input byte + uInt avail_in; // number of bytes available at next_in + uLong total_in; // total nb of input bytes read so far + + Byte *next_out; // next output byte should be put there + uInt avail_out; // remaining free space at next_out + uLong total_out; // total nb of bytes output so far + + char *msg; // last error message, NULL if no error + struct internal_state *state; // not visible by applications + + alloc_func zalloc; // used to allocate the internal state + free_func zfree; // used to free the internal state + voidpf opaque; // private data object passed to zalloc and zfree + + int data_type; // best guess about the data type: ascii or binary + uLong adler; // adler32 value of the uncompressed data + uLong reserved; // reserved for future use +} z_stream; + +typedef z_stream *z_streamp; + + +// The application must update next_in and avail_in when avail_in has +// dropped to zero. It must update next_out and avail_out when avail_out +// has dropped to zero. The application must initialize zalloc, zfree and +// opaque before calling the init function. All other fields are set by the +// compression library and must not be updated by the application. +// +// The opaque value provided by the application will be passed as the first +// parameter for calls of zalloc and zfree. This can be useful for custom +// memory management. The compression library attaches no meaning to the +// opaque value. +// +// zalloc must return Z_NULL if there is not enough memory for the object. +// If zlib is used in a multi-threaded application, zalloc and zfree must be +// thread safe. +// +// The fields total_in and total_out can be used for statistics or +// progress reports. After compression, total_in holds the total size of +// the uncompressed data and may be saved for use in the decompressor +// (particularly if the decompressor wants to decompress everything in +// a single step). +// + + +// basic functions + +const char *zlibVersion (); +// The application can compare zlibVersion and ZLIB_VERSION for consistency. +// If the first character differs, the library code actually used is +// not compatible with the zlib.h header file used by the application. +// This check is automatically made by inflateInit. + + + + + + +int inflate (z_streamp strm, int flush); +// +// inflate decompresses as much data as possible, and stops when the input +// buffer becomes empty or the output buffer becomes full. It may some +// introduce some output latency (reading input without producing any output) +// except when forced to flush. +// +// The detailed semantics are as follows. inflate performs one or both of the +// following actions: +// +// - Decompress more input starting at next_in and update next_in and avail_in +// accordingly. If not all input can be processed (because there is not +// enough room in the output buffer), next_in is updated and processing +// will resume at this point for the next call of inflate(). +// +// - Provide more output starting at next_out and update next_out and avail_out +// accordingly. inflate() provides as much output as possible, until there +// is no more input data or no more space in the output buffer (see below +// about the flush parameter). +// +// Before the call of inflate(), the application should ensure that at least +// one of the actions is possible, by providing more input and/or consuming +// more output, and updating the next_* and avail_* values accordingly. +// The application can consume the uncompressed output when it wants, for +// example when the output buffer is full (avail_out == 0), or after each +// call of inflate(). If inflate returns Z_OK and with zero avail_out, it +// must be called again after making room in the output buffer because there +// might be more output pending. +// +// If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much +// output as possible to the output buffer. The flushing behavior of inflate is +// not specified for values of the flush parameter other than Z_SYNC_FLUSH +// and Z_FINISH, but the current implementation actually flushes as much output +// as possible anyway. +// +// inflate() should normally be called until it returns Z_STREAM_END or an +// error. However if all decompression is to be performed in a single step +// (a single call of inflate), the parameter flush should be set to +// Z_FINISH. In this case all pending input is processed and all pending +// output is flushed; avail_out must be large enough to hold all the +// uncompressed data. (The size of the uncompressed data may have been saved +// by the compressor for this purpose.) The next operation on this stream must +// be inflateEnd to deallocate the decompression state. The use of Z_FINISH +// is never required, but can be used to inform inflate that a faster routine +// may be used for the single inflate() call. +// +// If a preset dictionary is needed at this point (see inflateSetDictionary +// below), inflate sets strm-adler to the adler32 checksum of the +// dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise +// it sets strm->adler to the adler32 checksum of all output produced +// so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or +// an error code as described below. At the end of the stream, inflate() +// checks that its computed adler32 checksum is equal to that saved by the +// compressor and returns Z_STREAM_END only if the checksum is correct. +// +// inflate() returns Z_OK if some progress has been made (more input processed +// or more output produced), Z_STREAM_END if the end of the compressed data has +// been reached and all uncompressed output has been produced, Z_NEED_DICT if a +// preset dictionary is needed at this point, Z_DATA_ERROR if the input data was +// corrupted (input stream not conforming to the zlib format or incorrect +// adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent +// (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not +// enough memory, Z_BUF_ERROR if no progress is possible or if there was not +// enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR +// case, the application may then call inflateSync to look for a good +// compression block. +// + + +int inflateEnd (z_streamp strm); +// +// All dynamically allocated data structures for this stream are freed. +// This function discards any unprocessed input and does not flush any +// pending output. +// +// inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state +// was inconsistent. In the error case, msg may be set but then points to a +// static string (which must not be deallocated). + + // Advanced functions + +// The following functions are needed only in some special applications. + + + + + +int inflateSetDictionary (z_streamp strm, + const Byte *dictionary, + uInt dictLength); +// +// Initializes the decompression dictionary from the given uncompressed byte +// sequence. This function must be called immediately after a call of inflate +// if this call returned Z_NEED_DICT. The dictionary chosen by the compressor +// can be determined from the Adler32 value returned by this call of +// inflate. The compressor and decompressor must use exactly the same +// dictionary. +// +// inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a +// parameter is invalid (such as NULL dictionary) or the stream state is +// inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the +// expected one (incorrect Adler32 value). inflateSetDictionary does not +// perform any decompression: this will be done by subsequent calls of +// inflate(). + + +int inflateSync (z_streamp strm); +// +// Skips invalid compressed data until a full flush point can be found, or until all +// available input is skipped. No output is provided. +// +// inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR +// if no more input was provided, Z_DATA_ERROR if no flush point has been found, +// or Z_STREAM_ERROR if the stream structure was inconsistent. In the success +// case, the application may save the current current value of total_in which +// indicates where valid compressed data was found. In the error case, the +// application may repeatedly call inflateSync, providing more input each time, +// until success or end of the input data. + + +int inflateReset (z_streamp strm); +// This function is equivalent to inflateEnd followed by inflateInit, +// but does not free and reallocate all the internal decompression state. +// The stream will keep attributes that may have been set by inflateInit2. +// +// inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source +// stream state was inconsistent (such as zalloc or state being NULL). +// + + + +// checksum functions +// These functions are not related to compression but are exported +// anyway because they might be useful in applications using the +// compression library. + +uLong adler32 (uLong adler, const Byte *buf, uInt len); +// Update a running Adler-32 checksum with the bytes buf[0..len-1] and +// return the updated checksum. If buf is NULL, this function returns +// the required initial value for the checksum. +// An Adler-32 checksum is almost as reliable as a CRC32 but can be computed +// much faster. Usage example: +// +// uLong adler = adler32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// adler = adler32(adler, buffer, length); +// } +// if (adler != original_adler) error(); + +uLong ucrc32 (uLong crc, const Byte *buf, uInt len); +// Update a running crc with the bytes buf[0..len-1] and return the updated +// crc. If buf is NULL, this function returns the required initial value +// for the crc. Pre- and post-conditioning (one's complement) is performed +// within this function so it shouldn't be done by the application. +// Usage example: +// +// uLong crc = crc32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// crc = crc32(crc, buffer, length); +// } +// if (crc != original_crc) error(); + + + + +const char *zError (int err); +int inflateSyncPoint (z_streamp z); +const uLong *get_crc_table (void); + + + +typedef unsigned char uch; +typedef uch uchf; +typedef unsigned short ush; +typedef ush ushf; +typedef unsigned long ulg; + + + +const char * const z_errmsg[10] = { // indexed by 2-zlib_error +"need dictionary", // Z_NEED_DICT 2 +"stream end", // Z_STREAM_END 1 +"", // Z_OK 0 +"file error", // Z_ERRNO (-1) +"stream error", // Z_STREAM_ERROR (-2) +"data error", // Z_DATA_ERROR (-3) +"insufficient memory", // Z_MEM_ERROR (-4) +"buffer error", // Z_BUF_ERROR (-5) +"incompatible version",// Z_VERSION_ERROR (-6) +""}; + + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +// To be used only when the state is known to be valid + + // common constants + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + +#define PRESET_DICT 0x20 // preset dictionary flag in zlib header + + // target dependencies + +#define OS_CODE 0x0b // Window 95 & Windows NT + + + + // functions + +#define zmemzero(dest, len) memset(dest, 0, len) + +// Diagnostic functions +#undef Assert +#undef Trace +#undef Tracev +#undef Tracevv +#undef Tracec +#undef Tracecv + +#ifdef DEBUG + + int z_verbose = 0; + void z_error (char *m) {fprintf(stderr, "%s\n", m); exit(1);} + +#define Assert(cond,msg) {if(!(cond)) z_error(msg);} +#define Trace(x) {if (z_verbose>=0) fprintf x ;} +#define Tracev(x) {if (z_verbose>0) fprintf x ;} +#define Tracevv(x) {if (z_verbose>1) fprintf x ;} +#define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +#define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} + +#else + +#ifndef __noop +#if _MSC_VER < 1300 +#define __noop ((void)0) +#endif +#endif + +#define Assert(cond,msg) __noop +#define Trace(x) __noop +#define Tracev(x) __noop +#define Tracevv(x) __noop +#define Tracec(c,x) __noop +#define Tracecv(c,x) __noop + +#endif + + +typedef uLong (*check_func) (uLong check, const Byte *buf, uInt len); +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size); +void zcfree (voidpf opaque, voidpf ptr); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) + +//void ZFREE(z_streamp strm,voidpf addr) +//{ *((strm)->zfree))((strm)->opaque, addr); +//} + +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + + + + +// Huffman code lookup table entry--this entry is four bytes for machines +// that have 16-bit pointers (e.g. PC's in the small or medium model). + + +typedef struct inflate_huft_s inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; // number of extra bits or operation + Byte Bits; // number of bits in this code or subcode + } what; + uInt pad; // pad structure to a power of 2 (4 bytes for + } word; // 16-bit, 8 bytes for 32-bit int's) + uInt base; // literal, length base, distance base, or table offset +}; + +// Maximum size of dynamic tree. The maximum found in a long but non- +// exhaustive search was 1004 huft structures (850 for length/literals +// and 154 for distances, the latter actually the result of an +// exhaustive search). The actual maximum is not known, but the +// value below is more than safe. +#define MANY 1440 + +int inflate_trees_bits ( + uInt *, // 19 code lengths + uInt *, // bits tree desired/actual depth + inflate_huft * *, // bits tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_dynamic ( + uInt, // number of literal/length codes + uInt, // number of distance codes + uInt *, // that many (total) code lengths + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + inflate_huft * *, // literal/length tree result + inflate_huft * *, // distance tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_fixed ( + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + const inflate_huft * *, // literal/length tree result + const inflate_huft * *, // distance tree result + z_streamp); // for memory allocation + + + + + +struct inflate_blocks_state; +typedef struct inflate_blocks_state inflate_blocks_statef; + +inflate_blocks_statef * inflate_blocks_new ( + z_streamp z, + check_func c, // check function + uInt w); // window size + +int inflate_blocks ( + inflate_blocks_statef *, + z_streamp , + int); // initial return code + +void inflate_blocks_reset ( + inflate_blocks_statef *, + z_streamp , + uLong *); // check value on output + +int inflate_blocks_free ( + inflate_blocks_statef *, + z_streamp); + +void inflate_set_dictionary ( + inflate_blocks_statef *s, + const Byte *d, // dictionary + uInt n); // dictionary length + +int inflate_blocks_sync_point ( + inflate_blocks_statef *s); + + + + +struct inflate_codes_state; +typedef struct inflate_codes_state inflate_codes_statef; + +inflate_codes_statef *inflate_codes_new ( + uInt, uInt, + const inflate_huft *, const inflate_huft *, + z_streamp ); + +int inflate_codes ( + inflate_blocks_statef *, + z_streamp , + int); + +void inflate_codes_free ( + inflate_codes_statef *, + z_streamp ); + + + + +typedef enum { + IBM_TYPE, // get type bits (3, including end bit) + IBM_LENS, // get lengths for stored + IBM_STORED, // processing stored block + IBM_TABLE, // get table lengths + IBM_BTREE, // get bit lengths tree for a dynamic block + IBM_DTREE, // get length, distance trees for a dynamic block + IBM_CODES, // processing fixed or dynamic block + IBM_DRY, // output remaining window bytes + IBM_DONE, // finished last block, done + IBM_BAD} // got a data error--stuck here +inflate_block_mode; + +// inflate blocks semi-private state +struct inflate_blocks_state { + + // mode + inflate_block_mode mode; // current inflate_block mode + + // mode dependent information + union { + uInt left; // if STORED, bytes left to copy + struct { + uInt table; // table lengths (14 bits) + uInt index; // index into blens (or border) + uInt *blens; // bit lengths of codes + uInt bb; // bit length tree depth + inflate_huft *tb; // bit length decoding tree + } trees; // if DTREE, decoding info for trees + struct { + inflate_codes_statef + *codes; + } decode; // if CODES, current state + } sub; // submode + uInt last; // true if this block is the last block + + // mode independent information + uInt bitk; // bits in bit buffer + uLong bitb; // bit buffer + inflate_huft *hufts; // single malloc for tree space + Byte *window; // sliding window + Byte *end; // one byte after sliding window + Byte *read; // window read pointer + Byte *write; // window write pointer + check_func checkfn; // check function + uLong check; // check on output + +}; + + +// defines for inflate input/output +// update pointers and return +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=(uLong)(p-z->next_in);z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +// get bytes and bits +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +// output bytes +#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;m;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +// load local pointers +#define LOAD {LOADIN LOADOUT} + +// masks for lower bits (size given to avoid silly warnings with Visual C++) +// And'ing with mask[n] masks the lower n bits +const uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +// copy as much as possible from the sliding window to the output area +int inflate_flush (inflate_blocks_statef *, z_streamp, int); + +int inflate_fast (uInt, uInt, const inflate_huft *, const inflate_huft *, inflate_blocks_statef *, z_streamp ); + + + +const uInt fixed_bl = 9; +const uInt fixed_bd = 5; +const inflate_huft fixed_tl[] = { + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} + }; +const inflate_huft fixed_td[] = { + {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, + {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, + {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, + {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, + {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, + {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, + {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, + {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} + }; + + + + + + + +// copy as much as possible from the sliding window to the output area +int inflate_flush(inflate_blocks_statef *s,z_streamp z,int r) +{ + uInt n; + Byte *p; + Byte *q; + + // local copies of source and destination pointers + p = z->next_out; + q = s->read; + + // compute number of bytes to copy as far as end of window + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy as far as end of window + if (n!=0) // check for n!=0 to avoid waking up CodeGuard + { memcpy(p, q, n); + p += n; + q += n; + } + + // see if more to copy at beginning of window + if (q == s->end) + { + // wrap pointers + q = s->window; + if (s->write == s->end) + s->write = s->window; + + // compute bytes to copy + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy + memcpy(p, q, n); + p += n; + q += n; + } + + // update pointers + z->next_out = p; + s->read = q; + + // done + return r; +} + + + + + + +// simplify the use of the inflate_huft type with some defines +#define exop word.what.Exop +#define bits word.what.Bits + +typedef enum { // waiting for "i:"=input, "o:"=output, "x:"=nothing + START, // x: set up for LEN + LEN, // i: get length/literal/eob next + LENEXT, // i: getting length extra (have base) + DIST, // i: get distance next + DISTEXT, // i: getting distance extra + COPY, // o: copying bytes in window, waiting for space + LIT, // o: got literal, waiting for output space + WASH, // o: got eob, possibly still output waiting + END, // x: got eob and all data flushed + BADCODE} // x: got error +inflate_codes_mode; + +// inflate codes private state +struct inflate_codes_state { + + // mode + inflate_codes_mode mode; // current inflate_codes mode + + // mode dependent information + uInt len; + union { + struct { + const inflate_huft *tree; // pointer into tree + uInt need; // bits needed + } code; // if LEN or DIST, where in tree + uInt lit; // if LIT, literal + struct { + uInt get; // bits to get for extra + uInt dist; // distance back to copy from + } copy; // if EXT or COPY, where and how much + } sub; // submode + + // mode independent information + Byte lbits; // ltree bits decoded per branch + Byte dbits; // dtree bits decoder per branch + const inflate_huft *ltree; // literal/length/eob tree + const inflate_huft *dtree; // distance tree + +}; + + +inflate_codes_statef *inflate_codes_new( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +z_streamp z) +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt j; // temporary storage + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + Byte *f; // pointer to copy strings from + inflate_codes_statef *c = s->sub.decode.codes; // codes state + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input and output based on current state + for(;;) switch (c->mode) + { // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif // !SLOW + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: // i: get length/literal/eob next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) // literal + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) // length + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + if (e & 32) // end of block + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: // i: getting length extra (have base) + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: // i: get distance next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) // distance + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: // i: getting distance extra + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: // o: copying bytes in window, waiting for space + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: // o: got literal, waiting for output space + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: // o: got eob, possibly more output + if (k > 7) // return unused byte, if any + { + Assert(k < 16, "inflate_codes grabbed too many bytes"); + k -= 8; + n++; + p--; // can always return one + } + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: // x: got error + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +void inflate_codes_free(inflate_codes_statef *c,z_streamp z) +{ ZFREE(z, c); + Tracev((stderr, "inflate: codes free\n")); +} + + + +// infblock.c -- interpret and process block types to last block +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + + +// Table for deflate from PKZIP's appnote.txt. +const uInt border[] = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +// +// Notes beyond the 1.93a appnote.txt: +// +// 1. Distance pointers never point before the beginning of the output stream. +// 2. Distance pointers can point back across blocks, up to 32k away. +// 3. There is an implied maximum of 7 bits for the bit length table and +// 15 bits for the actual data. +// 4. If only one code exists, then it is encoded using one bit. (Zero +// would be more efficient, but perhaps a little confusing.) If two +// codes exist, they are coded using one bit each (0 and 1). +// 5. There is no way of sending zero distance codes--a dummy must be +// sent if there are none. (History: a pre 2.0 version of PKZIP would +// store blocks with no distance codes, but this was discovered to be +// too harsh a criterion.) Valid only for 1.93a. 2.04c does allow +// zero distance codes, which is sent as one code of zero bits in +// length. +// 6. There are up to 286 literal/length codes. Code 256 represents the +// end-of-block. Note however that the static length tree defines +// 288 codes just to fill out the Huffman codes. Codes 286 and 287 +// cannot be used though, since there is no length base or extra bits +// defined for them. Similarily, there are up to 30 distance codes. +// However, static trees define 32 codes (all 5 bits) to fill out the +// Huffman codes, but the last two had better not show up in the data. +// 7. Unzip can check dynamic Huffman blocks for complete code sets. +// The exception is that a single code would not be complete (see #4). +// 8. The five bits following the block type is really the number of +// literal codes sent minus 257. +// 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits +// (1+6+6). Therefore, to output three times the length, you output +// three codes (1+1+1), whereas to output four times the same length, +// you only need two codes (1+3). Hmm. +//10. In the tree reconstruction algorithm, Code = Code + Increment +// only if BitLength(i) is not zero. (Pretty obvious.) +//11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) +//12. Note: length code 284 can represent 227-258, but length code 285 +// really is 258. The last length deserves its own, short code +// since it gets used a lot in very redundant files. The length +// 258 is special since 258 - 3 (the min match length) is 255. +//13. The literal/length and distance code bit lengths are read as a +// single stream of lengths. It is possible (and advantageous) for +// a repeat code (16, 17, or 18) to go across the boundary between +// the two sets of lengths. + + +void inflate_blocks_reset(inflate_blocks_statef *s, z_streamp z, uLong *c) +{ + if (c != Z_NULL) + *c = s->check; + if (s->mode == IBM_BTREE || s->mode == IBM_DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == IBM_CODES) + inflate_codes_free(s->sub.decode.codes, z); + s->mode = IBM_TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, (const Byte *)Z_NULL, 0); + Tracev((stderr, "inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z_streamp z, check_func c, uInt w) +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->hufts = + (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + if ((s->window = (Byte *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s->hufts); + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = IBM_TYPE; + Tracev((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, Z_NULL); + return s; +} + + +int inflate_blocks(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt t; // temporary storage + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input based on current state + for(;;) switch (s->mode) + { + case IBM_TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: // stored + Tracev((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; // go to byte boundary + DUMPBITS(t) + s->mode = IBM_LENS; // get length of stored block + break; + case 1: // fixed + Tracev((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + const inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td, z); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + } + DUMPBITS(3) + s->mode = IBM_CODES; + break; + case 2: // dynamic + Tracev((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = IBM_TABLE; + break; + case 3: // illegal + DUMPBITS(3) + s->mode = IBM_BAD; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case IBM_LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = IBM_BAD; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; // dump bits + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? IBM_STORED : (s->last ? IBM_DRY : IBM_TYPE); + break; + case IBM_STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + memcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? IBM_DRY : IBM_TYPE; + break; + case IBM_TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; + // remove this section to workaround bug in pkzip + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = IBM_BAD; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } + // end remove + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if ((s->sub.trees.blens = (uInt*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = IBM_BTREE; + case IBM_BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, s->hufts, z); + if (t != Z_OK) + { + ZFREE(z, s->sub.trees.blens); + r = t; + if (r == Z_DATA_ERROR) + s->mode = IBM_BAD; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = IBM_DTREE; + case IBM_DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->bits; + c = h->base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else // c == 16..18 + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + ZFREE(z, s->sub.trees.blens); + s->mode = IBM_BAD; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; // must be <= 9 for lookahead assumptions + bd = 6; // must be <= 9 for lookahead assumptions + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, + s->hufts, z); + ZFREE(z, s->sub.trees.blens); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = IBM_BAD; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + } + s->mode = IBM_CODES; + case IBM_CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = IBM_TYPE; + break; + } + s->mode = IBM_DRY; + case IBM_DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = IBM_DONE; + case IBM_DONE: + r = Z_STREAM_END; + LEAVE + case IBM_BAD: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(inflate_blocks_statef *s, z_streamp z) +{ + inflate_blocks_reset(s, z, Z_NULL); + ZFREE(z, s->window); + ZFREE(z, s->hufts); + ZFREE(z, s); + Tracev((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + + +// inftrees.c -- generate Huffman trees for efficient decoding +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + + +extern const char inflate_copyright[] = + " ";//inflate 1.1.3 Copyright 1995-1998 Mark Adler "; +// If you use the zlib library in a product, an acknowledgment is welcome +// in the documentation of your product. If for some reason you cannot +// include such an acknowledgment, I would appreciate that you keep this +// copyright string in the executable of your product. + + + +int huft_build ( + uInt *, // code lengths in bits + uInt, // number of codes + uInt, // number of "simple" codes + const uInt *, // list of base values for non-simple codes + const uInt *, // list of extra bits for non-simple codes + inflate_huft **,// result: starting table + uInt *, // maximum lookup bits (returns actual) + inflate_huft *, // space for trees + uInt *, // hufts used in space + uInt * ); // space for values + +// Tables for deflate from PKZIP's appnote.txt. +const uInt cplens[31] = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + // see note #13 above about 258 +const uInt cplext[31] = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; // 112==invalid +const uInt cpdist[30] = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +const uInt cpdext[30] = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +// +// Huffman code decoding is performed using a multi-level table lookup. +// The fastest way to decode is to simply build a lookup table whose +// size is determined by the longest code. However, the time it takes +// to build this table can also be a factor if the data being decoded +// is not very long. The most common codes are necessarily the +// shortest codes, so those codes dominate the decoding time, and hence +// the speed. The idea is you can have a shorter table that decodes the +// shorter, more probable codes, and then point to subsidiary tables for +// the longer codes. The time it costs to decode the longer codes is +// then traded against the time it takes to make longer tables. +// +// This results of this trade are in the variables lbits and dbits +// below. lbits is the number of bits the first level table for literal/ +// length codes can decode in one step, and dbits is the same thing for +// the distance codes. Subsequent tables are also less than or equal to +// those sizes. These values may be adjusted either when all of the +// codes are shorter than that, in which case the longest code length in +// bits is used, or when the shortest code is *longer* than the requested +// table size, in which case the length of the shortest code in bits is +// used. +// +// There are two different values for the two tables, since they code a +// different number of possibilities each. The literal/length table +// codes 286 possible values, or in a flat code, a little over eight +// bits. The distance table codes 30 possible values, or a little less +// than five bits, flat. The optimum values for speed end up being +// about one bit more than those, so lbits is 8+1 and dbits is 5+1. +// The optimum values may differ though from machine to machine, and +// possibly even between compilers. Your mileage may vary. +// + + +// If BMAX needs to be larger than 16, then h and x[] should be uLong. +#define BMAX 15 // maximum bit length of any code + +int huft_build( +uInt *b, // code lengths in bits (all assumed <= BMAX) +uInt n, // number of codes (assumed <= 288) +uInt s, // number of simple-valued codes (0..s-1) +const uInt *d, // list of base values for non-simple codes +const uInt *e, // list of extra bits for non-simple codes +inflate_huft * *t, // result: starting table +uInt *m, // maximum lookup bits, returns actual +inflate_huft *hp, // space for trees +uInt *hn, // hufts used in space +uInt *v) // working area: values in order of bit length +// Given a list of code lengths and a maximum table size, make a set of +// tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR +// if the given code set is incomplete (the tables are still built in this +// case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of +// lengths), or Z_MEM_ERROR if not enough memory. +{ + + uInt a; // counter for codes of length k + uInt c[BMAX+1]; // bit length count table + uInt f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + register uInt i; // counter, current code + register uInt j; // counter + register int k; // number of bits in current code + int l; // bits per table (returned in m) + uInt mask; // (1 << w) - 1, to avoid cc -O bug on HP + register uInt *p; // pointer into c[], b[], or v[] + inflate_huft *q; // points to current table + struct inflate_huft_s r; // table entry for structure assignment + inflate_huft *u[BMAX]; // table stack + register int w; // bits before this table == (l * h) + uInt x[BMAX+1]; // bit offsets, then code stack + uInt *xp; // pointer into x + int y; // number of dummy codes added + uInt z; // number of entries in current table + + + // Generate counts for each bit length + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4; p; // clear c[]--assume BMAX+1 is 16 + p = b; i = n; + do { + c[*p++]++; // assume all entries <= BMAX + } while (--i); + if (c[0] == n) // null input--all zero length codes + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + // Find minimum and maximum length, bound *m by those + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; // minimum code length + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; // maximum code length + if ((uInt)l > i) + l = i; + *m = l; + + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { // note that i == g from above + *xp++ = (j += *p++); + } + + + // Make a table of values in order of bit lengths + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; // set n to length of v + + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = v; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = (inflate_huft *)Z_NULL; // just to keep compilers happy + q = (inflate_huft *)Z_NULL; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + + // compute minimum size table less than or equal to l bits + z = g - w; + z = z > (uInt)l ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) // try a k-w bit table + { // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = c + k; + if (j < z) + while (++j < z) // try smaller tables up to z bits + { + if ((f <<= 1) <= *++xp) + break; // enough codes to use up j bits + f -= *xp; // else deduct codes from patterns + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (*hn + z > MANY) // (note: doesn't matter for fixed) + return Z_MEM_ERROR; // not enough memory + u[h] = q = hp + *hn; + *hn += z; + + // connect to last table, if there is one + if (h) + { + x[h] = i; // save pattern for backing up + r.bits = (Byte)l; // bits to dump before this table + r.exop = (Byte)j; // bits in this table + j = i >> (w - l); + r.base = (uInt)(q - u[h-1] - j); // offset to this table + u[h-1][j] = r; // connect to last table + } + else + *t = q; // first table is returned result + } + + // set up table entry in r + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; // out of values--invalid code + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); // 256 is end-of-block + r.base = *p++; // simple code is just the value + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);// non-simple--look up in lists + r.base = d[*p++ - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + + + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits( +uInt *c, // 19 code lengths +uInt *bb, // bits tree desired/actual depth +inflate_huft * *tb, // bits tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + if ((v = (uInt*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + r = huft_build(c, 19, 19, (uInt*)Z_NULL, (uInt*)Z_NULL, + tb, bb, hp, &hn, v); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +} + + +int inflate_trees_dynamic( +uInt nl, // number of literal/length codes +uInt nd, // number of distance codes +uInt *c, // that many (total) code lengths +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +inflate_huft * *tl, // literal/length tree result +inflate_huft * *td, // distance tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + // allocate work area + if ((v = (uInt*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + + // build literal/length tree + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // build distance tree + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // done + ZFREE(z, v); + return Z_OK; +} + + + + + +int inflate_trees_fixed( +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +const inflate_huft * * tl, // literal/length tree result +const inflate_huft * *td, // distance tree result +z_streamp ) // for memory allocation +{ + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +// inffast.c -- process literals and length/distance pairs fast +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + +// macros for bit input with no checking and for returning unused bytes +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} + +// Called with number of bytes left to write in window at least 258 +// (the maximum string length) and number of input bytes available +// at least ten. The ten bytes are six bytes for the longest length/ +// distance pair plus four bytes for overloading the bit buffer. + +int inflate_fast( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +inflate_blocks_statef *s, +z_streamp z) +{ + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + uInt ml; // mask for literal/length tree + uInt md; // mask for distance tree + uInt c; // bytes to copy + uInt d; // distance back to copy from + Byte *r; // copy source pointer + + // load input, output, bit values + LOAD + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do { // assume called with m >= 258 && n >= 10 + // get literal/length code + GRABBITS(20) // max bits for literal/length code + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits for length + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + // decode distance base of block to copy + GRABBITS(15); // max bits for distance code + e = (t = td + ((uInt)b & md))->exop; + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits to add to distance base + e &= 15; + GRABBITS(e) // get extra bits (up to 13) + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + // do the copy + m -= c; + if ((uInt)(q - s->window) >= d) // offset before dest + { // just copy + r = q - d; + *q++ = *r++; c--; // minimum count is three, + *q++ = *r++; c--; // so unroll loop a little + } + else // else offset after destination + { + e = d - (uInt)(q - s->window); // bytes from offset to end + r = s->end - e; // pointer to offset + if (c > e) // if source crosses, + { + c -= e; // copy to end of window + do { + *q++ = *r++; + } while (--e); + r = s->window; // copy rest from start of window + } + } + do { // copy all or what's left + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + { + t += t->base; + e = (t += ((uInt)b & inflate_mask[e]))->exop; + } + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + break; + } + if ((e & 64) == 0) + { + t += t->base; + if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + } while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + UNGRAB + UPDATE + return Z_OK; +} + + + + + + +// crc32.c -- compute the CRC-32 of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + + + + + +// Table of CRC-32's of all single-byte values (made by make_crc_table) +const uLong crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +const uLong * get_crc_table() +{ return (const uLong *)crc_table; +} + +#define CRC_DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define CRC_DO2(buf) CRC_DO1(buf); CRC_DO1(buf); +#define CRC_DO4(buf) CRC_DO2(buf); CRC_DO2(buf); +#define CRC_DO8(buf) CRC_DO4(buf); CRC_DO4(buf); + +uLong ucrc32(uLong crc, const Byte *buf, uInt len) +{ if (buf == Z_NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) {CRC_DO8(buf); len -= 8;} + if (len) do {CRC_DO1(buf);} while (--len); + return crc ^ 0xffffffffL; +} + + +// adler32.c -- compute the Adler-32 checksum of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + +#define BASE 65521L // largest prime smaller than 65536 +#define NMAX 5552 +// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + +#define AD_DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define AD_DO2(buf,i) AD_DO1(buf,i); AD_DO1(buf,i+1); +#define AD_DO4(buf,i) AD_DO2(buf,i); AD_DO2(buf,i+2); +#define AD_DO8(buf,i) AD_DO4(buf,i); AD_DO4(buf,i+4); +#define AD_DO16(buf) AD_DO8(buf,0); AD_DO8(buf,8); + +// ========================================================================= +uLong adler32(uLong adler, const Byte *buf, uInt len) +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + AD_DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} + + + +// zutil.c -- target dependent utility functions for the compression library +// Copyright (C) 1995-1998 Jean-loup Gailly. +// For conditions of distribution and use, see copyright notice in zlib.h +// @(#) $Id$ + + + + + + +const char * zlibVersion() +{ + return ZLIB_VERSION; +} + +// exported to allow conversion of error code to string for compress() and +// uncompress() +const char * zError(int err) +{ return ERR_MSG(err); +} + + + + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) items += size - size; // make compiler happy + return (voidpf)calloc(items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + zfree(ptr); + if (opaque) return; // make compiler happy +} + + + +// inflate.c -- zlib interface to inflate modules +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_blocks_state {int dummy;}; // for buggy compilers + +typedef enum { + IM_METHOD, // waiting for method byte + IM_FLAG, // waiting for flag byte + IM_DICT4, // four dictionary check bytes to go + IM_DICT3, // three dictionary check bytes to go + IM_DICT2, // two dictionary check bytes to go + IM_DICT1, // one dictionary check byte to go + IM_DICT0, // waiting for inflateSetDictionary + IM_BLOCKS, // decompressing blocks + IM_CHECK4, // four check bytes to go + IM_CHECK3, // three check bytes to go + IM_CHECK2, // two check bytes to go + IM_CHECK1, // one check byte to go + IM_DONE, // finished check, done + IM_BAD} // got an error--stay here +inflate_mode; + +// inflate private state +struct internal_state { + + // mode + inflate_mode mode; // current inflate mode + + // mode dependent information + union { + uInt method; // if IM_FLAGS, method byte + struct { + uLong was; // computed check value + uLong need; // stream check value + } check; // if CHECK, check values to compare + uInt marker; // if IM_BAD, inflateSync's marker bytes count + } sub; // submode + + // mode independent information + int nowrap; // flag for no wrapper + uInt wbits; // log2(window size) (8..15, defaults to 15) + inflate_blocks_statef + *blocks; // current inflate_blocks state + +}; + +int inflateReset(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? IM_BLOCKS : IM_METHOD; + inflate_blocks_reset(z->state->blocks, z, Z_NULL); + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int inflateEnd(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z); + ZFREE(z, z->state); + z->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z_streamp z) +{ const char *version = ZLIB_VERSION; int stream_size = sizeof(z_stream); + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != sizeof(z_stream)) return Z_VERSION_ERROR; + + int w = -15; // MAX_WBITS: 32K LZ77 window. + // Warning: reducing MAX_WBITS makes minigzip unable to extract .gz files created by gzip. + // The memory requirements for deflate are (in bytes): + // (1 << (windowBits+2)) + (1 << (memLevel+9)) + // that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + // plus a few kilobytes for small objects. For example, if you want to reduce + // the default memory requirements from 256K to 128K, compile with + // make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + // Of course this will generally degrade compression (there's no free lunch). + // + // The memory requirements for inflate are (in bytes) 1 << windowBits + // that is, 32K for windowBits=15 (default value) plus a few kilobytes + // for small objects. + + // initialize state + if (z == Z_NULL) return Z_STREAM_ERROR; + z->msg = Z_NULL; + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; + if ((z->state = (struct internal_state *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + // handle undocumented nowrap option (no zlib header or check) + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + // create inflate_blocks state + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Tracev((stderr, "inflate: allocated\n")); + + // reset state + inflateReset(z); + return Z_OK; +} + + + +#define IM_NEEDBYTE {if(z->avail_in==0)return r;r=f;} +#define IM_NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z_streamp z, int f) +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + for (;;) switch (z->state->mode) + { + case IM_METHOD: + IM_NEEDBYTE + if (((z->state->sub.method = IM_NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = IM_BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = IM_BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + z->state->mode = IM_FLAG; + case IM_FLAG: + IM_NEEDBYTE + b = IM_NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = IM_BLOCKS; + break; + } + z->state->mode = IM_DICT4; + case IM_DICT4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_DICT3; + case IM_DICT3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_DICT2; + case IM_DICT2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_DICT1; + case IM_DICT1: + IM_NEEDBYTE; r; + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = IM_DICT0; + return Z_NEED_DICT; + case IM_DICT0: + z->state->mode = IM_BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case IM_BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (r == Z_DATA_ERROR) + { + z->state->mode = IM_BAD; + z->state->sub.marker = 0; // can try inflateSync + break; + } + if (r == Z_OK) + r = f; + if (r != Z_STREAM_END) + return r; + r = f; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = IM_DONE; + break; + } + z->state->mode = IM_CHECK4; + case IM_CHECK4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_CHECK3; + case IM_CHECK3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_CHECK2; + case IM_CHECK2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_CHECK1; + case IM_CHECK1: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib check ok\n")); + z->state->mode = IM_DONE; + case IM_DONE: + return Z_STREAM_END; + case IM_BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } +} + + + +#ifdef _UNICODE + +static int GetAnsiFileName(LPCWSTR name, char * buf, int nBufSize) +{ + memset(buf, 0, nBufSize); + + int n = WideCharToMultiByte(CP_ACP, // code page + 0, // performance and mapping flags + name, // wide-character string + -1, // number of chars in string + buf, // buffer for new string + nBufSize, // size of buffer + NULL, // default for unmappable chars + NULL); // set when default char used + return n; +} + +static int GetUnicodeFileName(const char * name, LPWSTR buf, int nBufSize) +{ + memset(buf, 0, nBufSize*sizeof(TCHAR)); + + int n = MultiByteToWideChar(CP_ACP, // code page + 0, // character-type options + name, // string to map + -1, // number of bytes in string + buf, // wide-character buffer + nBufSize); // size of buffer + + return n; +} + +#endif + + +// unzip.c -- IO on .zip files using zlib +// Version 0.15 beta, Mar 19th, 1998, +// Read unzip.h for more info + + + + +#define UNZ_BUFSIZE (16384) +#define UNZ_MAXFILENAMEINZIP (256) +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = " ";//unzip 0.15 Copyright 1998 Gilles Vollant "; + +// unz_file_info_interntal contain internal info about a file in zipfile +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;// relative offset of local header 4 bytes +} unz_file_info_internal; + + +typedef struct +{ bool is_handle; // either a handle or memory + bool canseek; + // for handles: + HANDLE h; bool herr; unsigned long initial_offset; + // for memory: + void *buf; unsigned int len,pos; // if it's a memory block +} LUFILE; + + +LUFILE *lufopen(void *z,unsigned int len,DWORD flags,ZRESULT *err) +{ + if (flags!=ZIP_HANDLE && flags!=ZIP_FILENAME && flags!=ZIP_MEMORY) + { + *err=ZR_ARGS; + return NULL; + } + // + HANDLE h=0; bool canseek=false; *err=ZR_OK; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + if (flags==ZIP_HANDLE) + { + HANDLE hf = z; + + BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&h,0,FALSE,DUPLICATE_SAME_ACCESS); + + if (!res) + { + *err=ZR_NODUPH; + return NULL; + } + } + else + { + h = CreateFile((const TCHAR *)z, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) + { + *err = ZR_NOFILE; + return NULL; + } + } + DWORD type = GetFileType(h); + canseek = (type==FILE_TYPE_DISK); + } + LUFILE *lf = new LUFILE; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + lf->is_handle=true; + lf->canseek=canseek; + lf->h=h; lf->herr=false; + lf->initial_offset=0; + if (canseek) + lf->initial_offset = SetFilePointer(h,0,NULL,FILE_CURRENT); + } + else + { + lf->is_handle=false; + lf->canseek=true; + lf->buf=z; + lf->len=len; + lf->pos=0; + lf->initial_offset=0; + } + *err=ZR_OK; + return lf; +} + + +int lufclose(LUFILE *stream) +{ if (stream==NULL) return EOF; + if (stream->is_handle) CloseHandle(stream->h); + delete stream; + return 0; +} + +int luferror(LUFILE *stream) +{ if (stream->is_handle && stream->herr) return 1; + else return 0; +} + +long int luftell(LUFILE *stream) +{ if (stream->is_handle && stream->canseek) return SetFilePointer(stream->h,0,NULL,FILE_CURRENT)-stream->initial_offset; + else if (stream->is_handle) return 0; + else return stream->pos; +} + +int lufseek(LUFILE *stream, long offset, int whence) +{ if (stream->is_handle && stream->canseek) + { if (whence==SEEK_SET) SetFilePointer(stream->h,stream->initial_offset+offset,0,FILE_BEGIN); + else if (whence==SEEK_CUR) SetFilePointer(stream->h,offset,NULL,FILE_CURRENT); + else if (whence==SEEK_END) SetFilePointer(stream->h,offset,NULL,FILE_END); + else return 19; // EINVAL + return 0; + } + else if (stream->is_handle) return 29; // ESPIPE + else + { if (whence==SEEK_SET) stream->pos=offset; + else if (whence==SEEK_CUR) stream->pos+=offset; + else if (whence==SEEK_END) stream->pos=stream->len+offset; + return 0; + } +} + + +size_t lufread(void *ptr,size_t size,size_t n,LUFILE *stream) +{ unsigned int toread = (unsigned int)(size*n); + if (stream->is_handle) + { DWORD red; BOOL res = ReadFile(stream->h,ptr,toread,&red,NULL); + if (!res) stream->herr=true; + return red/size; + } + if (stream->pos+toread > stream->len) toread = stream->len-stream->pos; + memcpy(ptr, (char*)stream->buf + stream->pos, toread); DWORD red = toread; + stream->pos += red; + return red/size; +} + + + + +// file_in_zip_read_info_s contain internal information about a file in zipfile, +// when reading and decompress it +typedef struct +{ + char *read_buffer; // internal buffer for compressed data + z_stream stream; // zLib stream structure for inflate + + uLong pos_in_zipfile; // position in byte on the zipfile, for fseek + uLong stream_initialised; // flag set if stream structure is initialised + + uLong offset_local_extrafield;// offset of the local extra field + uInt size_local_extrafield;// size of the local extra field + uLong pos_local_extrafield; // position in the local extra field in read + + uLong crc32; // crc32 of all data uncompressed + uLong crc32_wait; // crc32 we must obtain after decompress all + uLong rest_read_compressed; // number of byte to be decompressed + uLong rest_read_uncompressed;//number of byte to be obtained after decomp + LUFILE* file; // io structore of the zipfile + uLong compression_method; // compression method (0==store) + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) +} file_in_zip_read_info_s; + + +// unz_s contain internal information about the zipfile +typedef struct +{ + LUFILE* file; // io structore of the zipfile + unz_global_info gi; // public global information + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) + uLong num_file; // number of the current file in the zipfile + uLong pos_in_central_dir; // pos of the current file in the central dir + uLong current_file_ok; // flag about the usability of the current file + uLong central_pos; // position of the beginning of the central dir + + uLong size_central_dir; // size of the central directory + uLong offset_central_dir; // offset of start of central directory with respect to the starting disk number + + unz_file_info cur_file_info; // public info about the current file in zip + unz_file_info_internal cur_file_info_internal; // private info about it + file_in_zip_read_info_s* pfile_in_zip_read; // structure about the current file if we are decompressing it +} unz_s, *unzFile; + + +int unzStringFileNameCompare (const char* fileName1,const char* fileName2,int iCaseSensitivity); +// Compare two filename (fileName1,fileName2). + +z_off_t unztell (unzFile file); +// Give the current position in uncompressed data + +int unzeof (unzFile file); +// return 1 if the end of file was reached, 0 elsewhere + +int unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len); +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// +// if buf==NULL, it return the size of the local extra field +// +// if buf!=NULL, len is the size of the buffer, the extra header is copied in +// buf. +// the return value is the number of bytes copied in buf, or (if <0) +// the error code + + + +// =========================================================================== +// Read a byte from a gz_stream; update next_in and avail_in. Return EOF +// for end of file. +// IN assertion: the stream s has been sucessfully opened for reading. + +int unzlocal_getByte(LUFILE *fin,int *pi) +{ unsigned char c; + int err = (int)lufread(&c, 1, 1, fin); + if (err==1) + { *pi = (int)c; + return UNZ_OK; + } + else + { if (luferror(fin)) return UNZ_ERRNO; + else return UNZ_EOF; + } +} + + +// =========================================================================== +// Reads a long in LSB order from the given gz_stream. Sets +int unzlocal_getShort (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +int unzlocal_getLong (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +// My own strcmpi / strcasecmp +int strcmpcasenosensitive_internal (const char* fileName1,const char *fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= (char)0x20; + if ((c2>='a') && (c2<='z')) + c2 -= (char)0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + + + +// +// Compare two filename (fileName1,fileName2). +// If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) +// If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) +// +int unzStringFileNameCompare (const char*fileName1,const char*fileName2,int iCaseSensitivity) +{ if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); + else return strcmpcasenosensitive_internal(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + + +// Locate the Central directory of a zipfile (at the end, just before +// the global comment) +uLong unzlocal_SearchCentralDir(LUFILE *fin) +{ if (lufseek(fin,0,SEEK_END) != 0) return 0; + uLong uSizeFile = luftell(fin); + + uLong uMaxBack=0xffff; // maximum size of global comment + if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; + + unsigned char *buf = (unsigned char*)zmalloc(BUFREADCOMMENT+4); + if (buf==NULL) return 0; + uLong uPosFound=0; + + uLong uBackRead = 4; + while (uBackReaduMaxBack) uBackRead = uMaxBack; + else uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (lufseek(fin,uReadPos,SEEK_SET)!=0) break; + if (lufread(buf,(uInt)uReadSize,1,fin)!=1) break; + for (i=(int)uReadSize-3; (i--)>0;) + { if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { uPosFound = uReadPos+i; break; + } + } + if (uPosFound!=0) break; + } + if (buf) zfree(buf); + return uPosFound; +} + + +int unzGoToFirstFile (unzFile file); +int unzCloseCurrentFile (unzFile file); + +// Open a Zip file. +// If the zipfile cannot be opened (file don't exist or in not valid), return NULL. +// Otherwise, the return value is a unzFile Handle, usable with other unzip functions +unzFile unzOpenInternal(LUFILE *fin) +{ + zopenerror = ZR_OK; //+++1.2 + if (fin==NULL) { zopenerror = ZR_ARGS; return NULL; } //+++1.2 + if (unz_copyright[0]!=' ') {lufclose(fin); zopenerror = ZR_CORRUPT; return NULL; } //+++1.2 + + int err=UNZ_OK; + unz_s us; + uLong central_pos,uL; + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) err=UNZ_ERRNO; + if (lufseek(fin,central_pos,SEEK_SET)!=0) err=UNZ_ERRNO; + // the signature, already checked + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) err=UNZ_ERRNO; + // number of this disk + uLong number_disk; // number of the current dist, used for spanning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; + // number of the disk with the start of the central directory + uLong number_disk_with_CD; // number the the disk with central dir, used for spaning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir on this disk + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir + uLong number_entry_CD; // total number of entries in the central dir (same than number_entry on nospan) + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; + if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; + // size of the central directory + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // offset of start of central directory with respect to the starting disk number + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // zipfile comment length + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; + if ((central_pos+fin->initial_offsetinitial_offset - (us.offset_central_dir+us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + fin->initial_offset = 0; // since the zipfile itself is expected to handle this + + unz_s *s = (unz_s*)zmalloc(sizeof(unz_s)); + *s=us; + unzGoToFirstFile((unzFile)s); + return (unzFile)s; +} + + + +// Close a ZipFile opened with unzipOpen. +// If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), +// these files MUST be closed with unzipCloseCurrentFile before call unzipClose. +// return UNZ_OK if there is no problem. +int unzClose (unzFile file) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + lufclose(s->file); + if (s) zfree(s); // unused s=0; + return UNZ_OK; +} + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +// Translate date/time from Dos format to tm_unz (readable more easilty) +void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +// Get Info about the current file in the zipfile, with internal only info +int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize); + +int unzlocal_GetCurrentFileInfoInternal (unzFile file, unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, char *szFileName, + uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (lufseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + // we check the magic + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (lufread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (lufread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + {} // unused lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (lufread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + //unused lSeek+=file_info.size_file_comment - uSizeRead; + } + else {} //unused lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, + char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, szComment,commentBufferSize); +} + + +// Set the current file of the zipfile to the first file. +// return UNZ_OK if there is no problem +int unzGoToFirstFile (unzFile file) +{ + int err; + unz_s* s; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Set the current file of the zipfile to the next file. +// return UNZ_OK if there is no problem +// return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +int unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Try locate the file szFileName in the zipfile. +// For the iCaseSensitivity signification, see unzStringFileNameCompare +// return value : +// UNZ_OK if the file is found. It becomes the current file. +// UNZ_END_OF_LIST_OF_FILE if the file is not found +int unzLocateFile (unzFile file, const TCHAR *szFileName, int iCaseSensitivity) +{ + unz_s* s; + int err; + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + if (file==NULL) + return UNZ_PARAMERROR; + + if (_tcslen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + char szFileNameA[MAX_PATH]; + +#ifdef _UNICODE + GetAnsiFileName(szFileName, szFileNameA, MAX_PATH-1); +#else + strcpy(szFileNameA, szFileName); +#endif + + // support Windows subdirectory by:daviyang35 + int iLen=strlen(szFileNameA); + for (int i=0;icurrent_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName,szFileNameA,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +// Read the local header of the current zipfile +// Check the coherency of the local header and info in the end of central +// directory about this file +// store in *piSizeVar the size of extra info in local header +// (filename and size of extra field data) +int unzlocal_CheckCurrentFileCoherencyHeader (unz_s *s,uInt *piSizeVar, + uLong *poffset_local_extrafield, uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (lufseek(s->file,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +// else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) +// err=UNZ_BADZIPFILE; + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // date/time + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // crc + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size compr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size uncompr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + + + + + +// Open for reading data the current file in the zipfile. +// If there is no error and the file is opened, the return value is UNZ_OK. +int unzOpenCurrentFile (unzFile file) +{ + int err; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; // offset of the local extra field + uInt size_local_extrafield; // size of the local extra field + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*)zmalloc(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)zmalloc(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); //unused pfile_in_zip_read_info=0; + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) + { // unused err=UNZ_BADZIPFILE; + } + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + // windowBits is passed < 0 to tell that there is no zlib header. + // Note that in this case inflate *requires* an extra "dummy" byte + // after the compressed stream in order to complete decompression and + // return Z_STREAM_END. + // In unzip, i don't wait absolutely Z_STREAM_END because I known the + // size of both compressed and uncompressed data + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +// Read bytes from the current file. +// buf contain buffer where data must be copied +// len the size of buf. +// return the number of byte copied if somes bytes are copied +// return 0 if the end of file was reached +// return <0 with error code if there is an error +// (UNZ_ERRNO for IO error, or zLib error for uncompress error) +int unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ int err=UNZ_OK; + uInt iRead = 0; + + unz_s *s = (unz_s*)file; + if (s==NULL) return UNZ_PARAMERROR; + + file_in_zip_read_info_s* pfile_in_zip_read_info = s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; + if ((pfile_in_zip_read_info->read_buffer == NULL)) return UNZ_END_OF_LIST_OF_FILE; + if (len==0) return 0; + + pfile_in_zip_read_info->stream.next_out = (Byte*)buf; + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + { pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + } + + while (pfile_in_zip_read_info->stream.avail_out>0) + { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) + { uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) return UNZ_EOF; + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) return UNZ_ERRNO; + if (lufread(pfile_in_zip_read_info->read_buffer,uReadThis,1,pfile_in_zip_read_info->file)!=1) return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + pfile_in_zip_read_info->stream.next_in = (Byte*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) + { uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + } + else + { uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + } + for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); + } + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,pfile_in_zip_read_info->stream.next_out,uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { uLong uTotalOutBefore,uTotalOutAfter; + const Byte *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + err=inflate(&pfile_in_zip_read_info->stream,flush); + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,bufBefore,(uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; //+++1.3 + //if (err==Z_STREAM_END) return (iRead==len) ? UNZ_EOF : iRead; //+++1.2 + + if (err != Z_OK) break; + } + } + + if (err==Z_OK) return iRead; + + return iRead; +} + + +// Give the current position in uncompressed data +z_off_t unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +// return 1 if the end of file was reached, 0 elsewhere +int unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// if buf==NULL, it return the size of the local extra field that can be read +// if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. +// the return value is the number of bytes copied in buf, or (if <0) the error code +int unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (lufread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +// Close the file in zip opened with unzipOpenCurrentFile +// Return UNZ_CRCERROR if all the file was read but the CRC is not good +int unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + if (pfile_in_zip_read_info->read_buffer!=0) + { void *buf = pfile_in_zip_read_info->read_buffer; + zfree(buf); + pfile_in_zip_read_info->read_buffer=0; + } + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); // unused pfile_in_zip_read_info=0; + + s->pfile_in_zip_read=NULL; + + return err; +} + + +// Get the global comment string of the ZipFile, in the szComment buffer. +// uSizeBuf is the size of the szComment buffer. +// return the number of byte copied or an error code <0 +int unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) +{ //int err=UNZ_OK; + unz_s* s; + uLong uReadThis ; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; + if (lufseek(s->file,s->central_pos+22,SEEK_SET)!=0) return UNZ_ERRNO; + if (uReadThis>0) + { *szComment='\0'; + if (lufread(szComment,(uInt)uReadThis,1,s->file)!=1) return UNZ_ERRNO; + } + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + + + + + +int unzOpenCurrentFile (unzFile file); +int unzReadCurrentFile (unzFile file, void *buf, unsigned len); +int unzCloseCurrentFile (unzFile file); + + +FILETIME timet2filetime(time_t timer) +{ + struct tm *tm = gmtime(&timer); + SYSTEMTIME st; + st.wYear = (WORD)(tm->tm_year+1900); + st.wMonth = (WORD)(tm->tm_mon+1); + st.wDay = (WORD)(tm->tm_mday); + st.wHour = (WORD)(tm->tm_hour); + st.wMinute = (WORD)(tm->tm_min); + st.wSecond = (WORD)(tm->tm_sec); + st.wMilliseconds=0; + FILETIME ft; + SystemTimeToFileTime(&st,&ft); + return ft; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class TUnzip +{ public: + TUnzip() : uf(0), currentfile(-1), czei(-1) {} + + unzFile uf; int currentfile; ZIPENTRY cze; int czei; + TCHAR rootdir[MAX_PATH]; + + ZRESULT Open(void *z,unsigned int len,DWORD flags); + ZRESULT Get(int index,ZIPENTRY *ze); + ZRESULT Find(const TCHAR *name,bool ic,int *index,ZIPENTRY *ze); + ZRESULT Unzip(int index,void *dst,unsigned int len,DWORD flags); + ZRESULT Close(); +}; + + +ZRESULT TUnzip::Open(void *z,unsigned int len,DWORD flags) +{ + if (uf!=0 || currentfile!=-1) + return ZR_NOTINITED; + GetCurrentDirectory(MAX_PATH,rootdir); + _tcscat(rootdir,_T("\\")); + if (flags==ZIP_HANDLE) + { + DWORD type = GetFileType(z); + if (type!=FILE_TYPE_DISK) + return ZR_SEEK; + } + ZRESULT e; + LUFILE *f = lufopen(z,len,flags,&e); + if (f==NULL) + return e; + uf = unzOpenInternal(f); + //return ZR_OK; + return zopenerror; //+++1.2 +} + +ZRESULT TUnzip::Get(int index,ZIPENTRY *ze) +{ if (index<-1 || index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index==czei && index!=-1) {memcpy(ze,&cze,sizeof(ZIPENTRY)); return ZR_OK;} + if (index==-1) + { ze->index = uf->gi.number_entry; + ze->name[0]=0; + ze->attr=0; + ze->atime.dwLowDateTime=0; ze->atime.dwHighDateTime=0; + ze->ctime.dwLowDateTime=0; ze->ctime.dwHighDateTime=0; + ze->mtime.dwLowDateTime=0; ze->mtime.dwHighDateTime=0; + ze->comp_size=0; + ze->unc_size=0; + return ZR_OK; + } + if (index<(int)uf->num_file) unzGoToFirstFile(uf); + while ((int)uf->num_filefile,offset,SEEK_SET)!=0) return ZR_READ; + char *extra = new char[extralen]; + if (lufread(extra,1,(uInt)extralen,uf->file)!=extralen) {delete[] extra; return ZR_READ;} + // + ze->index=uf->num_file; + strcpy(ze->name,fn); + // zip has an 'attribute' 32bit value. Its lower half is windows stuff + // its upper half is standard unix attr. + unsigned long a = ufi.external_fa; + bool uisdir = (a&0x40000000)!=0; + //bool uwriteable= (a&0x08000000)!=0; + bool uwriteable= (a&0x00800000)!=0; // ***hd*** + //bool ureadable= (a&0x01000000)!=0; + //bool uexecutable=(a&0x00400000)!=0; + bool wreadonly= (a&0x00000001)!=0; + bool whidden= (a&0x00000002)!=0; + bool wsystem= (a&0x00000004)!=0; + bool wisdir= (a&0x00000010)!=0; + bool warchive= (a&0x00000020)!=0; + ze->attr=FILE_ATTRIBUTE_NORMAL; + if (uisdir || wisdir) ze->attr |= FILE_ATTRIBUTE_DIRECTORY; + if (warchive) ze->attr|=FILE_ATTRIBUTE_ARCHIVE; + if (whidden) ze->attr|=FILE_ATTRIBUTE_HIDDEN; + if (!uwriteable||wreadonly) ze->attr|=FILE_ATTRIBUTE_READONLY; + if (wsystem) ze->attr|=FILE_ATTRIBUTE_SYSTEM; + ze->comp_size = ufi.compressed_size; + ze->unc_size = ufi.uncompressed_size; + // + WORD dostime = (WORD)(ufi.dosDate&0xFFFF); + WORD dosdate = (WORD)((ufi.dosDate>>16)&0xFFFF); + FILETIME ft; + DosDateTimeToFileTime(dosdate,dostime,&ft); + ze->atime=ft; ze->ctime=ft; ze->mtime=ft; + // the zip will always have at least that dostime. But if it also has + // an extra header, then we'll instead get the info from that. + unsigned int epos=0; + while (epos+4mtime = timet2filetime(mtime); + } + if (hasatime) + { time_t atime = *(__time32_t*)(extra+epos); epos+=4; + ze->atime = timet2filetime(atime); + } + if (hasctime) + { time_t ctime = *(__time32_t*)(extra+epos); + ze->ctime = timet2filetime(ctime); + } + break; + } + // + if (extra!=0) delete[] extra; + memcpy(&cze,ze,sizeof(ZIPENTRY)); czei=index; + return ZR_OK; +} + +ZRESULT TUnzip::Find(const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + int res = unzLocateFile(uf,name,ic?CASE_INSENSITIVE:CASE_SENSITIVE); + if (res!=UNZ_OK) + { + if (index!=0) + *index=-1; + if (ze!=NULL) + { + ZeroMemory(ze,sizeof(ZIPENTRY)); ze->index=-1; + } + return ZR_NOTFOUND; + } + if (currentfile!=-1) + unzCloseCurrentFile(uf); currentfile=-1; + int i = (int)uf->num_file; + if (index!=NULL) + *index=i; + if (ze!=NULL) + { + ZRESULT zres = Get(i,ze); + if (zres!=ZR_OK) + return zres; + } + return ZR_OK; +} + +void EnsureDirectory(const TCHAR *rootdir, const TCHAR *dir) +{ + if (dir==NULL || dir[0] == _T('\0')) + return; + const TCHAR *lastslash = dir, *c = lastslash; + while (*c != _T('\0')) + { + if (*c==_T('/') || *c==_T('\\')) + lastslash=c; + c++; + } + const TCHAR *name=lastslash; + if (lastslash!=dir) + { + TCHAR tmp[MAX_PATH]; + _tcsncpy(tmp, dir, lastslash-dir); + tmp[lastslash-dir] = _T('\0'); + EnsureDirectory(rootdir,tmp); + name++; + } + TCHAR cd[MAX_PATH]; + _tcscpy(cd,rootdir); + //_tcscat(cd,name); + _tcscat(cd,dir); //+++1.2 + CreateDirectory(cd,NULL); +} + +ZRESULT TUnzip::Unzip(int index,void *dst,unsigned int len,DWORD flags) +{ + if (flags!=ZIP_MEMORY && flags!=ZIP_FILENAME && flags!=ZIP_HANDLE) + return ZR_ARGS; + if (flags==ZIP_MEMORY) + { + if (index!=currentfile) + { + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (index<(int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_file0) + return ZR_MORE; + unzCloseCurrentFile(uf); + currentfile=-1; + if (res==0) + return ZR_OK; + else + return ZR_FLATE; + } + + // otherwise we're writing to a handle or a file + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index >= (int)uf->gi.number_entry) + return ZR_ARGS; + if (index < (int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_filelen) n=len-1; + strncpy(buf,msg,n); buf[n]=0; + return mlen; +} + + +typedef struct +{ DWORD flag; + TUnzip *unz; +} TUnzipHandleData; + +HZIP OpenZipU(void *z,unsigned int len,DWORD flags) +{ + TUnzip *unz = new TUnzip(); + lasterrorU = unz->Open(z,len,flags); + if (lasterrorU!=ZR_OK) + { + delete unz; + return 0; + } + TUnzipHandleData *han = new TUnzipHandleData; + han->flag=1; + han->unz=unz; + return (HZIP)han; +} + +ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Get(index,ze); + return lasterrorU; +} + +ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Get(index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + return lasterrorU; +} + +ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Find(name,ic,index,ze); + return lasterrorU; +} + +ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Find(name,ic,index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + + return lasterrorU; +} + +ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Unzip(index,dst,len,flags); + return lasterrorU; +} + +ZRESULT CloseZipU(HZIP hz) +{ if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} + TUnzip *unz = han->unz; + lasterrorU = unz->Close(); + delete unz; + delete han; + return lasterrorU; +} + +bool IsZipHandleU(HZIP hz) +{ if (hz==0) return true; + TUnzipHandleData *han = (TUnzipHandleData*)hz; + return (han->flag==1); +} + + diff --git a/Demos/xlgamebox/resource.h b/Demos/xlgamebox/resource.h new file mode 100644 index 00000000..02b2d8ff Binary files /dev/null and b/Demos/xlgamebox/resource.h differ diff --git a/Demos/xlgamebox/stb_image.c b/Demos/xlgamebox/stb_image.c new file mode 100644 index 00000000..ebb11195 --- /dev/null +++ b/Demos/xlgamebox/stb_image.c @@ -0,0 +1,4677 @@ +#define STBI_NO_STDIO +#define STBI_NO_WRITE +#define STBI_NO_HDR + +/* stbi-1.33 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c + when you control the images you're loading + no warranty implied; use at your own risk + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline (no JPEG progressive) + PNG 8-bit only + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - overridable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) + + Latest revisions: + 1.33 (2011-07-14) minor fixes suggested by Dave Moore + 1.32 (2011-07-13) info support for all filetypes (SpartanJ) + 1.31 (2011-06-19) a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) added ability to load files via io callbacks (Ben Wenger) + 1.29 (2010-08-16) various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) cast-to-uint8 to fix warnings (Laurent Gomila) + allow trailing 0s at end of image data (Laurent Gomila) + 1.26 (2010-07-24) fix bug in file buffering for PNG reported by SpartanJ + + See end of file for full revision history. + + TODO: + stbi_info support for BMP,PSD,HDR,PIC + + + ============================ Contributors ========================= + + Image formats Optimizations & bugfixes + Sean Barrett (jpeg, png, bmp) Fabian "ryg" Giesen + Nicolas Schulz (hdr, psd) + Jonathan Dummer (tga) Bug fixes & warning fixes + Jean-Marc Lienher (gif) Marc LeBlanc + Tom Seddon (pic) Christpher Lloyd + Thatcher Ulrich (psd) Dave Moore + Won Chun + the Horde3D community + Extensions, features Janez Zemva + Jetro Lauha (stbi_info) Jonathan Blow + James "moose2000" Brown (iPhone PNG) Laurent Gomila + Ben "Disch" Wenger (io callbacks) Aruelien Pocheville + Martin "SpartanJ" Golini Ryamond Barbiero + David Woo + + + If your name should be here but isn't, let Sean know. + +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// To get a header file for this, either cut and paste the header, +// or create stb_image.h, #define STBI_HEADER_FILE_ONLY, and +// then include stb_image.c from it. + +//// begin header file //////////////////////////////////////////////////// +// +// Limitations: +// - no jpeg progressive support +// - non-HDR formats support 8-bit samples only (jpeg, png) +// - no delayed line count (jpeg) -- IJG doesn't support either +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to easily see if it's opaque. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB; nominally they +// would silently load as BGR, except the existing code should have just +// failed on such iPhone PNGs. But you can disable this conversion by +// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through. +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). + + +#ifndef STBI_NO_STDIO + +#if defined(_MSC_VER) && _MSC_VER >= 0x1400 +#define _CRT_SECURE_NO_WARNINGS // suppress bogus warnings about fopen() +#endif + +#include +#endif + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,unsigned n); // skip the next 'n' bytes + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +extern stbi_uc *stbi_load_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_HDR + extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif + + extern float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + extern void stbi_hdr_to_ldr_gamma(float gamma); + extern void stbi_hdr_to_ldr_scale(float scale); + + extern void stbi_ldr_to_hdr_gamma(float gamma); + extern void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_HDR + +// stbi_is_hdr is always defined +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename); +extern int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +extern const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +extern void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +extern int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +extern void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +extern void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + + +// ZLIB client - used by PNG, available for other purposes + +extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +// define faster low-level operations (typically SIMD support) +#ifdef STBI_SIMD +typedef void (*stbi_idct_8x8)(stbi_uc *out, int out_stride, short data[64], unsigned short *dequantize); +// compute an integer IDCT on "input" +// input[x] = data[x] * dequantize[x] +// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' +// CLAMP results to 0..255 +typedef void (*stbi_YCbCr_to_RGB_run)(stbi_uc *output, stbi_uc const *y, stbi_uc const *cb, stbi_uc const *cr, int count, int step); +// compute a conversion from YCbCr to RGB +// 'count' pixels +// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B +// y: Y input channel +// cb: Cb input channel; scale/biased to be 0..255 +// cr: Cr input channel; scale/biased to be 0..255 + +extern void stbi_install_idct(stbi_idct_8x8 func); +extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); +#endif // STBI_SIMD + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifndef STBI_HEADER_FILE_ONLY + +#ifndef STBI_NO_HDR +#include // ldexp +#include // strcmp, strtok +#endif + +#ifndef STBI_NO_STDIO +#include +#endif +#include +#include +#include +#include + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +// implementation: +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; +typedef unsigned int uint; + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(uint32)==4 ? 1 : -1]; + +#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE) +#define STBI_NO_WRITE +#endif + +#define STBI_NOTUSED(v) (void)sizeof(v) + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +/////////////////////////////////////////////// +// +// stbi struct and start_xxx functions + +// stbi structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + uint8 buffer_start[128]; + + uint8 *img_buffer, *img_buffer_end; + uint8 *img_buffer_original; +} stbi; + + +static void refill_buffer(stbi *s); + +// initialize a memory-decode context +static void start_mem(stbi *s, uint8 const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (uint8 *) buffer; + s->img_buffer_end = (uint8 *) buffer+len; +} + +// initialize a callback-based context +static void start_callbacks(stbi *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + refill_buffer(s); +} + +#ifndef STBI_NO_STDIO + +static int stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stdio_skip(void *user, unsigned n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi_stdio_callbacks = +{ + stdio_read, + stdio_skip, + stdio_eof, +}; + +static void start_file(stbi *s, FILE *f) +{ + start_callbacks(s, &stbi_stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi_rewind(stbi *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; +} + +static int stbi_jpeg_test(stbi *s); +static stbi_uc *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp); +static int stbi_png_test(stbi *s); +static stbi_uc *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_png_info(stbi *s, int *x, int *y, int *comp); +static int stbi_bmp_test(stbi *s); +static stbi_uc *stbi_bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_test(stbi *s); +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_info(stbi *s, int *x, int *y, int *comp); +static int stbi_psd_test(stbi *s); +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_hdr_test(stbi *s); +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_pic_test(stbi *s); +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_test(stbi *s); +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp); + + +// this is not threadsafe +static const char *failure_reason; + +const char *stbi_failure_reason(void) +{ + return failure_reason; +} + +static int e(const char *str) +{ + failure_reason = str; + return 0; +} + +// e - error +// epf - error returning pointer to float +// epuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define e(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define e(x,y) e(y) +#else + #define e(x,y) e(x) +#endif + +#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) +#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) + +void stbi_image_free(void *retval_from_stbi_load) +{ + free(retval_from_stbi_load); +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static unsigned char *stbi_load_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + if (stbi_jpeg_test(s)) return stbi_jpeg_load(s,x,y,comp,req_comp); + if (stbi_png_test(s)) return stbi_png_load(s,x,y,comp,req_comp); + if (stbi_bmp_test(s)) return stbi_bmp_load(s,x,y,comp,req_comp); + if (stbi_gif_test(s)) return stbi_gif_load(s,x,y,comp,req_comp); + if (stbi_psd_test(s)) return stbi_psd_load(s,x,y,comp,req_comp); + if (stbi_pic_test(s)) return stbi_pic_load(s,x,y,comp,req_comp); + + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) { + float *hdr = stbi_hdr_load(s, x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + // test tga last because it's a crappy test! + if (stbi_tga_test(s)) + return stbi_tga_load(s,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + unsigned char *result; + if (!f) return epuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_load_main(&s,x,y,comp,req_comp); +} +#endif //!STBI_NO_STDIO + +unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_HDR + +float *stbi_loadf_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) + return stbi_hdr_load(s,x,y,comp,req_comp); + #endif + data = stbi_load_main(s, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} + +float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + float *result; + if (!f) return epf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_HDR + +// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is +// defined, for API simplicity; if STBI_NO_HDR is defined, it always +// reports false! + +int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi s; + start_mem(&s,buffer,len); + return stbi_hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename) +{ + FILE *f = fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +extern int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi s; + start_file(&s,f); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} + +#ifndef STBI_NO_HDR +static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; +static float l2h_gamma=2.2f, l2h_scale=1.0f; + +void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } +void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } + +void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } +void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + SCAN_load=0, + SCAN_type, + SCAN_header +}; + +static void refill_buffer(stbi *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_end-1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static int get8(stbi *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int at_eof(stbi *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +stbi_inline static uint8 get8u(stbi *s) +{ + return (uint8) get8(s); +} + +static void skip(stbi *s, int n) +{ + if (s->io.read) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int getn(stbi *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int get16(stbi *s) +{ + int z = get8(s); + return (z << 8) + get8(s); +} + +static uint32 get32(stbi *s) +{ + uint32 z = get16(s); + return (z << 16) + get16(s); +} + +static int get16le(stbi *s) +{ + int z = get8(s); + return z + (get8(s) << 8); +} + +static uint32 get32le(stbi *s) +{ + uint32 z = get16le(s); + return z + (get16le(s) << 16); +} + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static uint8 compute_y(int r, int g, int b) +{ + return (uint8) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, uint x, uint y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + assert(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) malloc(req_comp * x * y); + if (good == NULL) { + free(data); + return epuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: assert(0); + } + #undef CASE + } + + free(data); + return good; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) malloc(x * y * comp * sizeof(float)); + if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + free(data); + return output; +} + +#define float2int(x) ((int) (x)) +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) malloc(x * y * comp); + if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + } + free(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) +// +// simple implementation +// - channel subsampling of at most 2 in each dimension +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - uses a lot of intermediate memory, could cache poorly +// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 +// stb_jpeg: 1.34 seconds (MSVC6, default release build) +// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) +// IJL11.dll: 1.08 seconds (compiled by intel) +// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) +// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + uint8 fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + uint16 code[256]; + uint8 values[256]; + uint8 size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} huffman; + +typedef struct +{ + #ifdef STBI_SIMD + unsigned short dequant2[4][64]; + #endif + stbi *s; + huffman huff_dc[4]; + huffman huff_ac[4]; + uint8 dequant[4][64]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + uint8 *data; + void *raw_data; + uint8 *linebuf; + } img_comp[4]; + + uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int scan_n, order[4]; + int restart_interval, todo; +} jpeg; + +static int build_huffman(huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (uint8) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (uint16) (code++); + if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (uint8) i; + } + } + } + return 1; +} + +static void grow_buffer_unsafe(jpeg *j) +{ + do { + int b = j->nomore ? 0 : get8(j->s); + if (b == 0xff) { + int c = get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int decode(jpeg *j, huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & bmask[k]) + h->delta[k]; + assert((((j->code_buffer) >> (32 - h->size[c])) & bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +stbi_inline static int extend_receive(jpeg *j, int n) +{ + unsigned int m = 1 << (n-1); + unsigned int k; + if (j->code_bits < n) grow_buffer_unsafe(j); + + #if 1 + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~bmask[n]; + k &= bmask[n]; + j->code_bits -= n; + #else + k = (j->code_buffer >> (32 - n)) & bmask[n]; + j->code_bits -= n; + j->code_buffer <<= n; + #endif + // the following test is probably a random branch that won't + // predict well. I tried to table accelerate it but failed. + // maybe it's compiling as a conditional move? + if (k < m) + return (-1 << n) + k + 1; + else + return k; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static uint8 dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int b) +{ + int diff,dc,k; + int t = decode(j, hdc); + if (t < 0) return e("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) dc; + + // decode AC components, see JPEG spec + k = 1; + do { + int r,s; + int rs = decode(j, hac); + if (rs < 0) return e("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + data[dezigzag[k++]] = (short) extend_receive(j,s); + } + } while (k < 64); + return 1; +} + +// take a -128..127 value and clamp it and convert to 0..255 +stbi_inline static uint8 clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (uint8) x; +} + +#define f2f(x) (int) (((x) * 4096 + 0.5)) +#define fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * f2f(0.5411961f); \ + t2 = p1 + p3*f2f(-1.847759065f); \ + t3 = p1 + p2*f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = fsh(p2+p3); \ + t1 = fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*f2f( 1.175875602f); \ + t0 = t0*f2f( 0.298631336f); \ + t1 = t1*f2f( 2.053119869f); \ + t2 = t2*f2f( 3.072711026f); \ + t3 = t3*f2f( 1.501321110f); \ + p1 = p5 + p1*f2f(-0.899976223f); \ + p2 = p5 + p2*f2f(-2.562915447f); \ + p3 = p3*f2f(-1.961570560f); \ + p4 = p4*f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +#ifdef STBI_SIMD +typedef unsigned short stbi_dequantize_t; +#else +typedef uint8 stbi_dequantize_t; +#endif + +// .344 seconds on 3*anemones.jpg +static void idct_block(uint8 *out, int out_stride, short data[64], stbi_dequantize_t *dequantize) +{ + int i,val[64],*v=val; + stbi_dequantize_t *dq = dequantize; + uint8 *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SIMD +static stbi_idct_8x8 stbi_idct_installed = idct_block; + +void stbi_install_idct(stbi_idct_8x8 func) +{ + stbi_idct_installed = func; +} +#endif + +#define MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static uint8 get_marker(jpeg *j) +{ + uint8 x; + if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; } + x = get8u(j->s); + if (x != 0xff) return MARKER_none; + while (x == 0xff) + x = get8u(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, reset the entropy decoder and +// the dc prediction +static void reset(jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int parse_entropy_coded_data(jpeg *z) +{ + reset(z); + if (z->scan_n == 1) { + int i,j; + #ifdef STBI_SIMD + __declspec(align(16)) + #endif + short data[64]; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } else { // interleaved! + int i,j,k,x,y; + short data[64]; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } + return 1; +} + +static int process_marker(jpeg *z, int m) +{ + int L; + switch (m) { + case MARKER_none: // no marker found + return e("expected marker","Corrupt JPEG"); + + case 0xC2: // SOF - progressive + return e("progressive jpeg","JPEG format not supported (progressive)"); + + case 0xDD: // DRI - specify restart interval + if (get16(z->s) != 4) return e("bad DRI len","Corrupt JPEG"); + z->restart_interval = get16(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = get16(z->s)-2; + while (L > 0) { + int q = get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return e("bad DQT type","Corrupt JPEG"); + if (t > 3) return e("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][dezigzag[i]] = get8u(z->s); + #ifdef STBI_SIMD + for (i=0; i < 64; ++i) + z->dequant2[t][i] = z->dequant[t][i]; + #endif + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = get16(z->s)-2; + while (L > 0) { + uint8 *v; + int sizes[16],i,m=0; + int q = get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = get8(z->s); + m += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < m; ++i) + v[i] = get8u(z->s); + L -= m; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + skip(z->s, get16(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int process_scan_header(jpeg *z) +{ + int i; + int Ls = get16(z->s); + z->scan_n = get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return e("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = get8(z->s), which; + int q = get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + get8(z->s); // should be 63, but might be 0 + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + + return 1; +} + +static int process_frame_header(jpeg *z, int scan) +{ + stbi *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG + p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = get16(s); if (s->img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = get16(s); if (s->img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires + c = get8(s); + if (c != 3 && c != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return e("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return e("bad component ID","Corrupt JPEG"); + q = get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); + z->img_comp[i].tq = get8(s); if (z->img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); + } + + if (scan != SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + free(z->img_comp[i].raw_data); + z->img_comp[i].data = NULL; + } + return e("outofmem", "Out of memory"); + } + // align blocks for installable-idct using mmx/sse + z->img_comp[i].data = (uint8*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define DNL(x) ((x) == 0xdc) +#define SOI(x) ((x) == 0xd8) +#define EOI(x) ((x) == 0xd9) +#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) +#define SOS(x) ((x) == 0xda) + +static int decode_jpeg_header(jpeg *z, int scan) +{ + int m; + z->marker = MARKER_none; // initialize cached marker to empty + m = get_marker(z); + if (!SOI(m)) return e("no SOI","Corrupt JPEG"); + if (scan == SCAN_type) return 1; + m = get_marker(z); + while (!SOF(m)) { + if (!process_marker(z,m)) return 0; + m = get_marker(z); + while (m == MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (at_eof(z->s)) return e("no SOF", "Corrupt JPEG"); + m = get_marker(z); + } + } + if (!process_frame_header(z, scan)) return 0; + return 1; +} + +static int decode_jpeg_image(jpeg *j) +{ + int m; + j->restart_interval = 0; + if (!decode_jpeg_header(j, SCAN_load)) return 0; + m = get_marker(j); + while (!EOI(m)) { + if (SOS(m)) { + if (!process_scan_header(j)) return 0; + if (!parse_entropy_coded_data(j)) return 0; + if (j->marker == MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!at_eof(j->s)) { + int x = get8(j->s); + if (x == 255) { + j->marker = get8u(j->s); + break; + } else if (x != 0) { + return 0; + } + } + // if we reach eof without hitting a marker, get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!process_marker(j, m)) return 0; + } + m = get_marker(j); + } + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1, + int w, int hs); + +#define div4(x) ((uint8) ((x) >> 2)) + +static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + uint8 *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = div4(n+input[i-1]); + out[i*2+1] = div4(n+input[i+1]); + } + out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define div16(x) ((uint8) ((x) >> 4)) + +static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = div16(3*t0 + t1 + 8); + out[i*2 ] = div16(3*t1 + t0 + 8); + } + out[w*2-1] = div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + in_far = in_far; + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) + +// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) +// VC6 without processor=Pro is generating multiple LEAs per multiply! +static void YCbCr_to_RGB_row(uint8 *out, const uint8 *y, const uint8 *pcb, const uint8 *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (uint8)r; + out[1] = (uint8)g; + out[2] = (uint8)b; + out[3] = 255; + out += step; + } +} + +#ifdef STBI_SIMD +static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; + +void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) +{ + stbi_YCbCr_installed = func; +} +#endif + + +// clean up the temporary component buffers +static void cleanup_jpeg(jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].data) { + free(j->img_comp[i].raw_data); + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].linebuf) { + free(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + uint8 *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi_resample; + +static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + // validate req_comp + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + z->s->img_n = 0; + + // load a jpeg image from whichever source + if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + uint i,j; + uint8 *output; + uint8 *coutput[4]; + + stbi_resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (uint8 *) malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; + else r->resample = resample_row_generic; + } + + // can't error after this so, this is safe + output = (uint8 *) malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + uint8 *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + uint8 *y = coutput[0]; + if (z->s->img_n == 3) { + #ifdef STBI_SIMD + stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s.img_x, n); + #else + YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s->img_x, n); + #endif + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + uint8 *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + jpeg j; + j.s = s; + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +static int stbi_jpeg_test(stbi *s) +{ + int r; + jpeg j; + j.s = s; + r = decode_jpeg_header(&j, SCAN_type); + stbi_rewind(s); + return r; +} + +static int stbi_jpeg_info_raw(jpeg *j, int *x, int *y, int *comp) +{ + if (!decode_jpeg_header(j, SCAN_header)) { + stbi_rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp) +{ + jpeg j; + j.s = s; + return stbi_jpeg_info_raw(&j, x, y, comp); +} + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define ZFAST_BITS 9 // accelerate all cases in default tables +#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + uint16 fast[1 << ZFAST_BITS]; + uint16 firstcode[16]; + int maxcode[17]; + uint16 firstsymbol[16]; + uint8 size[288]; + uint16 value[288]; +} zhuffman; + +stbi_inline static int bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int bit_reverse(int v, int bits) +{ + assert(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return bitreverse16(v) >> (16-bits); +} + +static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 255, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + assert(sizes[i] <= (1 << i)); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (uint16) code; + z->firstsymbol[i] = (uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + z->size[c] = (uint8)s; + z->value[c] = (uint16)i; + if (s <= ZFAST_BITS) { + int k = bit_reverse(next_code[s],s); + while (k < (1 << ZFAST_BITS)) { + z->fast[k] = (uint16) c; + k += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + uint8 *zbuffer, *zbuffer_end; + int num_bits; + uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + zhuffman z_length, z_distance; +} zbuf; + +stbi_inline static int zget8(zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void fill_bits(zbuf *z) +{ + do { + assert(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int zreceive(zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +stbi_inline static int zhuffman_decode(zbuf *a, zhuffman *z) +{ + int b,s,k; + if (a->num_bits < 16) fill_bits(a); + b = z->fast[a->code_buffer & ZFAST_MASK]; + if (b < 0xffff) { + s = z->size[b]; + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; + } + + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = bit_reverse(a->code_buffer, 16); + for (s=ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + assert(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +static int expand(zbuf *z, int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + if (!z->z_expandable) return e("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) realloc(z->zout_start, limit); + if (q == NULL) return e("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int length_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int length_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int dist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int parse_huffman_block(zbuf *a) +{ + for(;;) { + int z = zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes + if (a->zout >= a->zout_end) if (!expand(a, 1)) return 0; + *a->zout++ = (char) z; + } else { + uint8 *p; + int len,dist; + if (z == 256) return 1; + z -= 257; + len = length_base[z]; + if (length_extra[z]) len += zreceive(a, length_extra[z]); + z = zhuffman_decode(a, &a->z_distance); + if (z < 0) return e("bad huffman code","Corrupt PNG"); + dist = dist_base[z]; + if (dist_extra[z]) dist += zreceive(a, dist_extra[z]); + if (a->zout - a->zout_start < dist) return e("bad dist","Corrupt PNG"); + if (a->zout + len > a->zout_end) if (!expand(a, len)) return 0; + p = (uint8 *) (a->zout - dist); + while (len--) + *a->zout++ = *p++; + } + } +} + +static int compute_huffman_codes(zbuf *a) +{ + static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + zhuffman z_codelength; + uint8 lencodes[286+32+137];//padding for maximum single op + uint8 codelength_sizes[19]; + int i,n; + + int hlit = zreceive(a,5) + 257; + int hdist = zreceive(a,5) + 1; + int hclen = zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (uint8) s; + } + if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = zhuffman_decode(a, &z_codelength); + assert(c >= 0 && c < 19); + if (c < 16) + lencodes[n++] = (uint8) c; + else if (c == 16) { + c = zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + assert(c == 18); + c = zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); + if (!zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int parse_uncompressed_block(zbuf *a) +{ + uint8 header[4]; + int len,nlen,k; + if (a->num_bits & 7) + zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (uint8) (a->code_buffer & 255); // wtf this warns? + a->code_buffer >>= 8; + a->num_bits -= 8; + } + assert(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = (uint8) zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return e("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!expand(a, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int parse_zlib_header(zbuf *a) +{ + int cmf = zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = zget8(a); + if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static uint8 default_length[288], default_distance[32]; +static void init_defaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) default_length[i] = 8; + for ( ; i <= 255; ++i) default_length[i] = 9; + for ( ; i <= 279; ++i) default_length[i] = 7; + for ( ; i <= 287; ++i) default_length[i] = 8; + + for (i=0; i <= 31; ++i) default_distance[i] = 5; +} + +int stbi_png_partial; // a quick hack to only allow decoding some of a PNG... I should implement real streaming support instead +static int parse_zlib(zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = zreceive(a,1); + type = zreceive(a,2); + if (type == 0) { + if (!parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!default_distance[31]) init_defaults(); + if (!zbuild_huffman(&a->z_length , default_length , 288)) return 0; + if (!zbuild_huffman(&a->z_distance, default_distance, 32)) return 0; + } else { + if (!compute_huffman_codes(a)) return 0; + } + if (!parse_huffman_block(a)) return 0; + } + if (stbi_png_partial && a->zout - a->zout_start > 65536) + break; + } while (!final); + return 1; +} + +static int do_zlib(zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return parse_zlib(a, parse_header); +} + +char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer+len; + if (do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + + +typedef struct +{ + uint32 length; + uint32 type; +} chunk; + +#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static chunk get_chunk_header(stbi *s) +{ + chunk c; + c.length = get32(s); + c.type = get32(s); + return c; +} + +static int check_png_header(stbi *s) +{ + static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (get8u(s) != png_sig[i]) return e("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi *s; + uint8 *idata, *expanded, *out; +} png; + + +enum { + F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, + F_avg_first, F_paeth_first +}; + +static uint8 first_row_filter[5] = +{ + F_none, F_sub, F_none, F_avg_first, F_paeth_first +}; + +static int paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +// create the png data from post-deflated data +static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, uint32 x, uint32 y) +{ + stbi *s = a->s; + uint32 i,j,stride = x*out_n; + int k; + int img_n = s->img_n; // copy it into a local for later + assert(out_n == s->img_n || out_n == s->img_n+1); + if (stbi_png_partial) y = 1; + a->out = (uint8 *) malloc(x * y * out_n); + if (!a->out) return e("outofmem", "Out of memory"); + if (!stbi_png_partial) { + if (s->img_x == x && s->img_y == y) { + if (raw_len != (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } + } + for (j=0; j < y; ++j) { + uint8 *cur = a->out + stride*j; + uint8 *prior = cur - stride; + int filter = *raw++; + if (filter > 4) return e("invalid filter","Corrupt PNG"); + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + // handle first pixel explicitly + for (k=0; k < img_n; ++k) { + switch (filter) { + case F_none : cur[k] = raw[k]; break; + case F_sub : cur[k] = raw[k]; break; + case F_up : cur[k] = raw[k] + prior[k]; break; + case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; + case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break; + case F_avg_first : cur[k] = raw[k]; break; + case F_paeth_first: cur[k] = raw[k]; break; + } + } + if (img_n != out_n) cur[img_n] = 255; + raw += img_n; + cur += out_n; + prior += out_n; + // this is a little gross, so that we don't switch per-pixel or per-component + if (img_n == out_n) { + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; + } + #undef CASE + } else { + assert(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + return 1; +} + +static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n, int interlaced) +{ + uint8 *final; + int p; + int save; + if (!interlaced) + return create_png_image_raw(a, raw, raw_len, out_n, a->s->img_x, a->s->img_y); + save = stbi_png_partial; + stbi_png_partial = 0; + + // de-interlacing + final = (uint8 *) malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + if (!create_png_image_raw(a, raw, raw_len, out_n, x, y)) { + free(final); + return 0; + } + for (j=0; j < y; ++j) + for (i=0; i < x; ++i) + memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*out_n, + a->out + (j*x+i)*out_n, out_n); + free(a->out); + raw += (x*out_n+1)*y; + raw_len -= (x*out_n+1)*y; + } + } + a->out = final; + + stbi_png_partial = save; + return 1; +} + +static int compute_transparency(png *z, uint8 tc[3], int out_n) +{ + stbi *s = z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + assert(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int expand_palette(png *a, uint8 *palette, int len, int pal_img_n) +{ + uint32 i, pixel_count = a->s->img_x * a->s->img_y; + uint8 *p, *temp_out, *orig = a->out; + + p = (uint8 *) malloc(pixel_count * pal_img_n); + if (p == NULL) return e("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + free(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi_unpremultiply_on_load = 0; +static int stbi_de_iphone_flag = 0; + +void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi_unpremultiply_on_load = flag_true_if_should_unpremultiply; +} +void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi_de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi_de_iphone(png *z) +{ + stbi *s = z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + assert(s->img_out_n == 4); + if (stbi_unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + uint8 a = p[3]; + uint8 t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +static int parse_png_file(png *z, int scan, int req_comp) +{ + uint8 palette[1024], pal_img_n=0; + uint8 has_trans=0, tc[3]; + uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, iphone=0; + stbi *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!check_png_header(s)) return 0; + + if (scan == SCAN_type) return 1; + + for (;;) { + chunk c = get_chunk_header(s); + switch (c.type) { + case PNG_TYPE('C','g','B','I'): + iphone = stbi_de_iphone_flag; + skip(s, c.length); + break; + case PNG_TYPE('I','H','D','R'): { + int depth,color,comp,filter; + if (!first) return e("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); + s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); + s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); + depth = get8(s); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); + color = get8(s); if (color > 6) return e("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); + comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG"); + filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG"); + interlace = get8(s); if (interlace>1) return e("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + if (scan == SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return e("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case PNG_TYPE('P','L','T','E'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = get8u(s); + palette[i*4+1] = get8u(s); + palette[i*4+2] = get8u(s); + palette[i*4+3] = 255; + } + break; + } + + case PNG_TYPE('t','R','N','S'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (z->idata) return e("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = get8u(s); + } else { + if (!(s->img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); + if (c.length != (uint32) s->img_n*2) return e("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (uint8) get16(s); // non 8-bit images will be larger + } + break; + } + + case PNG_TYPE('I','D','A','T'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); + if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; } + if (ioff + c.length > idata_limit) { + uint8 *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); + z->idata = p; + } + if (!getn(s, z->idata+ioff,c.length)) return e("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case PNG_TYPE('I','E','N','D'): { + uint32 raw_len; + if (first) return e("first not IHDR", "Corrupt PNG"); + if (scan != SCAN_load) return 1; + if (z->idata == NULL) return e("no IDAT","Corrupt PNG"); + z->expanded = (uint8 *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !iphone); + if (z->expanded == NULL) return 0; // zlib should set error + free(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!create_png_image(z, z->expanded, raw_len, s->img_out_n, interlace)) return 0; + if (has_trans) + if (!compute_transparency(z, tc, s->img_out_n)) return 0; + if (iphone && s->img_out_n > 2) + stbi_de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!expand_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + free(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return e("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX chunk not known"; + invalid_chunk[0] = (uint8) (c.type >> 24); + invalid_chunk[1] = (uint8) (c.type >> 16); + invalid_chunk[2] = (uint8) (c.type >> 8); + invalid_chunk[3] = (uint8) (c.type >> 0); + #endif + return e(invalid_chunk, "PNG not supported: unknown chunk type"); + } + skip(s, c.length); + break; + } + // end of chunk, read and skip CRC + get32(s); + } +} + +static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + if (parse_png_file(p, SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + free(p->out); p->out = NULL; + free(p->expanded); p->expanded = NULL; + free(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + png p; + p.s = s; + return do_png(&p, x,y,comp,req_comp); +} + +static int stbi_png_test(stbi *s) +{ + int r; + r = check_png_header(s); + stbi_rewind(s); + return r; +} + +static int stbi_png_info_raw(png *p, int *x, int *y, int *comp) +{ + if (!parse_png_file(p, SCAN_header, 0)) { + stbi_rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi_png_info(stbi *s, int *x, int *y, int *comp) +{ + png p; + p.s = s; + return stbi_png_info_raw(&p, x, y, comp); +} + +// Microsoft/Windows BMP image + +static int bmp_test(stbi *s) +{ + int sz; + if (get8(s) != 'B') return 0; + if (get8(s) != 'M') return 0; + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + get32le(s); // discard data offset + sz = get32le(s); + if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; + return 0; +} + +static int stbi_bmp_test(stbi *s) +{ + int r = bmp_test(s); + stbi_rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *out; + unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (get8(s) != 'B' || get8(s) != 'M') return epuc("not BMP", "Corrupt BMP"); + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + offset = get32le(s); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = get16le(s); + s->img_y = get16le(s); + } else { + s->img_x = get32le(s); + s->img_y = get32le(s); + } + if (get16le(s) != 1) return epuc("bad BMP", "bad BMP"); + bpp = get16le(s); + if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = get32le(s); + if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); + get32le(s); // discard sizeof + get32le(s); // discard hres + get32le(s); // discard vres + get32le(s); // discard colorsused + get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + get32le(s); + get32le(s); + get32le(s); + get32le(s); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xffu << 16; + mg = 0xffu << 8; + mb = 0xffu << 0; + ma = 0xffu << 24; + fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 + } else { + mr = 31u << 10; + mg = 31u << 5; + mb = 31u << 0; + } + } else if (compress == 3) { + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return epuc("bad BMP", "bad BMP"); + } + } else + return epuc("bad BMP", "bad BMP"); + } + } else { + assert(hsz == 108); + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + ma = get32le(s); + get32le(s); // discard color space + for (i=0; i < 12; ++i) + get32le(s); // discard color space parameters + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) malloc(target * s->img_x * s->img_y); + if (!out) return epuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + if (hsz != 12) get8(s); + pal[i][3] = 255; + } + skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (s->img_x + 1) >> 1; + else if (bpp == 8) width = s->img_x; + else { free(out); return epuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=get8(s),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { free(out); return epuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = high_bit(mr)-7; rcount = bitcount(mr); + gshift = high_bit(mg)-7; gcount = bitcount(mr); + bshift = high_bit(mb)-7; bcount = bitcount(mr); + ashift = high_bit(ma)-7; acount = bitcount(mr); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + int a; + out[z+2] = get8u(s); + out[z+1] = get8u(s); + out[z+0] = get8u(s); + z += 3; + a = (easy == 2 ? get8(s) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } else { + for (i=0; i < (int) s->img_x; ++i) { + uint32 v = (bpp == 16 ? get16le(s) : get32le(s)); + int a; + out[z++] = (uint8) shiftsigned(v & mr, rshift, rcount); + out[z++] = (uint8) shiftsigned(v & mg, gshift, gcount); + out[z++] = (uint8) shiftsigned(v & mb, bshift, bcount); + a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } + skip(s, pad); + } + } + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} + +static stbi_uc *stbi_bmp_load(stbi *s,int *x, int *y, int *comp, int req_comp) +{ + return bmp_load(s, x,y,comp,req_comp); +} + + +// Targa Truevision - TGA +// by Jonathan Dummer + +static int tga_info(stbi *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp; + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if( sz > 1 ) { + stbi_rewind(s); + return 0; // only RGB or indexed allowed + } + sz = get8u(s); // image type + // only RGB or grey allowed, +/- RLE + if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; + skip(s,9); + tga_w = get16le(s); + if( tga_w < 1 ) { + stbi_rewind(s); + return 0; // test width + } + tga_h = get16le(s); + if( tga_h < 1 ) { + stbi_rewind(s); + return 0; // test height + } + sz = get8(s); // bits per pixel + // only RGB or RGBA or grey allowed + if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { + stbi_rewind(s); + return 0; + } + tga_comp = sz; + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp / 8; + return 1; // seems to have passed everything +} + +int stbi_tga_info(stbi *s, int *x, int *y, int *comp) +{ + return tga_info(s, x, y, comp); +} + +static int tga_test(stbi *s) +{ + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if ( sz > 1 ) return 0; // only RGB or indexed allowed + sz = get8u(s); // image type + if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + get16(s); // discard palette start + get16(s); // discard palette length + get8(s); // discard bits per palette color entry + get16(s); // discard x origin + get16(s); // discard y origin + if ( get16(s) < 1 ) return 0; // test width + if ( get16(s) < 1 ) return 0; // test height + sz = get8(s); // bits per pixel + if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed + return 1; // seems to have passed everything +} + +static int stbi_tga_test(stbi *s) +{ + int res = tga_test(s); + stbi_rewind(s); + return res; +} + +static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = get8u(s); + int tga_indexed = get8u(s); + int tga_image_type = get8u(s); + int tga_is_RLE = 0; + int tga_palette_start = get16le(s); + int tga_palette_len = get16le(s); + int tga_palette_bits = get8u(s); + int tga_x_origin = get16le(s); + int tga_y_origin = get16le(s); + int tga_width = get16le(s); + int tga_height = get16le(s); + int tga_bits_per_pixel = get8u(s); + int tga_inverted = get8u(s); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + unsigned char trans_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if ( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA + } + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) + { + tga_bits_per_pixel = tga_palette_bits; + } + + // tga info + *x = tga_width; + *y = tga_height; + if ( (req_comp < 1) || (req_comp > 4) ) + { + // just use whatever the file was + req_comp = tga_bits_per_pixel / 8; + *comp = req_comp; + } else + { + // force a new number of components + *comp = tga_bits_per_pixel/8; + } + tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); + if (!tga_data) return epuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + skip(s, tga_offset ); + // do I need to load a palette? + if ( tga_indexed ) + { + // any data to skip? (offset usually = 0) + skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); + if (!tga_palette) return epuc("outofmem", "Out of memory"); + if (!getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { + free(tga_data); + free(tga_palette); + return epuc("bad palette", "Corrupt TGA"); + } + } + // load the data + trans_data[0] = trans_data[1] = trans_data[2] = trans_data[3] = 0; + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE chunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = get8u(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = get8u(s); + if ( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = get8u(s); + } + } + // convert raw to the intermediate format + switch (tga_bits_per_pixel) + { + case 8: + // Luminous => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 16: + // Luminous,Alpha => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[1]; + break; + case 24: + // BGR => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 32: + // BGRA => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[3]; + break; + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + // convert to final format + switch (req_comp) + { + case 1: + // RGBA => Luminance + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + break; + case 2: + // RGBA => Luminance,Alpha + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + tga_data[i*req_comp+1] = trans_data[3]; + break; + case 3: + // RGBA => RGB + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + break; + case 4: + // RGBA => RGBA + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + tga_data[i*req_comp+3] = trans_data[3]; + break; + } + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * req_comp; + int index2 = (tga_height - 1 - j) * tga_width * req_comp; + for (i = tga_width * req_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + free( tga_palette ); + } + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} + +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return tga_load(s,x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +static int psd_test(stbi *s) +{ + if (get32(s) != 0x38425053) return 0; // "8BPS" + else return 1; +} + +static int stbi_psd_test(stbi *s) +{ + int r = psd_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + uint8 *out; + + // Check identifier + if (get32(s) != 0x38425053) // "8BPS" + return epuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (get16(s) != 1) + return epuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) + return epuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = get32(s); + w = get32(s); + + // Make sure the depth is 8 bits. + if (get16(s) != 8) + return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (get16(s) != 3) + return epuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + skip(s,get32(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + skip(s, get32(s) ); + + // Skip the reserved data. + skip(s, get32(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = get16(s); + if (compression > 1) + return epuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) malloc(4 * w*h); + if (!out) return epuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = get8u(s); + p += 4; + len--; + } + } else if (len > 128) { + uint8 val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = get8u(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + for (i = 0; i < pixelCount; i++) + *p = get8u(s), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + out = convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} + +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return psd_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +static int pic_is4(stbi *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int pic_test(stbi *s) +{ + int i; + + if (!pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + get8(s); + + if (!pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} pic_packet_t; + +static stbi_uc *pic_readval(stbi *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (at_eof(s)) return epuc("bad file","PIC file too short"); + dest[i]=get8u(s); + } + } + + return dest; +} + +static void pic_copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *pic_load2(stbi *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + pic_packet_t packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return epuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + + act_comp |= packet->channel; + + if (at_eof(s)) return epuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return epuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return epuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=get8u(s); + if (at_eof(s)) return epuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (uint8) left; + + if (!pic_readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = get8(s), i; + if (at_eof(s)) return epuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + int i; + + if (count==128) + count = get16(s); + else + count -= 127; + if (count > left) + return epuc("bad file","scanline overrun"); + + if (!pic_readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return epuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *pic_load(stbi *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + get8(s); + + x = get16(s); + y = get16(s); + if (at_eof(s)) return epuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return epuc("too large", "Image too large to decode"); + + get32(s); //skip `ratio' + get16(s); //skip `fields' + get16(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!pic_load2(s,x,y,comp, result)) { + free(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi_pic_test(stbi *s) +{ + int r = pic_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return pic_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb +typedef struct stbi_gif_lzw_struct { + int16 prefix; + uint8 first; + uint8 suffix; +} stbi_gif_lzw; + +typedef struct stbi_gif_struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags; + uint8 pal[256][4]; + uint8 lpal[256][4]; + stbi_gif_lzw codes[4096]; + uint8 *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi_gif; + +static int gif_test(stbi *s) +{ + int sz; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') return 0; + sz = get8(s); + if (sz != '9' && sz != '7') return 0; + if (get8(s) != 'a') return 0; + return 1; +} + +static int stbi_gif_test(stbi *s) +{ + int r = gif_test(s); + stbi_rewind(s); + return r; +} + +static void stbi_gif_parse_colortable(stbi *s, uint8 pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + pal[i][3] = transp ? 0 : 255; + } +} + +static int stbi_gif_header(stbi *s, stbi_gif *g, int *comp, int is_info) +{ + uint8 version; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') + return e("not GIF", "Corrupt GIF"); + + version = get8u(s); + if (version != '7' && version != '9') return e("not GIF", "Corrupt GIF"); + if (get8(s) != 'a') return e("not GIF", "Corrupt GIF"); + + failure_reason = ""; + g->w = get16le(s); + g->h = get16le(s); + g->flags = get8(s); + g->bgindex = get8(s); + g->ratio = get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi_gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi_gif_info_raw(stbi *s, int *x, int *y, int *comp) +{ + stbi_gif g; + if (!stbi_gif_header(s, &g, comp, 1)) { + stbi_rewind( s ); + return 0; + } + if (x) *x = g.w; + if (y) *y = g.h; + return 1; +} + +static void stbi_out_gif_code(stbi_gif *g, uint16 code) +{ + uint8 *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi_out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static uint8 *stbi_process_gif_raster(stbi *s, stbi_gif *g) +{ + uint8 lzw_cs; + int32 len, code; + uint32 first; + int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi_gif_lzw *p; + + lzw_cs = get8u(s); + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (code = 0; code < clear; code++) { + g->codes[code].prefix = -1; + g->codes[code].first = (uint8) code; + g->codes[code].suffix = (uint8) code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (int32) get8(s) << valid_bits; + valid_bits += 8; + } else { + int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + skip(s, len); + while ((len = get8(s)) > 0) + skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return epuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return epuc("too many codes", "Corrupt GIF"); + p->prefix = (int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return epuc("illegal code in raster", "Corrupt GIF"); + + stbi_out_gif_code(g, (uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return epuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi_fill_gif_background(stbi_gif *g) +{ + int i; + uint8 *c = g->pal[g->bgindex]; + // @OPTIMIZE: write a dword at a time + for (i = 0; i < g->w * g->h * 4; i += 4) { + uint8 *p = &g->out[i]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static uint8 *stbi_gif_load_next(stbi *s, stbi_gif *g, int *comp, int req_comp) +{ + int i; + uint8 *old_out = 0; + + if (g->out == 0) { + if (!stbi_gif_header(s, g, comp,0)) return 0; // failure_reason set by stbi_gif_header + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + stbi_fill_gif_background(g); + } else { + // animated-gif-only path + if (((g->eflags & 0x1C) >> 2) == 3) { + old_out = g->out; + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + memcpy(g->out, old_out, g->w*g->h*4); + } + } + + for (;;) { + switch (get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int32 x, y, w, h; + uint8 *o; + + x = get16le(s); + y = get16le(s); + w = get16le(s); + h = get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return epuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi_gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (uint8 *) g->lpal; + } else if (g->flags & 0x80) { + for (i=0; i < 256; ++i) // @OPTIMIZE: reset only the previous transparent + g->pal[i][3] = 255; + if (g->transparent >= 0 && (g->eflags & 0x01)) + g->pal[g->transparent][3] = 0; + g->color_table = (uint8 *) g->pal; + } else + return epuc("missing color table", "Corrupt GIF"); + + o = stbi_process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (req_comp && req_comp != 4) + o = convert_format(o, 4, req_comp, g->w, g->h); + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (get8(s) == 0xF9) { // Graphic Control Extension. + len = get8(s); + if (len == 4) { + g->eflags = get8(s); + get16le(s); // delay + g->transparent = get8(s); + } else { + skip(s, len); + break; + } + } + while ((len = get8(s)) != 0) + skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (uint8 *) 1; + + default: + return epuc("unknown code", "Corrupt GIF"); + } + } +} + +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *u = 0; + stbi_gif g={0}; + + u = stbi_gif_load_next(s, &g, comp, req_comp); + if (u == (void *) 1) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + } + + return u; +} + +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp) +{ + return stbi_gif_info_raw(s,x,y,comp); +} + + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int hdr_test(stbi *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi_hdr_test(stbi* s) +{ + int r = hdr_test(s); + stbi_rewind(s); + return r; +} + +#define HDR_BUFLEN 1024 +static char *hdr_gettoken(stbi *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) get8(z); + + while (!at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == HDR_BUFLEN-1) { + // flush to end of line + while (!at_eof(z) && get8(z) != '\n') + ; + break; + } + c = (char) get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return epf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return epf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = strtol(token, NULL, 10); + + *x = width; + *y = height; + + *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + getn(s, rgbe, 4); + hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = get8(s); + c2 = get8(s); + len = get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + uint8 rgbe[4]; + rgbe[0] = (uint8) c1; + rgbe[1] = (uint8) c2; + rgbe[2] = (uint8) len; + rgbe[3] = (uint8) get8u(s); + hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + free(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= get8(s); + if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = get8u(s); + if (count > 128) { + // Run + value = get8u(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = get8u(s); + } + } + } + for (i=0; i < width; ++i) + hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + free(scanline); + } + + return hdr_data; +} + +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return hdr_load(s,x,y,comp,req_comp); +} + +static int stbi_hdr_info(stbi *s, int *x, int *y, int *comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { + stbi_rewind( s ); + return 0; + } + + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi_rewind( s ); + return 0; + } + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *y = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *x = strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +static int stbi_bmp_info(stbi *s, int *x, int *y, int *comp) +{ + int hsz; + if (get8(s) != 'B' || get8(s) != 'M') { + stbi_rewind( s ); + return 0; + } + skip(s,12); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) { + stbi_rewind( s ); + return 0; + } + if (hsz == 12) { + *x = get16le(s); + *y = get16le(s); + } else { + *x = get32le(s); + *y = get32le(s); + } + if (get16le(s) != 1) { + stbi_rewind( s ); + return 0; + } + *comp = get16le(s) / 8; + return 1; +} + +static int stbi_psd_info(stbi *s, int *x, int *y, int *comp) +{ + int channelCount; + if (get32(s) != 0x38425053) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 1) { + stbi_rewind( s ); + return 0; + } + skip(s, 6); + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) { + stbi_rewind( s ); + return 0; + } + *y = get32(s); + *x = get32(s); + if (get16(s) != 8) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 3) { + stbi_rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi_pic_info(stbi *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + pic_packet_t packets[10]; + + skip(s, 92); + + *x = get16(s); + *y = get16(s); + if (at_eof(s)) return 0; + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi_rewind( s ); + return 0; + } + + skip(s, 8); + + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + act_comp |= packet->channel; + + if (at_eof(s)) { + stbi_rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi_rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} + +static int stbi_info_main(stbi *s, int *x, int *y, int *comp) +{ + if (stbi_jpeg_info(s, x, y, comp)) + return 1; + if (stbi_png_info(s, x, y, comp)) + return 1; + if (stbi_gif_info(s, x, y, comp)) + return 1; + if (stbi_bmp_info(s, x, y, comp)) + return 1; + if (stbi_psd_info(s, x, y, comp)) + return 1; + if (stbi_pic_info(s, x, y, comp)) + return 1; + #ifndef STBI_NO_HDR + if (stbi_hdr_info(s, x, y, comp)) + return 1; + #endif + // test tga last because it's a crappy test! + if (stbi_tga_info(s, x, y, comp)) + return 1; + return e("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = fopen(filename, "rb"); + int result; + if (!f) return e("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi s; + long pos = ftell(f); + start_file(&s, f); + r = stbi_info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_info_main(&s,x,y,comp); +} + +int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi_info_main(&s,x,y,comp); +} + +#endif // STBI_HEADER_FILE_ONLY + +/* + revision history: + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-uint8 to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.e. Janez (U+017D)emva) + 1.21 fix use of 'uint8' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 2008-08-02 + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 first released version +*/ diff --git a/DuiLib.sln b/DuiLib.sln new file mode 100644 index 00000000..84aed6c4 --- /dev/null +++ b/DuiLib.sln @@ -0,0 +1,52 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DuiLib", "DuiLib\DuiLib.vcxproj", "{E106ACD7-4E53-4AEE-942B-D0DD426DB34E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Demos", "Demos", "{D01B5755-53F2-4929-B69C-75C99D151E6F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BDWallPaper", "Demos\bdwallpaper\BDWallPaper.vcxproj", "{565089A1-60C0-4281-99D7-A459E7354EEF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "duidemo", "Demos\duidemo\duidemo.vcxproj", "{54019823-E923-44D0-AD60-8EB636D107DC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GameBox", "Demos\gamebox\GameBox.vcxproj", "{3EF9795D-3509-4664-8E73-8D315CC0B944}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qqsetup", "Demos\qqsetup\qqsetup.vcxproj", "{797901AA-9447-45C6-AF89-5D5DA54C4159}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E106ACD7-4E53-4AEE-942B-D0DD426DB34E}.Debug|Win32.ActiveCfg = Debug|Win32 + {E106ACD7-4E53-4AEE-942B-D0DD426DB34E}.Debug|Win32.Build.0 = Debug|Win32 + {E106ACD7-4E53-4AEE-942B-D0DD426DB34E}.Release|Win32.ActiveCfg = Release|Win32 + {E106ACD7-4E53-4AEE-942B-D0DD426DB34E}.Release|Win32.Build.0 = Release|Win32 + {565089A1-60C0-4281-99D7-A459E7354EEF}.Debug|Win32.ActiveCfg = Debug|Win32 + {565089A1-60C0-4281-99D7-A459E7354EEF}.Debug|Win32.Build.0 = Debug|Win32 + {565089A1-60C0-4281-99D7-A459E7354EEF}.Release|Win32.ActiveCfg = Release|Win32 + {565089A1-60C0-4281-99D7-A459E7354EEF}.Release|Win32.Build.0 = Release|Win32 + {54019823-E923-44D0-AD60-8EB636D107DC}.Debug|Win32.ActiveCfg = Debug|Win32 + {54019823-E923-44D0-AD60-8EB636D107DC}.Debug|Win32.Build.0 = Debug|Win32 + {54019823-E923-44D0-AD60-8EB636D107DC}.Release|Win32.ActiveCfg = Release|Win32 + {54019823-E923-44D0-AD60-8EB636D107DC}.Release|Win32.Build.0 = Release|Win32 + {3EF9795D-3509-4664-8E73-8D315CC0B944}.Debug|Win32.ActiveCfg = Debug|Win32 + {3EF9795D-3509-4664-8E73-8D315CC0B944}.Debug|Win32.Build.0 = Debug|Win32 + {3EF9795D-3509-4664-8E73-8D315CC0B944}.Release|Win32.ActiveCfg = Release|Win32 + {3EF9795D-3509-4664-8E73-8D315CC0B944}.Release|Win32.Build.0 = Release|Win32 + {797901AA-9447-45C6-AF89-5D5DA54C4159}.Debug|Win32.ActiveCfg = Debug|Win32 + {797901AA-9447-45C6-AF89-5D5DA54C4159}.Debug|Win32.Build.0 = Debug|Win32 + {797901AA-9447-45C6-AF89-5D5DA54C4159}.Release|Win32.ActiveCfg = Release|Win32 + {797901AA-9447-45C6-AF89-5D5DA54C4159}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {565089A1-60C0-4281-99D7-A459E7354EEF} = {D01B5755-53F2-4929-B69C-75C99D151E6F} + {54019823-E923-44D0-AD60-8EB636D107DC} = {D01B5755-53F2-4929-B69C-75C99D151E6F} + {3EF9795D-3509-4664-8E73-8D315CC0B944} = {D01B5755-53F2-4929-B69C-75C99D151E6F} + {797901AA-9447-45C6-AF89-5D5DA54C4159} = {D01B5755-53F2-4929-B69C-75C99D151E6F} + EndGlobalSection +EndGlobal diff --git a/DuiLib/Control/UIActiveX.cpp b/DuiLib/Control/UIActiveX.cpp new file mode 100644 index 00000000..7c1bdfc1 --- /dev/null +++ b/DuiLib/Control/UIActiveX.cpp @@ -0,0 +1,1161 @@ +#include "StdAfx.h" + +namespace DuiLib { + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +class CActiveXCtrl; + + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +class CActiveXWnd : public CWindowWnd +{ +public: + HWND Init(CActiveXCtrl* pOwner, HWND hWndParent); + + LPCTSTR GetWindowClassName() const; + void OnFinalMessage(HWND hWnd); + + LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + +protected: + void DoVerb(LONG iVerb); + + LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + +protected: + CActiveXCtrl* m_pOwner; +}; + + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +class CActiveXEnum : public IEnumUnknown +{ +public: + CActiveXEnum(IUnknown* pUnk) : m_pUnk(pUnk), m_dwRef(1), m_iPos(0) + { + m_pUnk->AddRef(); + } + ~CActiveXEnum() + { + m_pUnk->Release(); + } + + LONG m_iPos; + ULONG m_dwRef; + IUnknown* m_pUnk; + + STDMETHOD_(ULONG,AddRef)() + { + return ++m_dwRef; + } + STDMETHOD_(ULONG,Release)() + { + LONG lRef = --m_dwRef; + if( lRef == 0 ) delete this; + return lRef; + } + STDMETHOD(QueryInterface)(REFIID riid, LPVOID *ppvObject) + { + *ppvObject = NULL; + if( riid == IID_IUnknown ) *ppvObject = static_cast(this); + else if( riid == IID_IEnumUnknown ) *ppvObject = static_cast(this); + if( *ppvObject != NULL ) AddRef(); + return *ppvObject == NULL ? E_NOINTERFACE : S_OK; + } + STDMETHOD(Next)(ULONG celt, IUnknown **rgelt, ULONG *pceltFetched) + { + if( pceltFetched != NULL ) *pceltFetched = 0; + if( ++m_iPos > 1 ) return S_FALSE; + *rgelt = m_pUnk; + (*rgelt)->AddRef(); + if( pceltFetched != NULL ) *pceltFetched = 1; + return S_OK; + } + STDMETHOD(Skip)(ULONG celt) + { + m_iPos += celt; + return S_OK; + } + STDMETHOD(Reset)(void) + { + m_iPos = 0; + return S_OK; + } + STDMETHOD(Clone)(IEnumUnknown **ppenum) + { + return E_NOTIMPL; + } +}; + + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +class CActiveXFrameWnd : public IOleInPlaceFrame +{ +public: + CActiveXFrameWnd(CActiveXUI* pOwner) : m_dwRef(1), m_pOwner(pOwner), m_pActiveObject(NULL) + { + } + ~CActiveXFrameWnd() + { + if( m_pActiveObject != NULL ) m_pActiveObject->Release(); + } + + ULONG m_dwRef; + CActiveXUI* m_pOwner; + IOleInPlaceActiveObject* m_pActiveObject; + + // IUnknown + STDMETHOD_(ULONG,AddRef)() + { + return ++m_dwRef; + } + STDMETHOD_(ULONG,Release)() + { + ULONG lRef = --m_dwRef; + if( lRef == 0 ) delete this; + return lRef; + } + STDMETHOD(QueryInterface)(REFIID riid, LPVOID *ppvObject) + { + *ppvObject = NULL; + if( riid == IID_IUnknown ) *ppvObject = static_cast(this); + else if( riid == IID_IOleWindow ) *ppvObject = static_cast(this); + else if( riid == IID_IOleInPlaceFrame ) *ppvObject = static_cast(this); + else if( riid == IID_IOleInPlaceUIWindow ) *ppvObject = static_cast(this); + if( *ppvObject != NULL ) AddRef(); + return *ppvObject == NULL ? E_NOINTERFACE : S_OK; + } + // IOleInPlaceFrameWindow + STDMETHOD(InsertMenus)(HMENU /*hmenuShared*/, LPOLEMENUGROUPWIDTHS /*lpMenuWidths*/) + { + return S_OK; + } + STDMETHOD(SetMenu)(HMENU /*hmenuShared*/, HOLEMENU /*holemenu*/, HWND /*hwndActiveObject*/) + { + return S_OK; + } + STDMETHOD(RemoveMenus)(HMENU /*hmenuShared*/) + { + return S_OK; + } + STDMETHOD(SetStatusText)(LPCOLESTR /*pszStatusText*/) + { + return S_OK; + } + STDMETHOD(EnableModeless)(BOOL /*fEnable*/) + { + return S_OK; + } + STDMETHOD(TranslateAccelerator)(LPMSG /*lpMsg*/, WORD /*wID*/) + { + return S_FALSE; + } + // IOleWindow + STDMETHOD(GetWindow)(HWND* phwnd) + { + if( m_pOwner == NULL ) return E_UNEXPECTED; + *phwnd = m_pOwner->GetManager()->GetPaintWindow(); + return S_OK; + } + STDMETHOD(ContextSensitiveHelp)(BOOL /*fEnterMode*/) + { + return S_OK; + } + // IOleInPlaceUIWindow + STDMETHOD(GetBorder)(LPRECT /*lprectBorder*/) + { + return S_OK; + } + STDMETHOD(RequestBorderSpace)(LPCBORDERWIDTHS /*pborderwidths*/) + { + return INPLACE_E_NOTOOLSPACE; + } + STDMETHOD(SetBorderSpace)(LPCBORDERWIDTHS /*pborderwidths*/) + { + return S_OK; + } + STDMETHOD(SetActiveObject)(IOleInPlaceActiveObject* pActiveObject, LPCOLESTR /*pszObjName*/) + { + if( pActiveObject != NULL ) pActiveObject->AddRef(); + if( m_pActiveObject != NULL ) m_pActiveObject->Release(); + m_pActiveObject = pActiveObject; + return S_OK; + } +}; + +///////////////////////////////////////////////////////////////////////////////////// +// + +class CActiveXCtrl : + public IOleClientSite, + public IOleInPlaceSiteWindowless, + public IOleControlSite, + public IObjectWithSite, + public IOleContainer +{ + friend class CActiveXUI; + friend class CActiveXWnd; +public: + CActiveXCtrl(); + ~CActiveXCtrl(); + + // IUnknown + STDMETHOD_(ULONG,AddRef)(); + STDMETHOD_(ULONG,Release)(); + STDMETHOD(QueryInterface)(REFIID riid, LPVOID *ppvObject); + + // IObjectWithSite + STDMETHOD(SetSite)(IUnknown *pUnkSite); + STDMETHOD(GetSite)(REFIID riid, LPVOID* ppvSite); + + // IOleClientSite + STDMETHOD(SaveObject)(void); + STDMETHOD(GetMoniker)(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk); + STDMETHOD(GetContainer)(IOleContainer** ppContainer); + STDMETHOD(ShowObject)(void); + STDMETHOD(OnShowWindow)(BOOL fShow); + STDMETHOD(RequestNewObjectLayout)(void); + + // IOleInPlaceSiteWindowless + STDMETHOD(CanWindowlessActivate)(void); + STDMETHOD(GetCapture)(void); + STDMETHOD(SetCapture)(BOOL fCapture); + STDMETHOD(GetFocus)(void); + STDMETHOD(SetFocus)(BOOL fFocus); + STDMETHOD(GetDC)(LPCRECT pRect, DWORD grfFlags, HDC* phDC); + STDMETHOD(ReleaseDC)(HDC hDC); + STDMETHOD(InvalidateRect)(LPCRECT pRect, BOOL fErase); + STDMETHOD(InvalidateRgn)(HRGN hRGN, BOOL fErase); + STDMETHOD(ScrollRect)(INT dx, INT dy, LPCRECT pRectScroll, LPCRECT pRectClip); + STDMETHOD(AdjustRect)(LPRECT prc); + STDMETHOD(OnDefWindowMessage)(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT* plResult); + + // IOleInPlaceSiteEx + STDMETHOD(OnInPlaceActivateEx)(BOOL *pfNoRedraw, DWORD dwFlags); + STDMETHOD(OnInPlaceDeactivateEx)(BOOL fNoRedraw); + STDMETHOD(RequestUIActivate)(void); + + // IOleInPlaceSite + STDMETHOD(CanInPlaceActivate)(void); + STDMETHOD(OnInPlaceActivate)(void); + STDMETHOD(OnUIActivate)(void); + STDMETHOD(GetWindowContext)(IOleInPlaceFrame** ppFrame, IOleInPlaceUIWindow** ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo); + STDMETHOD(Scroll)(SIZE scrollExtant); + STDMETHOD(OnUIDeactivate)(BOOL fUndoable); + STDMETHOD(OnInPlaceDeactivate)(void); + STDMETHOD(DiscardUndoState)( void); + STDMETHOD(DeactivateAndUndo)( void); + STDMETHOD(OnPosRectChange)(LPCRECT lprcPosRect); + + // IOleWindow + STDMETHOD(GetWindow)(HWND* phwnd); + STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode); + + // IOleControlSite + STDMETHOD(OnControlInfoChanged)(void); + STDMETHOD(LockInPlaceActive)(BOOL fLock); + STDMETHOD(GetExtendedControl)(IDispatch** ppDisp); + STDMETHOD(TransformCoords)(POINTL* pPtlHimetric, POINTF* pPtfContainer, DWORD dwFlags); + STDMETHOD(TranslateAccelerator)(MSG* pMsg, DWORD grfModifiers); + STDMETHOD(OnFocus)(BOOL fGotFocus); + STDMETHOD(ShowPropertyFrame)(void); + + // IOleContainer + STDMETHOD(EnumObjects)(DWORD grfFlags, IEnumUnknown** ppenum); + STDMETHOD(LockContainer)(BOOL fLock); + + // IParseDisplayName + STDMETHOD(ParseDisplayName)(IBindCtx* pbc, LPOLESTR pszDisplayName, ULONG* pchEaten, IMoniker** ppmkOut); + +protected: + HRESULT CreateActiveXWnd(); + +protected: + LONG m_dwRef; + CActiveXUI* m_pOwner; + CActiveXWnd* m_pWindow; + IUnknown* m_pUnkSite; + IViewObject* m_pViewObject; + IOleInPlaceObjectWindowless* m_pInPlaceObject; + bool m_bLocked; + bool m_bFocused; + bool m_bCaptured; + bool m_bUIActivated; + bool m_bInPlaceActive; + bool m_bWindowless; +}; + +CActiveXCtrl::CActiveXCtrl() : +m_dwRef(1), +m_pOwner(NULL), +m_pWindow(NULL), +m_pUnkSite(NULL), +m_pViewObject(NULL), +m_pInPlaceObject(NULL), +m_bLocked(false), +m_bFocused(false), +m_bCaptured(false), +m_bWindowless(true), +m_bUIActivated(false), +m_bInPlaceActive(false) +{ +} + +CActiveXCtrl::~CActiveXCtrl() +{ + if( m_pWindow != NULL ) { + ::DestroyWindow(*m_pWindow); + delete m_pWindow; + } + if( m_pUnkSite != NULL ) m_pUnkSite->Release(); + if( m_pViewObject != NULL ) m_pViewObject->Release(); + if( m_pInPlaceObject != NULL ) m_pInPlaceObject->Release(); +} + +STDMETHODIMP CActiveXCtrl::QueryInterface(REFIID riid, LPVOID *ppvObject) +{ + *ppvObject = NULL; + if( riid == IID_IUnknown ) *ppvObject = static_cast(this); + else if( riid == IID_IOleClientSite ) *ppvObject = static_cast(this); + else if( riid == IID_IOleInPlaceSiteWindowless ) *ppvObject = static_cast(this); + else if( riid == IID_IOleInPlaceSiteEx ) *ppvObject = static_cast(this); + else if( riid == IID_IOleInPlaceSite ) *ppvObject = static_cast(this); + else if( riid == IID_IOleWindow ) *ppvObject = static_cast(this); + else if( riid == IID_IOleControlSite ) *ppvObject = static_cast(this); + else if( riid == IID_IOleContainer ) *ppvObject = static_cast(this); + else if( riid == IID_IObjectWithSite ) *ppvObject = static_cast(this); + + if( *ppvObject != NULL ) AddRef(); + return *ppvObject == NULL ? E_NOINTERFACE : S_OK; +} + +STDMETHODIMP_(ULONG) CActiveXCtrl::AddRef() +{ + return ++m_dwRef; +} + +STDMETHODIMP_(ULONG) CActiveXCtrl::Release() +{ + LONG lRef = --m_dwRef; + if( lRef == 0 ) delete this; + return lRef; +} + +STDMETHODIMP CActiveXCtrl::SetSite(IUnknown *pUnkSite) +{ + DUITRACE(_T("AX: CActiveXCtrl::SetSite")); + if( m_pUnkSite != NULL ) { + m_pUnkSite->Release(); + m_pUnkSite = NULL; + } + if( pUnkSite != NULL ) { + m_pUnkSite = pUnkSite; + m_pUnkSite->AddRef(); + } + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::GetSite(REFIID riid, LPVOID* ppvSite) +{ + DUITRACE(_T("AX: CActiveXCtrl::GetSite")); + if( ppvSite == NULL ) return E_POINTER; + *ppvSite = NULL; + if( m_pUnkSite == NULL ) return E_FAIL; + return m_pUnkSite->QueryInterface(riid, ppvSite); +} + +STDMETHODIMP CActiveXCtrl::SaveObject(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::SaveObject")); + return E_NOTIMPL; +} + +STDMETHODIMP CActiveXCtrl::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk) +{ + DUITRACE(_T("AX: CActiveXCtrl::GetMoniker")); + if( ppmk != NULL ) *ppmk = NULL; + return E_NOTIMPL; +} + +STDMETHODIMP CActiveXCtrl::GetContainer(IOleContainer** ppContainer) +{ + DUITRACE(_T("AX: CActiveXCtrl::GetContainer")); + if( ppContainer == NULL ) return E_POINTER; + *ppContainer = NULL; + HRESULT Hr = E_NOTIMPL; + if( m_pUnkSite != NULL ) Hr = m_pUnkSite->QueryInterface(IID_IOleContainer, (LPVOID*) ppContainer); + if( FAILED(Hr) ) Hr = QueryInterface(IID_IOleContainer, (LPVOID*) ppContainer); + return Hr; +} + +STDMETHODIMP CActiveXCtrl::ShowObject(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::ShowObject")); + if( m_pOwner == NULL ) return E_UNEXPECTED; + HDC hDC = ::GetDC(m_pOwner->m_hwndHost); + if( hDC == NULL ) return E_FAIL; + if( m_pViewObject != NULL ) m_pViewObject->Draw(DVASPECT_CONTENT, -1, NULL, NULL, NULL, hDC, (RECTL*) &m_pOwner->m_rcItem, (RECTL*) &m_pOwner->m_rcItem, NULL, NULL); + ::ReleaseDC(m_pOwner->m_hwndHost, hDC); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::OnShowWindow(BOOL fShow) +{ + DUITRACE(_T("AX: CActiveXCtrl::OnShowWindow")); + return E_NOTIMPL; +} + +STDMETHODIMP CActiveXCtrl::RequestNewObjectLayout(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::RequestNewObjectLayout")); + return E_NOTIMPL; +} + +STDMETHODIMP CActiveXCtrl::CanWindowlessActivate(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::CanWindowlessActivate")); + return S_OK; // Yes, we can!! +} + +STDMETHODIMP CActiveXCtrl::GetCapture(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::GetCapture")); + if( m_pOwner == NULL ) return E_UNEXPECTED; + return m_bCaptured ? S_OK : S_FALSE; +} + +STDMETHODIMP CActiveXCtrl::SetCapture(BOOL fCapture) +{ + DUITRACE(_T("AX: CActiveXCtrl::SetCapture")); + if( m_pOwner == NULL ) return E_UNEXPECTED; + m_bCaptured = (fCapture == TRUE); + if( fCapture ) ::SetCapture(m_pOwner->m_hwndHost); else ::ReleaseCapture(); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::GetFocus(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::GetFocus")); + if( m_pOwner == NULL ) return E_UNEXPECTED; + return m_bFocused ? S_OK : S_FALSE; +} + +STDMETHODIMP CActiveXCtrl::SetFocus(BOOL fFocus) +{ + DUITRACE(_T("AX: CActiveXCtrl::SetFocus")); + if( m_pOwner == NULL ) return E_UNEXPECTED; + if( fFocus ) m_pOwner->SetFocus(); + m_bFocused = (fFocus == TRUE); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::GetDC(LPCRECT pRect, DWORD grfFlags, HDC* phDC) +{ + DUITRACE(_T("AX: CActiveXCtrl::GetDC")); + if( phDC == NULL ) return E_POINTER; + if( m_pOwner == NULL ) return E_UNEXPECTED; + *phDC = ::GetDC(m_pOwner->m_hwndHost); + if( (grfFlags & OLEDC_PAINTBKGND) != 0 ) { + CDuiRect rcItem = m_pOwner->GetPos(); + if( !m_bWindowless ) rcItem.ResetOffset(); + ::FillRect(*phDC, &rcItem, (HBRUSH) (COLOR_WINDOW + 1)); + } + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::ReleaseDC(HDC hDC) +{ + DUITRACE(_T("AX: CActiveXCtrl::ReleaseDC")); + if( m_pOwner == NULL ) return E_UNEXPECTED; + ::ReleaseDC(m_pOwner->m_hwndHost, hDC); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::InvalidateRect(LPCRECT pRect, BOOL fErase) +{ + DUITRACE(_T("AX: CActiveXCtrl::InvalidateRect")); + if( m_pOwner == NULL ) return E_UNEXPECTED; + if( m_pOwner->m_hwndHost == NULL ) return E_FAIL; + return ::InvalidateRect(m_pOwner->m_hwndHost, pRect, fErase) ? S_OK : E_FAIL; +} + +STDMETHODIMP CActiveXCtrl::InvalidateRgn(HRGN hRGN, BOOL fErase) +{ + DUITRACE(_T("AX: CActiveXCtrl::InvalidateRgn")); + if( m_pOwner == NULL ) return E_UNEXPECTED; + return ::InvalidateRgn(m_pOwner->m_hwndHost, hRGN, fErase) ? S_OK : E_FAIL; +} + +STDMETHODIMP CActiveXCtrl::ScrollRect(INT dx, INT dy, LPCRECT pRectScroll, LPCRECT pRectClip) +{ + DUITRACE(_T("AX: CActiveXCtrl::ScrollRect")); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::AdjustRect(LPRECT prc) +{ + DUITRACE(_T("AX: CActiveXCtrl::AdjustRect")); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::OnDefWindowMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT* plResult) +{ + DUITRACE(_T("AX: CActiveXCtrl::OnDefWindowMessage")); + if( m_pOwner == NULL ) return E_UNEXPECTED; + *plResult = ::DefWindowProc(m_pOwner->m_hwndHost, msg, wParam, lParam); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::OnInPlaceActivateEx(BOOL* pfNoRedraw, DWORD dwFlags) +{ + DUITRACE(_T("AX: CActiveXCtrl::OnInPlaceActivateEx")); + ASSERT(m_pInPlaceObject==NULL); + if( m_pOwner == NULL ) return E_UNEXPECTED; + if( m_pOwner->m_pUnk == NULL ) return E_UNEXPECTED; + ::OleLockRunning(m_pOwner->m_pUnk, TRUE, FALSE); + HWND hWndFrame = m_pOwner->GetManager()->GetPaintWindow(); + HRESULT Hr = E_FAIL; + if( (dwFlags & ACTIVATE_WINDOWLESS) != 0 ) { + m_bWindowless = true; + Hr = m_pOwner->m_pUnk->QueryInterface(IID_IOleInPlaceObjectWindowless, (LPVOID*) &m_pInPlaceObject); + m_pOwner->m_hwndHost = hWndFrame; + m_pOwner->GetManager()->AddMessageFilter(m_pOwner); + } + if( FAILED(Hr) ) { + m_bWindowless = false; + Hr = CreateActiveXWnd(); + if( FAILED(Hr) ) return Hr; + Hr = m_pOwner->m_pUnk->QueryInterface(IID_IOleInPlaceObject, (LPVOID*) &m_pInPlaceObject); + } + if( m_pInPlaceObject != NULL ) { + CDuiRect rcItem = m_pOwner->m_rcItem; + if( !m_bWindowless ) rcItem.ResetOffset(); + m_pInPlaceObject->SetObjectRects(&rcItem, &rcItem); + } + m_bInPlaceActive = SUCCEEDED(Hr); + return Hr; +} + +STDMETHODIMP CActiveXCtrl::OnInPlaceDeactivateEx(BOOL fNoRedraw) +{ + DUITRACE(_T("AX: CActiveXCtrl::OnInPlaceDeactivateEx")); + m_bInPlaceActive = false; + if( m_pInPlaceObject != NULL ) { + m_pInPlaceObject->Release(); + m_pInPlaceObject = NULL; + } + if( m_pWindow != NULL ) { + ::DestroyWindow(*m_pWindow); + delete m_pWindow; + m_pWindow = NULL; + } + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::RequestUIActivate(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::RequestUIActivate")); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::CanInPlaceActivate(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::CanInPlaceActivate")); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::OnInPlaceActivate(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::OnInPlaceActivate")); + BOOL bDummy = FALSE; + return OnInPlaceActivateEx(&bDummy, 0); +} + +STDMETHODIMP CActiveXCtrl::OnUIActivate(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::OnUIActivate")); + m_bUIActivated = true; + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::GetWindowContext(IOleInPlaceFrame** ppFrame, IOleInPlaceUIWindow** ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo) +{ + DUITRACE(_T("AX: CActiveXCtrl::GetWindowContext")); + if( ppDoc == NULL ) return E_POINTER; + if( ppFrame == NULL ) return E_POINTER; + if( lprcPosRect == NULL ) return E_POINTER; + if( lprcClipRect == NULL ) return E_POINTER; + if (m_pWindow) + { + ::GetClientRect(m_pWindow->GetHWND(),lprcPosRect); + ::GetClientRect(m_pWindow->GetHWND(),lprcClipRect); + } + else + { + RECT rcItem = m_pOwner->GetPos(); + memcpy(lprcPosRect, &rcItem, sizeof(rcItem)); + memcpy(lprcClipRect, &rcItem, sizeof(rcItem)); + } + *ppFrame = new CActiveXFrameWnd(m_pOwner); + *ppDoc = NULL; + ACCEL ac = { 0 }; + HACCEL hac = ::CreateAcceleratorTable(&ac, 1); + lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO); + lpFrameInfo->fMDIApp = FALSE; + lpFrameInfo->hwndFrame = m_pOwner->GetManager()->GetPaintWindow(); + lpFrameInfo->haccel = hac; + lpFrameInfo->cAccelEntries = 1; + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::Scroll(SIZE scrollExtant) +{ + DUITRACE(_T("AX: CActiveXCtrl::Scroll")); + return E_NOTIMPL; +} + +STDMETHODIMP CActiveXCtrl::OnUIDeactivate(BOOL fUndoable) +{ + DUITRACE(_T("AX: CActiveXCtrl::OnUIDeactivate")); + m_bUIActivated = false; + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::OnInPlaceDeactivate(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::OnInPlaceDeactivate")); + return OnInPlaceDeactivateEx(TRUE); +} + +STDMETHODIMP CActiveXCtrl::DiscardUndoState(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::DiscardUndoState")); + return E_NOTIMPL; +} + +STDMETHODIMP CActiveXCtrl::DeactivateAndUndo(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::DeactivateAndUndo")); + return E_NOTIMPL; +} + +STDMETHODIMP CActiveXCtrl::OnPosRectChange(LPCRECT lprcPosRect) +{ + DUITRACE(_T("AX: CActiveXCtrl::OnPosRectChange")); + return E_NOTIMPL; +} + +STDMETHODIMP CActiveXCtrl::GetWindow(HWND* phwnd) +{ + DUITRACE(_T("AX: CActiveXCtrl::GetWindow")); + if( m_pOwner == NULL ) return E_UNEXPECTED; + if( m_pOwner->m_hwndHost == NULL ) CreateActiveXWnd(); + if( m_pOwner->m_hwndHost == NULL ) return E_FAIL; + *phwnd = m_pOwner->m_hwndHost; + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::ContextSensitiveHelp(BOOL fEnterMode) +{ + DUITRACE(_T("AX: CActiveXCtrl::ContextSensitiveHelp")); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::OnControlInfoChanged(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::OnControlInfoChanged")); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::LockInPlaceActive(BOOL fLock) +{ + DUITRACE(_T("AX: CActiveXCtrl::LockInPlaceActive")); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::GetExtendedControl(IDispatch** ppDisp) +{ + DUITRACE(_T("AX: CActiveXCtrl::GetExtendedControl")); + if( ppDisp == NULL ) return E_POINTER; + if( m_pOwner == NULL ) return E_UNEXPECTED; + if( m_pOwner->m_pUnk == NULL ) return E_UNEXPECTED; + return m_pOwner->m_pUnk->QueryInterface(IID_IDispatch, (LPVOID*) ppDisp); +} + +STDMETHODIMP CActiveXCtrl::TransformCoords(POINTL* pPtlHimetric, POINTF* pPtfContainer, DWORD dwFlags) +{ + DUITRACE(_T("AX: CActiveXCtrl::TransformCoords")); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::TranslateAccelerator(MSG *pMsg, DWORD grfModifiers) +{ + DUITRACE(_T("AX: CActiveXCtrl::TranslateAccelerator")); + return S_FALSE; +} + +STDMETHODIMP CActiveXCtrl::OnFocus(BOOL fGotFocus) +{ + DUITRACE(_T("AX: CActiveXCtrl::OnFocus")); + m_bFocused = (fGotFocus == TRUE); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::ShowPropertyFrame(void) +{ + DUITRACE(_T("AX: CActiveXCtrl::ShowPropertyFrame")); + return E_NOTIMPL; +} + +STDMETHODIMP CActiveXCtrl::EnumObjects(DWORD grfFlags, IEnumUnknown** ppenum) +{ + DUITRACE(_T("AX: CActiveXCtrl::EnumObjects")); + if( ppenum == NULL ) return E_POINTER; + if( m_pOwner == NULL ) return E_UNEXPECTED; + *ppenum = new CActiveXEnum(m_pOwner->m_pUnk); + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::LockContainer(BOOL fLock) +{ + DUITRACE(_T("AX: CActiveXCtrl::LockContainer")); + m_bLocked = fLock != FALSE; + return S_OK; +} + +STDMETHODIMP CActiveXCtrl::ParseDisplayName(IBindCtx *pbc, LPOLESTR pszDisplayName, ULONG* pchEaten, IMoniker** ppmkOut) +{ + DUITRACE(_T("AX: CActiveXCtrl::ParseDisplayName")); + return E_NOTIMPL; +} + + +HRESULT CActiveXCtrl::CreateActiveXWnd() +{ + if( m_pWindow != NULL ) return S_OK; + m_pWindow = new CActiveXWnd; + if( m_pWindow == NULL ) return E_OUTOFMEMORY; + m_pOwner->m_hwndHost = m_pWindow->Init(this, m_pOwner->GetManager()->GetPaintWindow()); + return S_OK; +} + + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +HWND CActiveXWnd::Init(CActiveXCtrl* pOwner, HWND hWndParent) +{ + m_pOwner = pOwner; + UINT uStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + Create(hWndParent, _T("UIActiveX"), uStyle, 0L, 0,0,0,0, NULL); + return m_hWnd; +} + +LPCTSTR CActiveXWnd::GetWindowClassName() const +{ + return _T("ActiveXWnd"); +} + +void CActiveXWnd::OnFinalMessage(HWND hWnd) +{ + //delete this; // ﲻҪCActiveXUI +} + +void CActiveXWnd::DoVerb(LONG iVerb) +{ + if( m_pOwner == NULL ) return; + if( m_pOwner->m_pOwner == NULL ) return; + IOleObject* pUnk = NULL; + m_pOwner->m_pOwner->GetControl(IID_IOleObject, (LPVOID*) &pUnk); + if( pUnk == NULL ) return; + CSafeRelease RefOleObject = pUnk; + IOleClientSite* pOleClientSite = NULL; + m_pOwner->QueryInterface(IID_IOleClientSite, (LPVOID*) &pOleClientSite); + CSafeRelease RefOleClientSite = pOleClientSite; + pUnk->DoVerb(iVerb, NULL, pOleClientSite, 0, m_hWnd, &m_pOwner->m_pOwner->GetPos()); +} + +LRESULT CActiveXWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LRESULT lRes=0; + BOOL bHandled = TRUE; + switch( uMsg ) { + case WM_PAINT: lRes = OnPaint(uMsg, wParam, lParam, bHandled); break; + case WM_SETFOCUS: lRes = OnSetFocus(uMsg, wParam, lParam, bHandled); break; + case WM_KILLFOCUS: lRes = OnKillFocus(uMsg, wParam, lParam, bHandled); break; + case WM_ERASEBKGND: lRes = OnEraseBkgnd(uMsg, wParam, lParam, bHandled); break; + case WM_MOUSEACTIVATE: lRes = OnMouseActivate(uMsg, wParam, lParam, bHandled); break; + case WM_MOUSEWHEEL: break; + default: + bHandled = FALSE; + } + if( !bHandled ) return CWindowWnd::HandleMessage(uMsg, wParam, lParam); + return lRes; +} + +LRESULT CActiveXWnd::OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + if( m_pOwner->m_pViewObject == NULL ) bHandled = FALSE; + return 1; +} + +LRESULT CActiveXWnd::OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + IOleObject* pUnk = NULL; + m_pOwner->m_pOwner->GetControl(IID_IOleObject, (LPVOID*) &pUnk); + if( pUnk == NULL ) return 0; + CSafeRelease RefOleObject = pUnk; + DWORD dwMiscStatus = 0; + pUnk->GetMiscStatus(DVASPECT_CONTENT, &dwMiscStatus); + if( (dwMiscStatus & OLEMISC_NOUIACTIVATE) != 0 ) return 0; + if( !m_pOwner->m_bInPlaceActive ) DoVerb(OLEIVERB_INPLACEACTIVATE); + bHandled = FALSE; + return 0; +} + +LRESULT CActiveXWnd::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + bHandled = FALSE; + m_pOwner->m_bFocused = true; + if( !m_pOwner->m_bUIActivated ) DoVerb(OLEIVERB_UIACTIVATE); + return 0; +} + +LRESULT CActiveXWnd::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + bHandled = FALSE; + m_pOwner->m_bFocused = false; + return 0; +} + +LRESULT CActiveXWnd::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + PAINTSTRUCT ps = { 0 }; + ::BeginPaint(m_hWnd, &ps); + ::EndPaint(m_hWnd, &ps); + return 1; +} + + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +CActiveXUI::CActiveXUI() : m_pUnk(NULL), m_pControl(NULL), m_hwndHost(NULL), m_bCreated(false), m_bDelayCreate(true) +{ + m_clsid = IID_NULL; +} + +CActiveXUI::~CActiveXUI() +{ + ReleaseControl(); +} + +LPCTSTR CActiveXUI::GetClass() const +{ + return _T("ActiveXUI"); +} + +LPVOID CActiveXUI::GetInterface(LPCTSTR pstrName) +{ + if( _tcscmp(pstrName, DUI_CTR_ACTIVEX) == 0 ) return static_cast(this); + return CControlUI::GetInterface(pstrName); +} + +HWND CActiveXUI::GetHostWindow() const +{ + return m_hwndHost; +} + +static void PixelToHiMetric(const SIZEL* lpSizeInPix, LPSIZEL lpSizeInHiMetric) +{ +#define HIMETRIC_PER_INCH 2540 +#define MAP_PIX_TO_LOGHIM(x,ppli) MulDiv(HIMETRIC_PER_INCH, (x), (ppli)) +#define MAP_LOGHIM_TO_PIX(x,ppli) MulDiv((ppli), (x), HIMETRIC_PER_INCH) + int nPixelsPerInchX; // Pixels per logical inch along width + int nPixelsPerInchY; // Pixels per logical inch along height + HDC hDCScreen = ::GetDC(NULL); + nPixelsPerInchX = ::GetDeviceCaps(hDCScreen, LOGPIXELSX); + nPixelsPerInchY = ::GetDeviceCaps(hDCScreen, LOGPIXELSY); + ::ReleaseDC(NULL, hDCScreen); + lpSizeInHiMetric->cx = MAP_PIX_TO_LOGHIM(lpSizeInPix->cx, nPixelsPerInchX); + lpSizeInHiMetric->cy = MAP_PIX_TO_LOGHIM(lpSizeInPix->cy, nPixelsPerInchY); +} + +void CActiveXUI::SetVisible(bool bVisible) +{ + CControlUI::SetVisible(bVisible); + if( m_hwndHost != NULL && !m_pControl->m_bWindowless ) + ::ShowWindow(m_hwndHost, IsVisible() ? SW_SHOW : SW_HIDE); +} + +void CActiveXUI::SetInternVisible(bool bVisible) +{ + CControlUI::SetInternVisible(bVisible); + if( m_hwndHost != NULL && !m_pControl->m_bWindowless ) + ::ShowWindow(m_hwndHost, IsVisible() ? SW_SHOW : SW_HIDE); +} + +void CActiveXUI::SetPos(RECT rc) +{ + CControlUI::SetPos(rc); + + if( !m_bCreated ) DoCreateControl(); + + if( m_pUnk == NULL ) return; + if( m_pControl == NULL ) return; + + SIZEL hmSize = { 0 }; + SIZEL pxSize = { 0 }; + pxSize.cx = m_rcItem.right - m_rcItem.left; + pxSize.cy = m_rcItem.bottom - m_rcItem.top; + PixelToHiMetric(&pxSize, &hmSize); + + if( m_pUnk != NULL ) { + m_pUnk->SetExtent(DVASPECT_CONTENT, &hmSize); + } + if( m_pControl->m_pInPlaceObject != NULL ) { + CDuiRect rcItem = m_rcItem; + if( !m_pControl->m_bWindowless ) rcItem.ResetOffset(); + m_pControl->m_pInPlaceObject->SetObjectRects(&rcItem, &rcItem); + } + if( !m_pControl->m_bWindowless ) { + ASSERT(m_pControl->m_pWindow); + ::MoveWindow(*m_pControl->m_pWindow, m_rcItem.left, m_rcItem.top, m_rcItem.right - m_rcItem.left, m_rcItem.bottom - m_rcItem.top, TRUE); + } +} + +void CActiveXUI::DoPaint(HDC hDC, const RECT& rcPaint) +{ + if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return; + + if( m_pControl != NULL && m_pControl->m_bWindowless && m_pControl->m_pViewObject != NULL ) + { + m_pControl->m_pViewObject->Draw(DVASPECT_CONTENT, -1, NULL, NULL, NULL, hDC, (RECTL*) &m_rcItem, (RECTL*) &m_rcItem, NULL, NULL); + } +} + +void CActiveXUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) +{ + if( _tcscmp(pstrName, _T("clsid")) == 0 ) CreateControl(pstrValue); + else if( _tcscmp(pstrName, _T("modulename")) == 0 ) SetModuleName(pstrValue); + else if( _tcscmp(pstrName, _T("delaycreate")) == 0 ) SetDelayCreate(_tcscmp(pstrValue, _T("true")) == 0); + else CControlUI::SetAttribute(pstrName, pstrValue); +} + +LRESULT CActiveXUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled) +{ + if( m_pControl == NULL ) return 0; + ASSERT(m_pControl->m_bWindowless); + if( !m_pControl->m_bInPlaceActive ) return 0; + if( m_pControl->m_pInPlaceObject == NULL ) return 0; + if( !IsMouseEnabled() && uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST ) return 0; + bool bWasHandled = true; + if( (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) || uMsg == WM_SETCURSOR ) { + // Mouse message only go when captured or inside rect + DWORD dwHitResult = m_pControl->m_bCaptured ? HITRESULT_HIT : HITRESULT_OUTSIDE; + if( dwHitResult == HITRESULT_OUTSIDE && m_pControl->m_pViewObject != NULL ) { + IViewObjectEx* pViewEx = NULL; + m_pControl->m_pViewObject->QueryInterface(IID_IViewObjectEx, (LPVOID*) &pViewEx); + if( pViewEx != NULL ) { + POINT ptMouse = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + pViewEx->QueryHitPoint(DVASPECT_CONTENT, &m_rcItem, ptMouse, 0, &dwHitResult); + pViewEx->Release(); + } + } + if( dwHitResult != HITRESULT_HIT ) return 0; + if( uMsg == WM_SETCURSOR ) bWasHandled = false; + } + else if( uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST ) { + // Keyboard messages just go when we have focus + if( !IsFocused() ) return 0; + } + else { + switch( uMsg ) { + case WM_HELP: + case WM_CONTEXTMENU: + bWasHandled = false; + break; + default: + return 0; + } + } + LRESULT lResult = 0; + HRESULT Hr = m_pControl->m_pInPlaceObject->OnWindowMessage(uMsg, wParam, lParam, &lResult); + if( Hr == S_OK ) bHandled = bWasHandled; + return lResult; +} + +bool CActiveXUI::IsDelayCreate() const +{ + return m_bDelayCreate; +} + +void CActiveXUI::SetDelayCreate(bool bDelayCreate) +{ + if( m_bDelayCreate == bDelayCreate ) return; + if( bDelayCreate == false ) { + if( m_bCreated == false && m_clsid != IID_NULL ) DoCreateControl(); + } + m_bDelayCreate = bDelayCreate; +} + +bool CActiveXUI::CreateControl(LPCTSTR pstrCLSID) +{ + CLSID clsid = { 0 }; + OLECHAR szCLSID[100] = { 0 }; +#ifndef _UNICODE + ::MultiByteToWideChar(::GetACP(), 0, pstrCLSID, -1, szCLSID, lengthof(szCLSID) - 1); +#else + _tcsncpy(szCLSID, pstrCLSID, lengthof(szCLSID) - 1); +#endif + if( pstrCLSID[0] == '{' ) ::CLSIDFromString(szCLSID, &clsid); + else ::CLSIDFromProgID(szCLSID, &clsid); + return CreateControl(clsid); +} + +bool CActiveXUI::CreateControl(const CLSID clsid) +{ + ASSERT(clsid!=IID_NULL); + if( clsid == IID_NULL ) return false; + m_bCreated = false; + m_clsid = clsid; + if( !m_bDelayCreate ) DoCreateControl(); + return true; +} + +void CActiveXUI::ReleaseControl() +{ + m_hwndHost = NULL; + if( m_pUnk != NULL ) { + IObjectWithSite* pSite = NULL; + m_pUnk->QueryInterface(IID_IObjectWithSite, (LPVOID*) &pSite); + if( pSite != NULL ) { + pSite->SetSite(NULL); + pSite->Release(); + } + m_pUnk->Close(OLECLOSE_NOSAVE); + m_pUnk->SetClientSite(NULL); + m_pUnk->Release(); + m_pUnk = NULL; + } + if( m_pControl != NULL ) { + m_pControl->m_pOwner = NULL; + m_pControl->Release(); + m_pControl = NULL; + } + m_pManager->RemoveMessageFilter(this); +} + +typedef HRESULT (__stdcall *DllGetClassObjectFunc)(REFCLSID rclsid, REFIID riid, LPVOID* ppv); + +bool CActiveXUI::DoCreateControl() +{ + ReleaseControl(); + // At this point we'll create the ActiveX control + m_bCreated = true; + IOleControl* pOleControl = NULL; + + HRESULT Hr = -1; + if( !m_sModuleName.IsEmpty() ) { + HMODULE hModule = ::LoadLibrary((LPCTSTR)m_sModuleName); + if( hModule != NULL ) { + IClassFactory* aClassFactory = NULL; + DllGetClassObjectFunc aDllGetClassObjectFunc = (DllGetClassObjectFunc)::GetProcAddress(hModule, "DllGetClassObject"); + Hr = aDllGetClassObjectFunc(m_clsid, IID_IClassFactory, (LPVOID*)&aClassFactory); + if( SUCCEEDED(Hr) ) { + Hr = aClassFactory->CreateInstance(NULL, IID_IOleObject, (LPVOID*)&pOleControl); + } + aClassFactory->Release(); + } + } + if( FAILED(Hr) ) { + Hr = ::CoCreateInstance(m_clsid, NULL, CLSCTX_ALL, IID_IOleControl, (LPVOID*)&pOleControl); + } + ASSERT(SUCCEEDED(Hr)); + if( FAILED(Hr) ) return false; + pOleControl->QueryInterface(IID_IOleObject, (LPVOID*) &m_pUnk); + pOleControl->Release(); + if( m_pUnk == NULL ) return false; + // Create the host too + m_pControl = new CActiveXCtrl(); + m_pControl->m_pOwner = this; + // More control creation stuff + DWORD dwMiscStatus = 0; + m_pUnk->GetMiscStatus(DVASPECT_CONTENT, &dwMiscStatus); + IOleClientSite* pOleClientSite = NULL; + m_pControl->QueryInterface(IID_IOleClientSite, (LPVOID*) &pOleClientSite); + CSafeRelease RefOleClientSite = pOleClientSite; + // Initialize control + if( (dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST) != 0 ) m_pUnk->SetClientSite(pOleClientSite); + IPersistStreamInit* pPersistStreamInit = NULL; + m_pUnk->QueryInterface(IID_IPersistStreamInit, (LPVOID*) &pPersistStreamInit); + if( pPersistStreamInit != NULL ) { + Hr = pPersistStreamInit->InitNew(); + pPersistStreamInit->Release(); + } + if( FAILED(Hr) ) return false; + if( (dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST) == 0 ) m_pUnk->SetClientSite(pOleClientSite); + // Grab the view... + Hr = m_pUnk->QueryInterface(IID_IViewObjectEx, (LPVOID*) &m_pControl->m_pViewObject); + if( FAILED(Hr) ) Hr = m_pUnk->QueryInterface(IID_IViewObject2, (LPVOID*) &m_pControl->m_pViewObject); + if( FAILED(Hr) ) Hr = m_pUnk->QueryInterface(IID_IViewObject, (LPVOID*) &m_pControl->m_pViewObject); + // Activate and done... + m_pUnk->SetHostNames(OLESTR("UIActiveX"), NULL); + if( m_pManager != NULL ) m_pManager->SendNotify((CControlUI*)this, DUI_MSGTYPE_SHOWACTIVEX, 0, 0, false); + if( (dwMiscStatus & OLEMISC_INVISIBLEATRUNTIME) == 0 ) { + Hr = m_pUnk->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, pOleClientSite, 0, m_pManager->GetPaintWindow(), &m_rcItem); + //::RedrawWindow(m_pManager->GetPaintWindow(), &m_rcItem, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE | RDW_INTERNALPAINT | RDW_FRAME); + } + IObjectWithSite* pSite = NULL; + m_pUnk->QueryInterface(IID_IObjectWithSite, (LPVOID*) &pSite); + if( pSite != NULL ) { + pSite->SetSite(static_cast(m_pControl)); + pSite->Release(); + } + + return SUCCEEDED(Hr); +} + +HRESULT CActiveXUI::GetControl(const IID iid, LPVOID* ppRet) +{ + ASSERT(ppRet!=NULL); + ASSERT(*ppRet==NULL); + if( ppRet == NULL ) return E_POINTER; + if( m_pUnk == NULL ) return E_PENDING; + return m_pUnk->QueryInterface(iid, (LPVOID*) ppRet); +} + +CLSID CActiveXUI::GetClisd() const +{ + return m_clsid; +} + +CDuiString CActiveXUI::GetModuleName() const +{ + return m_sModuleName; +} + +void CActiveXUI::SetModuleName(LPCTSTR pstrText) +{ + m_sModuleName = pstrText; +} + +} // namespace DuiLib \ No newline at end of file diff --git a/DuiLib/Control/UIActiveX.h b/DuiLib/Control/UIActiveX.h new file mode 100644 index 00000000..17e15bba --- /dev/null +++ b/DuiLib/Control/UIActiveX.h @@ -0,0 +1,75 @@ +#ifndef __UIACTIVEX_H__ +#define __UIACTIVEX_H__ + +#pragma once + +struct IOleObject; + + +namespace DuiLib { + ///////////////////////////////////////////////////////////////////////////////////// + // + + class CActiveXCtrl; + + template< class T > + class CSafeRelease + { + public: + CSafeRelease(T* p) : m_p(p) { }; + ~CSafeRelease() { if (m_p != NULL) m_p->Release(); }; + T* Detach() { T* t = m_p; m_p = NULL; return t; }; + T* m_p; + }; + + ///////////////////////////////////////////////////////////////////////////////////// + // + + class UILIB_API CActiveXUI : public CControlUI, public IMessageFilterUI + { + friend class CActiveXCtrl; + public: + CActiveXUI(); + virtual ~CActiveXUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + + HWND GetHostWindow() const; + + bool IsDelayCreate() const; + void SetDelayCreate(bool bDelayCreate = true); + + bool CreateControl(const CLSID clsid); + bool CreateControl(LPCTSTR pstrCLSID); + HRESULT GetControl(const IID iid, LPVOID* ppRet); + CLSID GetClisd() const; + CDuiString GetModuleName() const; + void SetModuleName(LPCTSTR pstrText); + + void SetVisible(bool bVisible = true); + void SetInternVisible(bool bVisible = true); + void SetPos(RECT rc); + void DoPaint(HDC hDC, const RECT& rcPaint); + + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled); + + protected: + virtual void ReleaseControl(); + virtual bool DoCreateControl(); + + protected: + CLSID m_clsid; + CDuiString m_sModuleName; + bool m_bCreated; + bool m_bDelayCreate; + IOleObject* m_pUnk; + CActiveXCtrl* m_pControl; + HWND m_hwndHost; + }; + +} // namespace DuiLib + +#endif // __UIACTIVEX_H__ diff --git a/DuiLib/Control/UIButton.cpp b/DuiLib/Control/UIButton.cpp new file mode 100644 index 00000000..81772816 --- /dev/null +++ b/DuiLib/Control/UIButton.cpp @@ -0,0 +1,453 @@ +#include "stdafx.h" +#include "UIButton.h" + +namespace DuiLib +{ + CButtonUI::CButtonUI() + : m_uButtonState(0) + , m_dwHotTextColor(0) + , m_dwPushedTextColor(0) + , m_dwFocusedTextColor(0) + ,m_dwHotBkColor(0) + ,m_iBindTabIndex(-1) + { + m_uTextStyle = DT_SINGLELINE | DT_VCENTER | DT_CENTER; + } + + LPCTSTR CButtonUI::GetClass() const + { + return _T("ButtonUI"); + } + + LPVOID CButtonUI::GetInterface(LPCTSTR pstrName) + { + if( _tcscmp(pstrName, DUI_CTR_BUTTON) == 0 ) return static_cast(this); + return CLabelUI::GetInterface(pstrName); + } + + UINT CButtonUI::GetControlFlags() const + { + return (IsKeyboardEnabled() ? UIFLAG_TABSTOP : 0) | (IsEnabled() ? UIFLAG_SETCURSOR : 0); + } + + void CButtonUI::DoEvent(TEventUI& event) + { + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pParent != NULL ) m_pParent->DoEvent(event); + else CLabelUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_SETFOCUS ) + { + Invalidate(); + } + if( event.Type == UIEVENT_KILLFOCUS ) + { + Invalidate(); + } + if( event.Type == UIEVENT_KEYDOWN ) + { + if (IsKeyboardEnabled()) { + if( event.chKey == VK_SPACE || event.chKey == VK_RETURN ) { + Activate(); + return; + } + } + } + if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK) + { + if( ::PtInRect(&m_rcItem, event.ptMouse) && IsEnabled() ) { + m_uButtonState |= UISTATE_PUSHED | UISTATE_CAPTURED; + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_MOUSEMOVE ) + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { + if( ::PtInRect(&m_rcItem, event.ptMouse) ) m_uButtonState |= UISTATE_PUSHED; + else m_uButtonState &= ~UISTATE_PUSHED; + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_BUTTONUP ) + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { + if( ::PtInRect(&m_rcItem, event.ptMouse) ) Activate(); + m_uButtonState &= ~(UISTATE_PUSHED | UISTATE_CAPTURED); + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_CONTEXTMENU ) + { + if( IsContextMenuUsed() ) { + m_pManager->SendNotify(this, DUI_MSGTYPE_MENU, event.wParam, event.lParam); + } + return; + } + if( event.Type == UIEVENT_MOUSEENTER ) + { + if( IsEnabled() ) { + m_uButtonState |= UISTATE_HOT; + Invalidate(); + } + // return; + } + if( event.Type == UIEVENT_MOUSELEAVE ) + { + if( IsEnabled() ) { + m_uButtonState &= ~UISTATE_HOT; + Invalidate(); + } + // return; + } + if( event.Type == UIEVENT_SETCURSOR ) { + ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND))); + return; + } + CLabelUI::DoEvent(event); + } + + bool CButtonUI::Activate() + { + if( !CControlUI::Activate() ) return false; + if( m_pManager != NULL ) + { + m_pManager->SendNotify(this, DUI_MSGTYPE_CLICK); + BindTriggerTabSel(); + } + return true; + } + + void CButtonUI::SetEnabled(bool bEnable) + { + CControlUI::SetEnabled(bEnable); + if( !IsEnabled() ) { + m_uButtonState = 0; + } + } + + void CButtonUI::SetHotBkColor( DWORD dwColor ) + { + m_dwHotBkColor = dwColor; + } + + DWORD CButtonUI::GetHotBkColor() const + { + return m_dwHotBkColor; + } + + void CButtonUI::SetHotTextColor(DWORD dwColor) + { + m_dwHotTextColor = dwColor; + } + + DWORD CButtonUI::GetHotTextColor() const + { + return m_dwHotTextColor; + } + + void CButtonUI::SetPushedTextColor(DWORD dwColor) + { + m_dwPushedTextColor = dwColor; + } + + DWORD CButtonUI::GetPushedTextColor() const + { + return m_dwPushedTextColor; + } + + void CButtonUI::SetFocusedTextColor(DWORD dwColor) + { + m_dwFocusedTextColor = dwColor; + } + + DWORD CButtonUI::GetFocusedTextColor() const + { + return m_dwFocusedTextColor; + } + + LPCTSTR CButtonUI::GetNormalImage() + { + return m_sNormalImage; + } + + void CButtonUI::SetNormalImage(LPCTSTR pStrImage) + { + m_sNormalImage = pStrImage; + Invalidate(); + } + + LPCTSTR CButtonUI::GetHotImage() + { + return m_sHotImage; + } + + void CButtonUI::SetHotImage(LPCTSTR pStrImage) + { + m_sHotImage = pStrImage; + Invalidate(); + } + + LPCTSTR CButtonUI::GetPushedImage() + { + return m_sPushedImage; + } + + void CButtonUI::SetPushedImage(LPCTSTR pStrImage) + { + m_sPushedImage = pStrImage; + Invalidate(); + } + + LPCTSTR CButtonUI::GetFocusedImage() + { + return m_sFocusedImage; + } + + void CButtonUI::SetFocusedImage(LPCTSTR pStrImage) + { + m_sFocusedImage = pStrImage; + Invalidate(); + } + + LPCTSTR CButtonUI::GetDisabledImage() + { + return m_sDisabledImage; + } + + void CButtonUI::SetDisabledImage(LPCTSTR pStrImage) + { + m_sDisabledImage = pStrImage; + Invalidate(); + } + + LPCTSTR CButtonUI::GetForeImage() + { + return m_sForeImage; + } + + void CButtonUI::SetForeImage( LPCTSTR pStrImage ) + { + m_sForeImage = pStrImage; + Invalidate(); + } + + LPCTSTR CButtonUI::GetHotForeImage() + { + return m_sHotForeImage; + } + + void CButtonUI::SetHotForeImage( LPCTSTR pStrImage ) + { + m_sHotForeImage = pStrImage; + Invalidate(); + } + + void CButtonUI::BindTabIndex(int _BindTabIndex ) + { + if( _BindTabIndex >= 0) + m_iBindTabIndex = _BindTabIndex; + } + + void CButtonUI::BindTabLayoutName( LPCTSTR _TabLayoutName ) + { + if(_TabLayoutName) + m_sBindTabLayoutName = _TabLayoutName; + } + + void CButtonUI::BindTriggerTabSel( int _SetSelectIndex /*= -1*/ ) + { + if(GetBindTabLayoutName().IsEmpty() || (GetBindTabLayoutIndex() < 0 && _SetSelectIndex < 0)) + return; + + CTabLayoutUI* pTabLayout = static_cast(GetManager()->FindControl(GetBindTabLayoutName())); + if(!pTabLayout) + return; + + pTabLayout->SelectItem(_SetSelectIndex >=0?_SetSelectIndex:GetBindTabLayoutIndex()); + } + + void CButtonUI::RemoveBindTabIndex() + { + m_iBindTabIndex = -1; + m_sBindTabLayoutName.Empty(); + } + + int CButtonUI::GetBindTabLayoutIndex() + { + return m_iBindTabIndex; + } + + CDuiString CButtonUI::GetBindTabLayoutName() + { + return m_sBindTabLayoutName; + } + + SIZE CButtonUI::EstimateSize(SIZE szAvailable) + { + if( m_cxyFixed.cy == 0 ) return CDuiSize(m_cxyFixed.cx, m_pManager->GetFontInfo(GetFont())->tm.tmHeight + 8); + return CControlUI::EstimateSize(szAvailable); + } + + void CButtonUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if( _tcscmp(pstrName, _T("normalimage")) == 0 ) SetNormalImage(pstrValue); + else if( _tcscmp(pstrName, _T("hotimage")) == 0 ) SetHotImage(pstrValue); + else if( _tcscmp(pstrName, _T("pushedimage")) == 0 ) SetPushedImage(pstrValue); + else if( _tcscmp(pstrName, _T("focusedimage")) == 0 ) SetFocusedImage(pstrValue); + else if( _tcscmp(pstrName, _T("disabledimage")) == 0 ) SetDisabledImage(pstrValue); + else if( _tcscmp(pstrName, _T("foreimage")) == 0 ) SetForeImage(pstrValue); + else if( _tcscmp(pstrName, _T("hotforeimage")) == 0 ) SetHotForeImage(pstrValue); + else if( _tcscmp(pstrName, _T("bindtabindex")) == 0 ) BindTabIndex(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("bindtablayoutname")) == 0 ) BindTabLayoutName(pstrValue); + else if( _tcscmp(pstrName, _T("hotbkcolor")) == 0 ) + { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetHotBkColor(clrColor); + } + else if( _tcscmp(pstrName, _T("hottextcolor")) == 0 ) + { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetHotTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("pushedtextcolor")) == 0 ) + { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetPushedTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("focusedtextcolor")) == 0 ) + { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetFocusedTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("multiline")) == 0 ) + { + if( _tcscmp(pstrValue, _T("true")) == 0) + { + m_uTextStyle = m_uTextStyle & ~DT_SINGLELINE; + m_uTextStyle |= DT_WORDBREAK; + } + else + { + m_uTextStyle = m_uTextStyle & ~DT_WORDBREAK; + m_uTextStyle |= DT_SINGLELINE; + } + } + else CLabelUI::SetAttribute(pstrName, pstrValue); + } + + void CButtonUI::PaintText(HDC hDC) + { + if( IsFocused() ) m_uButtonState |= UISTATE_FOCUSED; + else m_uButtonState &= ~ UISTATE_FOCUSED; + if( !IsEnabled() ) m_uButtonState |= UISTATE_DISABLED; + else m_uButtonState &= ~ UISTATE_DISABLED; + + if( m_dwTextColor == 0 ) m_dwTextColor = m_pManager->GetDefaultFontColor(); + if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor(); + + if( m_sText.IsEmpty() ) return; + int nLinks = 0; + RECT rc = m_rcItem; + rc.left += m_rcTextPadding.left; + rc.right -= m_rcTextPadding.right; + rc.top += m_rcTextPadding.top; + rc.bottom -= m_rcTextPadding.bottom; + + DWORD clrColor = IsEnabled()?m_dwTextColor:m_dwDisabledTextColor; + + if( ((m_uButtonState & UISTATE_PUSHED) != 0) && (GetPushedTextColor() != 0) ) + clrColor = GetPushedTextColor(); + else if( ((m_uButtonState & UISTATE_HOT) != 0) && (GetHotTextColor() != 0) ) + clrColor = GetHotTextColor(); + else if( ((m_uButtonState & UISTATE_FOCUSED) != 0) && (GetFocusedTextColor() != 0) ) + clrColor = GetFocusedTextColor(); + + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, clrColor, \ + NULL, NULL, nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, clrColor, \ + m_iFont, m_uTextStyle); + } + + void CButtonUI::PaintStatusImage(HDC hDC) + { + if( IsFocused() ) m_uButtonState |= UISTATE_FOCUSED; + else m_uButtonState &= ~ UISTATE_FOCUSED; + if( !IsEnabled() ) m_uButtonState |= UISTATE_DISABLED; + else m_uButtonState &= ~ UISTATE_DISABLED; + + if( (m_uButtonState & UISTATE_DISABLED) != 0 ) { + if( !m_sDisabledImage.IsEmpty() ) + { + if( !DrawImage(hDC, (LPCTSTR)m_sDisabledImage) ) m_sDisabledImage.Empty(); + else goto Label_ForeImage; + } + } + else if( (m_uButtonState & UISTATE_PUSHED) != 0 ) { + if( !m_sPushedImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sPushedImage) ){ + m_sPushedImage.Empty(); + } + if( !m_sPushedForeImage.IsEmpty() ) + { + if( !DrawImage(hDC, (LPCTSTR)m_sPushedForeImage) ) + m_sPushedForeImage.Empty(); + return; + } + else goto Label_ForeImage; + } + } + else if( (m_uButtonState & UISTATE_HOT) != 0 ) { + if( !m_sHotImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sHotImage) ){ + m_sHotImage.Empty(); + } + if( !m_sHotForeImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sHotForeImage) ) + m_sHotForeImage.Empty(); + return; + } + else goto Label_ForeImage; + } + else if(m_dwHotBkColor != 0) { + CRenderEngine::DrawColor(hDC, m_rcPaint, GetAdjustColor(m_dwHotBkColor)); + return; + } + } + else if( (m_uButtonState & UISTATE_FOCUSED) != 0 ) { + if( !m_sFocusedImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sFocusedImage) ) m_sFocusedImage.Empty(); + else goto Label_ForeImage; + } + } + + if( !m_sNormalImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sNormalImage) ) m_sNormalImage.Empty(); + else goto Label_ForeImage; + } + + if(!m_sForeImage.IsEmpty() ) + goto Label_ForeImage; + + return; + +Label_ForeImage: + if(!m_sForeImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sForeImage) ) m_sForeImage.Empty(); + } + } +} \ No newline at end of file diff --git a/DuiLib/Control/UIButton.h b/DuiLib/Control/UIButton.h new file mode 100644 index 00000000..42392159 --- /dev/null +++ b/DuiLib/Control/UIButton.h @@ -0,0 +1,79 @@ +#ifndef __UIBUTTON_H__ +#define __UIBUTTON_H__ + +#pragma once + +namespace DuiLib +{ + class UILIB_API CButtonUI : public CLabelUI + { + public: + CButtonUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + UINT GetControlFlags() const; + + bool Activate(); + void SetEnabled(bool bEnable = true); + void DoEvent(TEventUI& event); + + LPCTSTR GetNormalImage(); + void SetNormalImage(LPCTSTR pStrImage); + LPCTSTR GetHotImage(); + void SetHotImage(LPCTSTR pStrImage); + LPCTSTR GetPushedImage(); + void SetPushedImage(LPCTSTR pStrImage); + LPCTSTR GetFocusedImage(); + void SetFocusedImage(LPCTSTR pStrImage); + LPCTSTR GetDisabledImage(); + void SetDisabledImage(LPCTSTR pStrImage); + LPCTSTR GetForeImage(); + void SetForeImage(LPCTSTR pStrImage); + LPCTSTR GetHotForeImage(); + void SetHotForeImage(LPCTSTR pStrImage); + + void BindTabIndex(int _BindTabIndex); + void BindTabLayoutName(LPCTSTR _TabLayoutName); + void BindTriggerTabSel(int _SetSelectIndex = -1); + void RemoveBindTabIndex(); + int GetBindTabLayoutIndex(); + CDuiString GetBindTabLayoutName(); + + void SetHotBkColor(DWORD dwColor); + DWORD GetHotBkColor() const; + void SetHotTextColor(DWORD dwColor); + DWORD GetHotTextColor() const; + void SetPushedTextColor(DWORD dwColor); + DWORD GetPushedTextColor() const; + void SetFocusedTextColor(DWORD dwColor); + DWORD GetFocusedTextColor() const; + SIZE EstimateSize(SIZE szAvailable); + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + void PaintText(HDC hDC); + void PaintStatusImage(HDC hDC); + + protected: + UINT m_uButtonState; + + DWORD m_dwHotBkColor; + DWORD m_dwHotTextColor; + DWORD m_dwPushedTextColor; + DWORD m_dwFocusedTextColor; + + CDuiString m_sNormalImage; + CDuiString m_sHotImage; + CDuiString m_sHotForeImage; + CDuiString m_sPushedImage; + CDuiString m_sPushedForeImage; + CDuiString m_sFocusedImage; + CDuiString m_sDisabledImage; + + int m_iBindTabIndex; + CDuiString m_sBindTabLayoutName; + }; + +} // namespace DuiLib + +#endif // __UIBUTTON_H__ \ No newline at end of file diff --git a/DuiLib/Control/UICombo.cpp b/DuiLib/Control/UICombo.cpp new file mode 100644 index 00000000..486aed9d --- /dev/null +++ b/DuiLib/Control/UICombo.cpp @@ -0,0 +1,971 @@ +#include "StdAfx.h" + +namespace DuiLib { + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +class CComboWnd : public CWindowWnd, public INotifyUI +{ +public: + void Init(CComboUI* pOwner); + LPCTSTR GetWindowClassName() const; + void OnFinalMessage(HWND hWnd); + + LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + void Notify(TNotifyUI& msg) override; + + void EnsureVisible(int iIndex); + void Scroll(int dx, int dy); + +#if(_WIN32_WINNT >= 0x0501) + virtual UINT GetClassStyle() const; +#endif + +public: + CPaintManagerUI m_pm; + CComboUI* m_pOwner; + CVerticalLayoutUI* m_pLayout; + int m_iOldSel; +}; + +void CComboWnd::Notify(TNotifyUI& msg) +{ + if (msg.sType == _T("windowinit")) + { + EnsureVisible(m_iOldSel); + } +} + +void CComboWnd::Init(CComboUI* pOwner) +{ + m_pOwner = pOwner; + m_pLayout = NULL; + m_iOldSel = m_pOwner->GetCurSel(); + + // Position the popup window in absolute space + SIZE szDrop = m_pOwner->GetDropBoxSize(); + RECT rcOwner = pOwner->GetPos(); + RECT rc = rcOwner; + rc.top = rc.bottom; // leftbottomλΪ + rc.bottom = rc.top + szDrop.cy; // 㵯ڸ߶ + if( szDrop.cx > 0 ) rc.right = rc.left + szDrop.cx; // 㵯ڿ + + SIZE szAvailable = { rc.right - rc.left, rc.bottom - rc.top }; + int cyFixed = 0; + for( int it = 0; it < pOwner->GetCount(); it++ ) { + CControlUI* pControl = static_cast(pOwner->GetItemAt(it)); + if( !pControl->IsVisible() ) continue; + SIZE sz = pControl->EstimateSize(szAvailable); + cyFixed += sz.cy; + } + cyFixed += 4; // CVerticalLayoutUI ĬϵInset + rc.bottom = rc.top + MIN(cyFixed, szDrop.cy); + + ::MapWindowRect(pOwner->GetManager()->GetPaintWindow(), HWND_DESKTOP, &rc); + + MONITORINFO oMonitor = {}; + oMonitor.cbSize = sizeof(oMonitor); + ::GetMonitorInfo(::MonitorFromWindow(*this, MONITOR_DEFAULTTOPRIMARY), &oMonitor); + CDuiRect rcWork = oMonitor.rcWork; + if( rc.bottom > rcWork.bottom ) { + rc.left = rcOwner.left; + rc.right = rcOwner.right; + if( szDrop.cx > 0 ) rc.right = rc.left + szDrop.cx; + rc.top = rcOwner.top - MIN(cyFixed, szDrop.cy); + rc.bottom = rcOwner.top; + ::MapWindowRect(pOwner->GetManager()->GetPaintWindow(), HWND_DESKTOP, &rc); + } + + Create(pOwner->GetManager()->GetPaintWindow(), NULL, WS_POPUP, WS_EX_TOOLWINDOW, rc); + // HACK: Don't deselect the parent's caption + HWND hWndParent = m_hWnd; + while( ::GetParent(hWndParent) != NULL ) hWndParent = ::GetParent(hWndParent); + ::ShowWindow(m_hWnd, SW_SHOW); + ::SendMessage(hWndParent, WM_NCACTIVATE, TRUE, 0L); +} + +LPCTSTR CComboWnd::GetWindowClassName() const +{ + return _T("ComboWnd"); +} + +void CComboWnd::OnFinalMessage(HWND hWnd) +{ + m_pOwner->m_pWindow = NULL; + m_pOwner->m_uButtonState &= ~ UISTATE_PUSHED; + m_pOwner->Invalidate(); + delete this; +} + +LRESULT CComboWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if( uMsg == WM_CREATE ) { + m_pm.Init(m_hWnd); + // The trick is to add the items to the new container. Their owner gets + // reassigned by this operation - which is why it is important to reassign + // the items back to the righfull owner/manager when the window closes. + m_pLayout = new CVerticalLayoutUI; + m_pm.UseParentResource(m_pOwner->GetManager()); + m_pLayout->SetManager(&m_pm, NULL, true); + LPCTSTR pDefaultAttributes = m_pOwner->GetManager()->GetDefaultAttributeList(_T("VerticalLayout")); + if( pDefaultAttributes ) { + m_pLayout->ApplyAttributeList(pDefaultAttributes); + } + m_pLayout->SetInset(CDuiRect(1, 1, 1, 1)); + m_pLayout->SetBkColor(0xFFFFFFFF); + m_pLayout->SetBorderColor(0xFFC6C7D2); + m_pLayout->SetBorderSize(1); + m_pLayout->SetAutoDestroy(false); + m_pLayout->EnableScrollBar(); + m_pLayout->ApplyAttributeList(m_pOwner->GetDropBoxAttributeList()); + for( int i = 0; i < m_pOwner->GetCount(); i++ ) { + m_pLayout->Add(static_cast(m_pOwner->GetItemAt(i))); + } + m_pm.AttachDialog(m_pLayout); + m_pm.AddNotifier(this); + return 0; + } + else if( uMsg == WM_CLOSE ) { + m_pOwner->SetManager(m_pOwner->GetManager(), m_pOwner->GetParent(), false); + m_pOwner->SetPos(m_pOwner->GetPos()); + m_pOwner->SetFocus(); + } + else if( uMsg == WM_LBUTTONUP ) { + POINT pt = { 0 }; + ::GetCursorPos(&pt); + ::ScreenToClient(m_pm.GetPaintWindow(), &pt); + CControlUI* pControl = m_pm.FindControl(pt); + if( pControl && _tcscmp(pControl->GetClass(), _T("ScrollBarUI")) != 0 ) PostMessage(WM_KILLFOCUS); + } + else if( uMsg == WM_KEYDOWN ) { + switch( wParam ) { + case VK_ESCAPE: + m_pOwner->SelectItem(m_iOldSel, true); + EnsureVisible(m_iOldSel); + // FALL THROUGH... + case VK_RETURN: + PostMessage(WM_KILLFOCUS); + break; + default: + TEventUI event; + event.Type = UIEVENT_KEYDOWN; + event.chKey = (TCHAR)wParam; + m_pOwner->DoEvent(event); + EnsureVisible(m_pOwner->GetCurSel()); + return 0; + } + } + else if( uMsg == WM_MOUSEWHEEL ) { + int zDelta = (int) (short) HIWORD(wParam); + TEventUI event = { 0 }; + event.Type = UIEVENT_SCROLLWHEEL; + event.wParam = MAKELPARAM(zDelta < 0 ? SB_LINEDOWN : SB_LINEUP, 0); + event.lParam = lParam; + event.dwTimestamp = ::GetTickCount(); + m_pOwner->DoEvent(event); + EnsureVisible(m_pOwner->GetCurSel()); + return 0; + } + else if( uMsg == WM_KILLFOCUS ) { + if( m_hWnd != (HWND) wParam ) PostMessage(WM_CLOSE); + } + + LRESULT lRes = 0; + if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes; + return CWindowWnd::HandleMessage(uMsg, wParam, lParam); +} + +void CComboWnd::EnsureVisible(int iIndex) +{ + if( m_pOwner->GetCurSel() < 0 ) return; + m_pLayout->FindSelectable(m_pOwner->GetCurSel(), false); + RECT rcItem = m_pLayout->GetItemAt(iIndex)->GetPos(); + RECT rcList = m_pLayout->GetPos(); + CScrollBarUI* pHorizontalScrollBar = m_pLayout->GetHorizontalScrollBar(); + if( pHorizontalScrollBar && pHorizontalScrollBar->IsVisible() ) rcList.bottom -= pHorizontalScrollBar->GetFixedHeight(); + int iPos = m_pLayout->GetScrollPos().cy; + if( rcItem.top >= rcList.top && rcItem.bottom < rcList.bottom ) return; + int dx = 0; + if( rcItem.top < rcList.top ) dx = rcItem.top - rcList.top; + if( rcItem.bottom > rcList.bottom ) dx = rcItem.bottom - rcList.bottom; + Scroll(0, dx); +} + +void CComboWnd::Scroll(int dx, int dy) +{ + if( dx == 0 && dy == 0 ) return; + SIZE sz = m_pLayout->GetScrollPos(); + m_pLayout->SetScrollPos(CDuiSize(sz.cx + dx, sz.cy + dy)); +} + +#if(_WIN32_WINNT >= 0x0501) +UINT CComboWnd::GetClassStyle() const +{ + return __super::GetClassStyle() | CS_DROPSHADOW; +} +#endif +//////////////////////////////////////////////////////// + + +CComboUI::CComboUI() : m_pWindow(NULL), m_iCurSel(-1), m_uButtonState(0) +{ + m_szDropBox = CDuiSize(0, 150); + ::ZeroMemory(&m_rcTextPadding, sizeof(m_rcTextPadding)); + + m_ListInfo.nColumns = 0; + m_ListInfo.nFont = -1; + m_ListInfo.uTextStyle = DT_VCENTER; + m_ListInfo.dwTextColor = 0xFF000000; + m_ListInfo.dwBkColor = 0; + m_ListInfo.bAlternateBk = false; + m_ListInfo.dwSelectedTextColor = 0xFF000000; + m_ListInfo.dwSelectedBkColor = 0xFFC1E3FF; + m_ListInfo.dwHotTextColor = 0xFF000000; + m_ListInfo.dwHotBkColor = 0xFFE9F5FF; + m_ListInfo.dwDisabledTextColor = 0xFFCCCCCC; + m_ListInfo.dwDisabledBkColor = 0xFFFFFFFF; + m_ListInfo.dwLineColor = 0; + m_ListInfo.bShowHtml = false; + m_ListInfo.bMultiExpandable = false; + ::ZeroMemory(&m_ListInfo.rcTextPadding, sizeof(m_ListInfo.rcTextPadding)); + ::ZeroMemory(&m_ListInfo.rcColumn, sizeof(m_ListInfo.rcColumn)); +} + +LPCTSTR CComboUI::GetClass() const +{ + return _T("ComboUI"); +} + +LPVOID CComboUI::GetInterface(LPCTSTR pstrName) +{ + if( _tcscmp(pstrName, DUI_CTR_COMBO) == 0 ) return static_cast(this); + if( _tcscmp(pstrName, _T("IListOwner")) == 0 ) return static_cast(this); + return CContainerUI::GetInterface(pstrName); +} + +UINT CComboUI::GetControlFlags() const +{ + return UIFLAG_TABSTOP; +} + +void CComboUI::DoInit() +{ +} + +int CComboUI::GetCurSel() const +{ + return m_iCurSel; +} + +bool CComboUI::SelectItem(int iIndex, bool bTakeFocus) +{ + if( iIndex == m_iCurSel ) return true; + int iOldSel = m_iCurSel; + if( m_iCurSel >= 0 ) { + CControlUI* pControl = static_cast(m_items[m_iCurSel]); + if( !pControl ) return false; + IListItemUI* pListItem = static_cast(pControl->GetInterface(_T("ListItem"))); + if( pListItem != NULL ) pListItem->Select(false); + m_iCurSel = -1; + } + if( iIndex < 0 ) return false; + if( m_items.GetSize() == 0 ) return false; + if( iIndex >= m_items.GetSize() ) iIndex = m_items.GetSize() - 1; + CControlUI* pControl = static_cast(m_items[iIndex]); + if( !pControl || !pControl->IsVisible() || !pControl->IsEnabled() ) return false; + IListItemUI* pListItem = static_cast(pControl->GetInterface(_T("ListItem"))); + if( pListItem == NULL ) return false; + m_iCurSel = iIndex; + if( m_pWindow != NULL || bTakeFocus ) pControl->SetFocus(); + pListItem->Select(true); + if( m_pManager != NULL ) m_pManager->SendNotify(this, DUI_MSGTYPE_ITEMSELECT, m_iCurSel, iOldSel); + Invalidate(); + + return true; +} + +bool CComboUI::SetItemIndex(CControlUI* pControl, int iIndex) +{ + int iOrginIndex = GetItemIndex(pControl); + if( iOrginIndex == -1 ) return false; + if( iOrginIndex == iIndex ) return true; + + IListItemUI* pSelectedListItem = NULL; + if( m_iCurSel >= 0 ) pSelectedListItem = + static_cast(GetItemAt(m_iCurSel)->GetInterface(_T("ListItem"))); + if( !CContainerUI::SetItemIndex(pControl, iIndex) ) return false; + int iMinIndex = min(iOrginIndex, iIndex); + int iMaxIndex = max(iOrginIndex, iIndex); + for(int i = iMinIndex; i < iMaxIndex + 1; ++i) { + CControlUI* p = GetItemAt(i); + IListItemUI* pListItem = static_cast(p->GetInterface(_T("ListItem"))); + if( pListItem != NULL ) { + pListItem->SetIndex(i); + } + } + if( m_iCurSel >= 0 && pSelectedListItem != NULL ) m_iCurSel = pSelectedListItem->GetIndex(); + return true; +} + +bool CComboUI::Add(CControlUI* pControl) +{ + IListItemUI* pListItem = static_cast(pControl->GetInterface(_T("ListItem"))); + if( pListItem != NULL ) + { + pListItem->SetOwner(this); + pListItem->SetIndex(m_items.GetSize()); + } + return CContainerUI::Add(pControl); +} + +bool CComboUI::AddAt(CControlUI* pControl, int iIndex) +{ + if (!CContainerUI::AddAt(pControl, iIndex)) return false; + + // The list items should know about us + IListItemUI* pListItem = static_cast(pControl->GetInterface(_T("ListItem"))); + if( pListItem != NULL ) { + pListItem->SetOwner(this); + pListItem->SetIndex(iIndex); + } + + for(int i = iIndex + 1; i < GetCount(); ++i) { + CControlUI* p = GetItemAt(i); + pListItem = static_cast(p->GetInterface(_T("ListItem"))); + if( pListItem != NULL ) { + pListItem->SetIndex(i); + } + } + if( m_iCurSel >= iIndex ) m_iCurSel += 1; + return true; +} + +bool CComboUI::Remove(CControlUI* pControl) +{ + int iIndex = GetItemIndex(pControl); + if (iIndex == -1) return false; + + if (!CContainerUI::RemoveAt(iIndex)) return false; + + for(int i = iIndex; i < GetCount(); ++i) { + CControlUI* p = GetItemAt(i); + IListItemUI* pListItem = static_cast(p->GetInterface(_T("ListItem"))); + if( pListItem != NULL ) { + pListItem->SetIndex(i); + } + } + + if( iIndex == m_iCurSel && m_iCurSel >= 0 ) { + int iSel = m_iCurSel; + m_iCurSel = -1; + SelectItem(FindSelectable(iSel, false)); + } + else if( iIndex < m_iCurSel ) m_iCurSel -= 1; + return true; +} + +bool CComboUI::RemoveAt(int iIndex) +{ + if (!CContainerUI::RemoveAt(iIndex)) return false; + + for(int i = iIndex; i < GetCount(); ++i) { + CControlUI* p = GetItemAt(i); + IListItemUI* pListItem = static_cast(p->GetInterface(_T("ListItem"))); + if( pListItem != NULL ) pListItem->SetIndex(i); + } + + if( iIndex == m_iCurSel && m_iCurSel >= 0 ) { + int iSel = m_iCurSel; + m_iCurSel = -1; + SelectItem(FindSelectable(iSel, false)); + } + else if( iIndex < m_iCurSel ) m_iCurSel -= 1; + return true; +} + +void CComboUI::RemoveAll() +{ + m_iCurSel = -1; + CContainerUI::RemoveAll(); +} + +void CComboUI::DoEvent(TEventUI& event) +{ + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pParent != NULL ) m_pParent->DoEvent(event); + else CContainerUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_SETFOCUS ) + { + Invalidate(); + } + if( event.Type == UIEVENT_KILLFOCUS ) + { + Invalidate(); + } + if( event.Type == UIEVENT_BUTTONDOWN ) + { + if( IsEnabled() ) { + Activate(); + m_uButtonState |= UISTATE_PUSHED | UISTATE_CAPTURED; + } + return; + } + if( event.Type == UIEVENT_BUTTONUP ) + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { + m_uButtonState &= ~ UISTATE_CAPTURED; + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_MOUSEMOVE ) + { + return; + } + if( event.Type == UIEVENT_KEYDOWN ) + { + switch( event.chKey ) { + case VK_F4: + Activate(); + return; + case VK_UP: + SelectItem(FindSelectable(m_iCurSel - 1, false)); + return; + case VK_DOWN: + SelectItem(FindSelectable(m_iCurSel + 1, true)); + return; + case VK_PRIOR: + SelectItem(FindSelectable(m_iCurSel - 1, false)); + return; + case VK_NEXT: + SelectItem(FindSelectable(m_iCurSel + 1, true)); + return; + case VK_HOME: + SelectItem(FindSelectable(0, false)); + return; + case VK_END: + SelectItem(FindSelectable(GetCount() - 1, true)); + return; + } + } + if( event.Type == UIEVENT_SCROLLWHEEL ) + { + bool bDownward = LOWORD(event.wParam) == SB_LINEDOWN; + SelectItem(FindSelectable(m_iCurSel + (bDownward ? 1 : -1), bDownward)); + return; + } + if( event.Type == UIEVENT_CONTEXTMENU ) + { + return; + } + if( event.Type == UIEVENT_MOUSEENTER ) + { + if( ::PtInRect(&m_rcItem, event.ptMouse ) ) { + if( (m_uButtonState & UISTATE_HOT) == 0 ) + m_uButtonState |= UISTATE_HOT; + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_MOUSELEAVE ) + { + if( (m_uButtonState & UISTATE_HOT) != 0 ) { + m_uButtonState &= ~UISTATE_HOT; + Invalidate(); + } + return; + } + CControlUI::DoEvent(event); +} + +SIZE CComboUI::EstimateSize(SIZE szAvailable) +{ + if( m_cxyFixed.cy == 0 ) return CDuiSize(m_cxyFixed.cx, m_pManager->GetDefaultFontInfo()->tm.tmHeight + 12); + return CControlUI::EstimateSize(szAvailable); +} + +bool CComboUI::Activate() +{ + if( !CControlUI::Activate() ) return false; + if( m_pWindow ) return true; + m_pWindow = new CComboWnd(); + ASSERT(m_pWindow); + m_pWindow->Init(this); + if( m_pManager != NULL ) m_pManager->SendNotify(this, DUI_MSGTYPE_DROPDOWN); + Invalidate(); + return true; +} + +CDuiString CComboUI::GetText() const +{ + if( m_iCurSel < 0 ) return _T(""); + CControlUI* pControl = static_cast(m_items[m_iCurSel]); + return pControl->GetText(); +} + +void CComboUI::SetEnabled(bool bEnable) +{ + CContainerUI::SetEnabled(bEnable); + if( !IsEnabled() ) m_uButtonState = 0; +} + +CDuiString CComboUI::GetDropBoxAttributeList() +{ + return m_sDropBoxAttributes; +} + +void CComboUI::SetDropBoxAttributeList(LPCTSTR pstrList) +{ + m_sDropBoxAttributes = pstrList; +} + +SIZE CComboUI::GetDropBoxSize() const +{ + return m_szDropBox; +} + +void CComboUI::SetDropBoxSize(SIZE szDropBox) +{ + m_szDropBox = szDropBox; +} + +RECT CComboUI::GetTextPadding() const +{ + return m_rcTextPadding; +} + +void CComboUI::SetTextPadding(RECT rc) +{ + m_rcTextPadding = rc; + Invalidate(); +} + +LPCTSTR CComboUI::GetNormalImage() const +{ + return m_sNormalImage; +} + +void CComboUI::SetNormalImage(LPCTSTR pStrImage) +{ + m_sNormalImage = pStrImage; + Invalidate(); +} + +LPCTSTR CComboUI::GetHotImage() const +{ + return m_sHotImage; +} + +void CComboUI::SetHotImage(LPCTSTR pStrImage) +{ + m_sHotImage = pStrImage; + Invalidate(); +} + +LPCTSTR CComboUI::GetPushedImage() const +{ + return m_sPushedImage; +} + +void CComboUI::SetPushedImage(LPCTSTR pStrImage) +{ + m_sPushedImage = pStrImage; + Invalidate(); +} + +LPCTSTR CComboUI::GetFocusedImage() const +{ + return m_sFocusedImage; +} + +void CComboUI::SetFocusedImage(LPCTSTR pStrImage) +{ + m_sFocusedImage = pStrImage; + Invalidate(); +} + +LPCTSTR CComboUI::GetDisabledImage() const +{ + return m_sDisabledImage; +} + +void CComboUI::SetDisabledImage(LPCTSTR pStrImage) +{ + m_sDisabledImage = pStrImage; + Invalidate(); +} + +TListInfoUI* CComboUI::GetListInfo() +{ + return &m_ListInfo; +} + +void CComboUI::SetItemFont(int index) +{ + m_ListInfo.nFont = index; + Invalidate(); +} + +void CComboUI::SetItemTextStyle(UINT uStyle) +{ + m_ListInfo.uTextStyle = uStyle; + Invalidate(); +} + +RECT CComboUI::GetItemTextPadding() const +{ + return m_ListInfo.rcTextPadding; +} + +void CComboUI::SetItemTextPadding(RECT rc) +{ + m_ListInfo.rcTextPadding = rc; + Invalidate(); +} + +void CComboUI::SetItemTextColor(DWORD dwTextColor) +{ + m_ListInfo.dwTextColor = dwTextColor; + Invalidate(); +} + +void CComboUI::SetItemBkColor(DWORD dwBkColor) +{ + m_ListInfo.dwBkColor = dwBkColor; +} + +void CComboUI::SetItemBkImage(LPCTSTR pStrImage) +{ + m_ListInfo.sBkImage = pStrImage; +} + +DWORD CComboUI::GetItemTextColor() const +{ + return m_ListInfo.dwTextColor; +} + +DWORD CComboUI::GetItemBkColor() const +{ + return m_ListInfo.dwBkColor; +} + +LPCTSTR CComboUI::GetItemBkImage() const +{ + return m_ListInfo.sBkImage; +} + +bool CComboUI::IsAlternateBk() const +{ + return m_ListInfo.bAlternateBk; +} + +void CComboUI::SetAlternateBk(bool bAlternateBk) +{ + m_ListInfo.bAlternateBk = bAlternateBk; +} + +void CComboUI::SetSelectedItemTextColor(DWORD dwTextColor) +{ + m_ListInfo.dwSelectedTextColor = dwTextColor; +} + +void CComboUI::SetSelectedItemBkColor(DWORD dwBkColor) +{ + m_ListInfo.dwSelectedBkColor = dwBkColor; +} + +void CComboUI::SetSelectedItemImage(LPCTSTR pStrImage) +{ + m_ListInfo.sSelectedImage = pStrImage; +} + +DWORD CComboUI::GetSelectedItemTextColor() const +{ + return m_ListInfo.dwSelectedTextColor; +} + +DWORD CComboUI::GetSelectedItemBkColor() const +{ + return m_ListInfo.dwSelectedBkColor; +} + +LPCTSTR CComboUI::GetSelectedItemImage() const +{ + return m_ListInfo.sSelectedImage; +} + +void CComboUI::SetHotItemTextColor(DWORD dwTextColor) +{ + m_ListInfo.dwHotTextColor = dwTextColor; +} + +void CComboUI::SetHotItemBkColor(DWORD dwBkColor) +{ + m_ListInfo.dwHotBkColor = dwBkColor; +} + +void CComboUI::SetHotItemImage(LPCTSTR pStrImage) +{ + m_ListInfo.sHotImage = pStrImage; +} + +DWORD CComboUI::GetHotItemTextColor() const +{ + return m_ListInfo.dwHotTextColor; +} +DWORD CComboUI::GetHotItemBkColor() const +{ + return m_ListInfo.dwHotBkColor; +} + +LPCTSTR CComboUI::GetHotItemImage() const +{ + return m_ListInfo.sHotImage; +} + +void CComboUI::SetDisabledItemTextColor(DWORD dwTextColor) +{ + m_ListInfo.dwDisabledTextColor = dwTextColor; +} + +void CComboUI::SetDisabledItemBkColor(DWORD dwBkColor) +{ + m_ListInfo.dwDisabledBkColor = dwBkColor; +} + +void CComboUI::SetDisabledItemImage(LPCTSTR pStrImage) +{ + m_ListInfo.sDisabledImage = pStrImage; +} + +DWORD CComboUI::GetDisabledItemTextColor() const +{ + return m_ListInfo.dwDisabledTextColor; +} + +DWORD CComboUI::GetDisabledItemBkColor() const +{ + return m_ListInfo.dwDisabledBkColor; +} + +LPCTSTR CComboUI::GetDisabledItemImage() const +{ + return m_ListInfo.sDisabledImage; +} + +DWORD CComboUI::GetItemLineColor() const +{ + return m_ListInfo.dwLineColor; +} + +void CComboUI::SetItemLineColor(DWORD dwLineColor) +{ + m_ListInfo.dwLineColor = dwLineColor; +} + +bool CComboUI::IsItemShowHtml() +{ + return m_ListInfo.bShowHtml; +} + +void CComboUI::SetItemShowHtml(bool bShowHtml) +{ + if( m_ListInfo.bShowHtml == bShowHtml ) return; + + m_ListInfo.bShowHtml = bShowHtml; + Invalidate(); +} + +void CComboUI::SetPos(RECT rc) +{ + // Put all elements out of sight + RECT rcNull = { 0 }; + for( int i = 0; i < m_items.GetSize(); i++ ) static_cast(m_items[i])->SetPos(rcNull); + // Position this control + CControlUI::SetPos(rc); +} + +void CComboUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) +{ + if( _tcscmp(pstrName, _T("textpadding")) == 0 ) { + RECT rcTextPadding = { 0 }; + LPTSTR pstr = NULL; + rcTextPadding.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + rcTextPadding.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcTextPadding.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcTextPadding.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetTextPadding(rcTextPadding); + } + else if( _tcscmp(pstrName, _T("normalimage")) == 0 ) SetNormalImage(pstrValue); + else if( _tcscmp(pstrName, _T("hotimage")) == 0 ) SetHotImage(pstrValue); + else if( _tcscmp(pstrName, _T("pushedimage")) == 0 ) SetPushedImage(pstrValue); + else if( _tcscmp(pstrName, _T("focusedimage")) == 0 ) SetFocusedImage(pstrValue); + else if( _tcscmp(pstrName, _T("disabledimage")) == 0 ) SetDisabledImage(pstrValue); + else if( _tcscmp(pstrName, _T("dropbox")) == 0 ) SetDropBoxAttributeList(pstrValue); + else if( _tcscmp(pstrName, _T("dropboxsize")) == 0) + { + SIZE szDropBoxSize = { 0 }; + LPTSTR pstr = NULL; + szDropBoxSize.cx = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + szDropBoxSize.cy = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetDropBoxSize(szDropBoxSize); + } + else if( _tcscmp(pstrName, _T("itemfont")) == 0 ) m_ListInfo.nFont = _ttoi(pstrValue); + else if( _tcscmp(pstrName, _T("itemalign")) == 0 ) { + if( _tcsstr(pstrValue, _T("left")) != NULL ) { + m_ListInfo.uTextStyle &= ~(DT_CENTER | DT_RIGHT); + m_ListInfo.uTextStyle |= DT_LEFT; + } + if( _tcsstr(pstrValue, _T("center")) != NULL ) { + m_ListInfo.uTextStyle &= ~(DT_LEFT | DT_RIGHT); + m_ListInfo.uTextStyle |= DT_CENTER; + } + if( _tcsstr(pstrValue, _T("right")) != NULL ) { + m_ListInfo.uTextStyle &= ~(DT_LEFT | DT_CENTER); + m_ListInfo.uTextStyle |= DT_RIGHT; + } + } + else if( _tcscmp(pstrName, _T("itemtextpadding")) == 0 ) { + RECT rcTextPadding = { 0 }; + LPTSTR pstr = NULL; + rcTextPadding.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + rcTextPadding.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcTextPadding.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcTextPadding.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetItemTextPadding(rcTextPadding); + } + else if( _tcscmp(pstrName, _T("itemtextcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetItemTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itembkcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetItemBkColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itembkimage")) == 0 ) SetItemBkImage(pstrValue); + else if( _tcscmp(pstrName, _T("itemaltbk")) == 0 ) SetAlternateBk(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("itemselectedtextcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetSelectedItemTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itemselectedbkcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetSelectedItemBkColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itemselectedimage")) == 0 ) SetSelectedItemImage(pstrValue); + else if( _tcscmp(pstrName, _T("itemhottextcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetHotItemTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itemhotbkcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetHotItemBkColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itemhotimage")) == 0 ) SetHotItemImage(pstrValue); + else if( _tcscmp(pstrName, _T("itemdisabledtextcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetDisabledItemTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itemdisabledbkcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetDisabledItemBkColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itemdisabledimage")) == 0 ) SetDisabledItemImage(pstrValue); + else if( _tcscmp(pstrName, _T("itemlinecolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetItemLineColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itemshowhtml")) == 0 ) SetItemShowHtml(_tcscmp(pstrValue, _T("true")) == 0); + else CContainerUI::SetAttribute(pstrName, pstrValue); +} + +void CComboUI::DoPaint(HDC hDC, const RECT& rcPaint) +{ + CControlUI::DoPaint(hDC, rcPaint); +} + +void CComboUI::PaintStatusImage(HDC hDC) +{ + if( IsFocused() ) m_uButtonState |= UISTATE_FOCUSED; + else m_uButtonState &= ~ UISTATE_FOCUSED; + if( !IsEnabled() ) m_uButtonState |= UISTATE_DISABLED; + else m_uButtonState &= ~ UISTATE_DISABLED; + + if( (m_uButtonState & UISTATE_DISABLED) != 0 ) { + if( !m_sDisabledImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sDisabledImage) ) m_sDisabledImage.Empty(); + else return; + } + } + else if( (m_uButtonState & UISTATE_PUSHED) != 0 ) { + if( !m_sPushedImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sPushedImage) ) m_sPushedImage.Empty(); + else return; + } + } + else if( (m_uButtonState & UISTATE_HOT) != 0 ) { + if( !m_sHotImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sHotImage) ) m_sHotImage.Empty(); + else return; + } + } + else if( (m_uButtonState & UISTATE_FOCUSED) != 0 ) { + if( !m_sFocusedImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sFocusedImage) ) m_sFocusedImage.Empty(); + else return; + } + } + + if( !m_sNormalImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sNormalImage) ) m_sNormalImage.Empty(); + else return; + } +} + +void CComboUI::PaintText(HDC hDC) +{ + RECT rcText = m_rcItem; + rcText.left += m_rcTextPadding.left; + rcText.right -= m_rcTextPadding.right; + rcText.top += m_rcTextPadding.top; + rcText.bottom -= m_rcTextPadding.bottom; + + if( m_iCurSel >= 0 ) { + CControlUI* pControl = static_cast(m_items[m_iCurSel]); + IListItemUI* pElement = static_cast(pControl->GetInterface(_T("ListItem"))); + if( pElement != NULL ) { + pElement->DrawItemText(hDC, rcText); + } + else { + RECT rcOldPos = pControl->GetPos(); + pControl->SetPos(rcText); + pControl->DoPaint(hDC, rcText); + pControl->SetPos(rcOldPos); + } + } +} + +} // namespace DuiLib diff --git a/DuiLib/Control/UICombo.h b/DuiLib/Control/UICombo.h new file mode 100644 index 00000000..78b7f6cb --- /dev/null +++ b/DuiLib/Control/UICombo.h @@ -0,0 +1,122 @@ +#ifndef __UICOMBO_H__ +#define __UICOMBO_H__ + +#pragma once + +namespace DuiLib { +///////////////////////////////////////////////////////////////////////////////////// +// + +class CComboWnd; + +class UILIB_API CComboUI : public CContainerUI, public IListOwnerUI +{ + friend class CComboWnd; +public: + CComboUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + + void DoInit(); + UINT GetControlFlags() const; + + CDuiString GetText() const; + void SetEnabled(bool bEnable = true); + + CDuiString GetDropBoxAttributeList(); + void SetDropBoxAttributeList(LPCTSTR pstrList); + SIZE GetDropBoxSize() const; + void SetDropBoxSize(SIZE szDropBox); + + int GetCurSel() const; + bool SelectItem(int iIndex, bool bTakeFocus = false); + + bool SetItemIndex(CControlUI* pControl, int iIndex); + bool Add(CControlUI* pControl); + bool AddAt(CControlUI* pControl, int iIndex); + bool Remove(CControlUI* pControl); + bool RemoveAt(int iIndex); + void RemoveAll(); + + bool Activate(); + + RECT GetTextPadding() const; + void SetTextPadding(RECT rc); + LPCTSTR GetNormalImage() const; + void SetNormalImage(LPCTSTR pStrImage); + LPCTSTR GetHotImage() const; + void SetHotImage(LPCTSTR pStrImage); + LPCTSTR GetPushedImage() const; + void SetPushedImage(LPCTSTR pStrImage); + LPCTSTR GetFocusedImage() const; + void SetFocusedImage(LPCTSTR pStrImage); + LPCTSTR GetDisabledImage() const; + void SetDisabledImage(LPCTSTR pStrImage); + + TListInfoUI* GetListInfo(); + void SetItemFont(int index); + void SetItemTextStyle(UINT uStyle); + RECT GetItemTextPadding() const; + void SetItemTextPadding(RECT rc); + DWORD GetItemTextColor() const; + void SetItemTextColor(DWORD dwTextColor); + DWORD GetItemBkColor() const; + void SetItemBkColor(DWORD dwBkColor); + LPCTSTR GetItemBkImage() const; + void SetItemBkImage(LPCTSTR pStrImage); + bool IsAlternateBk() const; + void SetAlternateBk(bool bAlternateBk); + DWORD GetSelectedItemTextColor() const; + void SetSelectedItemTextColor(DWORD dwTextColor); + DWORD GetSelectedItemBkColor() const; + void SetSelectedItemBkColor(DWORD dwBkColor); + LPCTSTR GetSelectedItemImage() const; + void SetSelectedItemImage(LPCTSTR pStrImage); + DWORD GetHotItemTextColor() const; + void SetHotItemTextColor(DWORD dwTextColor); + DWORD GetHotItemBkColor() const; + void SetHotItemBkColor(DWORD dwBkColor); + LPCTSTR GetHotItemImage() const; + void SetHotItemImage(LPCTSTR pStrImage); + DWORD GetDisabledItemTextColor() const; + void SetDisabledItemTextColor(DWORD dwTextColor); + DWORD GetDisabledItemBkColor() const; + void SetDisabledItemBkColor(DWORD dwBkColor); + LPCTSTR GetDisabledItemImage() const; + void SetDisabledItemImage(LPCTSTR pStrImage); + DWORD GetItemLineColor() const; + void SetItemLineColor(DWORD dwLineColor); + bool IsItemShowHtml(); + void SetItemShowHtml(bool bShowHtml = true); + + SIZE EstimateSize(SIZE szAvailable); + void SetPos(RECT rc); + void DoEvent(TEventUI& event); + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + void DoPaint(HDC hDC, const RECT& rcPaint); + void PaintText(HDC hDC); + void PaintStatusImage(HDC hDC); + +protected: + CComboWnd* m_pWindow; + + int m_iCurSel; + RECT m_rcTextPadding; + CDuiString m_sDropBoxAttributes; + SIZE m_szDropBox; + UINT m_uButtonState; + + CDuiString m_sNormalImage; + CDuiString m_sHotImage; + CDuiString m_sPushedImage; + CDuiString m_sFocusedImage; + CDuiString m_sDisabledImage; + + TListInfoUI m_ListInfo; +}; + +} // namespace DuiLib + +#endif // __UICOMBO_H__ diff --git a/DuiLib/Control/UIComboBox.cpp b/DuiLib/Control/UIComboBox.cpp new file mode 100644 index 00000000..129a1154 --- /dev/null +++ b/DuiLib/Control/UIComboBox.cpp @@ -0,0 +1,108 @@ +#include "stdafx.h" +#include "UIComboBox.h" + +namespace DuiLib +{ + CComboBoxUI::CComboBoxUI() + { + m_nArrowWidth = 0; + } + + LPCTSTR CComboBoxUI::GetClass() const + { + return _T("ComboBoxUI"); + } + + void CComboBoxUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if (_tcscmp(pstrName, _T("arrowimage")) == 0) + m_sArrowImage = pstrValue; + else + CComboUI::SetAttribute(pstrName, pstrValue); + } + + void CComboBoxUI::PaintStatusImage(HDC hDC) + { + if (m_sArrowImage.IsEmpty()) + CComboUI::PaintStatusImage(hDC); + else + { + // get index + if( IsFocused() ) m_uButtonState |= UISTATE_FOCUSED; + else m_uButtonState &= ~ UISTATE_FOCUSED; + if( !IsEnabled() ) m_uButtonState |= UISTATE_DISABLED; + else m_uButtonState &= ~ UISTATE_DISABLED; + + int nIndex = 0; + if ((m_uButtonState & UISTATE_DISABLED) != 0) + nIndex = 4; + else if ((m_uButtonState & UISTATE_PUSHED) != 0) + nIndex = 2; + else if ((m_uButtonState & UISTATE_HOT) != 0) + nIndex = 1; + else if ((m_uButtonState & UISTATE_FOCUSED) != 0) + nIndex = 3; + + // make modify string + CDuiString sModify = m_sArrowImage; + + int nPos1 = sModify.Find(_T("source")); + int nPos2 = sModify.Find(_T("'"), nPos1 + 7); + if (nPos2 == -1) return; //first + int nPos3 = sModify.Find(_T("'"), nPos2 + 1); + if (nPos3 == -1) return; //second + + CDuiRect rcBmpPart; + LPTSTR lpszValue = NULL; + rcBmpPart.left = _tcstol(sModify.GetData() + nPos2 + 1, &lpszValue, 10); ASSERT(lpszValue); + rcBmpPart.top = _tcstol(lpszValue + 1, &lpszValue, 10); ASSERT(lpszValue); + rcBmpPart.right = _tcstol(lpszValue + 1, &lpszValue, 10); ASSERT(lpszValue); + rcBmpPart.bottom = _tcstol(lpszValue + 1, &lpszValue, 10); ASSERT(lpszValue); + + m_nArrowWidth = rcBmpPart.GetWidth() / 5; + rcBmpPart.left += nIndex * m_nArrowWidth; + rcBmpPart.right = rcBmpPart.left + m_nArrowWidth; + + CDuiRect rcDest(0, 0, m_rcItem.right - m_rcItem.left, m_rcItem.bottom - m_rcItem.top); + rcDest.Deflate(GetBorderSize(), GetBorderSize()); + rcDest.left = rcDest.right - m_nArrowWidth; + + CDuiString sSource = sModify.Mid(nPos1, nPos3 + 1 - nPos1); + CDuiString sReplace; + sReplace.SmallFormat(_T("source='%d,%d,%d,%d' dest='%d,%d,%d,%d'"), + rcBmpPart.left, rcBmpPart.top, rcBmpPart.right, rcBmpPart.bottom, + rcDest.left, rcDest.top, rcDest.right, rcDest.bottom); + + sModify.Replace(sSource, sReplace); + + // draw image + if (!DrawImage(hDC, m_sArrowImage, sModify)) + m_sNormalImage.Empty(); + } + } + + void CComboBoxUI::PaintText(HDC hDC) + { + RECT rcText = m_rcItem; + rcText.left += m_rcTextPadding.left; + rcText.right -= m_rcTextPadding.right; + rcText.top += m_rcTextPadding.top; + rcText.bottom -= m_rcTextPadding.bottom; + + rcText.right -= m_nArrowWidth; // add this line than CComboUI::PaintText(HDC hDC) + + if( m_iCurSel >= 0 ) { + CControlUI* pControl = static_cast(m_items[m_iCurSel]); + IListItemUI* pElement = static_cast(pControl->GetInterface(_T("ListItem"))); + if( pElement != NULL ) { + pElement->DrawItemText(hDC, rcText); + } + else { + RECT rcOldPos = pControl->GetPos(); + pControl->SetPos(rcText); + pControl->DoPaint(hDC, rcText); + pControl->SetPos(rcOldPos); + } + } + } +} diff --git a/DuiLib/Control/UIComboBox.h b/DuiLib/Control/UIComboBox.h new file mode 100644 index 00000000..50c8fc3d --- /dev/null +++ b/DuiLib/Control/UIComboBox.h @@ -0,0 +1,28 @@ +#ifndef __UICOMBOBOX_H__ +#define __UICOMBOBOX_H__ + +#pragma once + +namespace DuiLib +{ + /// չб + /// arrowimage,һͼƬƽֳ5,Normal/Hot/Pushed/Focused/Disabled(source) + /// + class UILIB_API CComboBoxUI : public CComboUI + { + public: + CComboBoxUI(); + LPCTSTR GetClass() const; + + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + void PaintText(HDC hDC); + void PaintStatusImage(HDC hDC); + + protected: + CDuiString m_sArrowImage; + int m_nArrowWidth; + }; +} + +#endif // __UICOMBOBOX_H__ diff --git a/DuiLib/Control/UIDateTime.cpp b/DuiLib/Control/UIDateTime.cpp new file mode 100644 index 00000000..ca674f55 --- /dev/null +++ b/DuiLib/Control/UIDateTime.cpp @@ -0,0 +1,293 @@ +#include "stdafx.h" +#include "UIDateTime.h" + +namespace DuiLib +{ +#define DT_NONE 0 +#define DT_UPDATE 1 +#define DT_DELETE 2 +#define DT_KEEP 3 + + class CDateTimeWnd : public CWindowWnd + { + public: + CDateTimeWnd(); + + void Init(CDateTimeUI* pOwner); + RECT CalPos(); + + LPCTSTR GetWindowClassName() const; + LPCTSTR GetSuperClassName() const; + void OnFinalMessage(HWND hWnd); + + LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + + protected: + CDateTimeUI* m_pOwner; + HBRUSH m_hBkBrush; + bool m_bInit; + }; + + CDateTimeWnd::CDateTimeWnd() : m_pOwner(NULL), m_hBkBrush(NULL), m_bInit(false) + { + } + + void CDateTimeWnd::Init(CDateTimeUI* pOwner) + { + m_pOwner = pOwner; + m_pOwner->m_nDTUpdateFlag = DT_NONE; + + if (m_hWnd == NULL) + { + RECT rcPos = CalPos(); + UINT uStyle = WS_CHILD; + Create(m_pOwner->GetManager()->GetPaintWindow(), NULL, uStyle, 0, rcPos); + SetWindowFont(m_hWnd, m_pOwner->GetManager()->GetFontInfo(m_pOwner->GetFont())->hFont, TRUE); + } + + if (m_pOwner->GetText().IsEmpty()) + ::GetLocalTime(&m_pOwner->m_sysTime); + + ::SendMessage(m_hWnd, DTM_SETSYSTEMTIME, 0, (LPARAM)&m_pOwner->m_sysTime); + ::ShowWindow(m_hWnd, SW_SHOWNOACTIVATE); + ::SetFocus(m_hWnd); + + m_bInit = true; + } + + RECT CDateTimeWnd::CalPos() + { + CDuiRect rcPos = m_pOwner->GetPos(); + return rcPos; + } + + LPCTSTR CDateTimeWnd::GetWindowClassName() const + { + return _T("DateTimeWnd"); + } + + LPCTSTR CDateTimeWnd::GetSuperClassName() const + { + return DATETIMEPICK_CLASS; + } + + void CDateTimeWnd::OnFinalMessage(HWND /*hWnd*/) + { + // Clear reference and die + if( m_hBkBrush != NULL ) ::DeleteObject(m_hBkBrush); + m_pOwner->m_pWindow = NULL; + delete this; + } + + LRESULT CDateTimeWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + LRESULT lRes = 0; + BOOL bHandled = TRUE; + + if( uMsg == WM_NOTIFY) + { + ::SetFocus(m_hWnd); + } + // 粩֪bug޸ + if( uMsg == WM_KILLFOCUS ) + { + HWND hCanlender=::FindWindow( MONTHCAL_CLASS, NULL); + if(::IsWindow(hCanlender)) + { + MCHITTESTINFO hitInfo; + memset(&hitInfo,0,sizeof(hitInfo)); + GetCursorPos(&hitInfo.pt); + ::ScreenToClient(hCanlender,&hitInfo.pt); + hitInfo.cbSize=sizeof(hitInfo); + MonthCal_HitTest(hCanlender,&hitInfo); + //һ + if(hitInfo.uHit==MCHT_TITLEBTNNEXT) + { + return 1; + } + //һ + if(hitInfo.uHit==MCHT_TITLEBTNPREV) + { + return 1; + } + } + else + { + POINT pt; + ::GetCursorPos(&pt); + RECT rcWnd; + ::GetWindowRect(m_hWnd,&rcWnd); + if( !( pt.x >= rcWnd.left && pt.x <= rcWnd.right )|| + !( pt.x >= rcWnd.top && pt.x <= rcWnd.bottom )) + { + lRes= OnKillFocus(uMsg,wParam, lParam,bHandled); + } + } + } + else if (uMsg == WM_KEYUP && (wParam == VK_DELETE || wParam == VK_BACK)) + { + LRESULT lRes = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam); + m_pOwner->m_nDTUpdateFlag = DT_DELETE; + m_pOwner->UpdateText(); + PostMessage(WM_CLOSE); + return lRes; + } + else if (uMsg == WM_KEYUP && wParam == VK_ESCAPE) + { + LRESULT lRes = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam); + m_pOwner->m_nDTUpdateFlag = DT_KEEP; + PostMessage(WM_CLOSE); + return lRes; + } + else bHandled = FALSE; + if( !bHandled ) return CWindowWnd::HandleMessage(uMsg, wParam, lParam); + return lRes; + } + + LRESULT CDateTimeWnd::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + LRESULT lRes = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam); + if (m_pOwner->m_nDTUpdateFlag == DT_NONE) + { + ::SendMessage(m_hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&m_pOwner->m_sysTime); + m_pOwner->m_nDTUpdateFlag = DT_UPDATE; + m_pOwner->UpdateText(); + } + PostMessage(WM_CLOSE); + return lRes; + } + + + ////////////////////////////////////////////////////////////////////////// + // + CDateTimeUI::CDateTimeUI() + { + ::GetLocalTime(&m_sysTime); + m_bReadOnly = false; + m_pWindow = NULL; + m_nDTUpdateFlag=DT_UPDATE; + UpdateText(); + m_nDTUpdateFlag = DT_NONE; + } + + LPCTSTR CDateTimeUI::GetClass() const + { + return _T("DateTimeUI"); + } + + LPVOID CDateTimeUI::GetInterface(LPCTSTR pstrName) + { + if( _tcscmp(pstrName, DUI_CTR_DATETIME) == 0 ) return static_cast(this); + return CLabelUI::GetInterface(pstrName); + } + + SYSTEMTIME& CDateTimeUI::GetTime() + { + return m_sysTime; + } + + void CDateTimeUI::SetTime(SYSTEMTIME* pst) + { + m_sysTime = *pst; + Invalidate(); + } + + void CDateTimeUI::SetReadOnly(bool bReadOnly) + { + m_bReadOnly = bReadOnly; + Invalidate(); + } + + bool CDateTimeUI::IsReadOnly() const + { + return m_bReadOnly; + } + + void CDateTimeUI::UpdateText() + { + if (m_nDTUpdateFlag == DT_DELETE) + SetText(_T("")); + else if (m_nDTUpdateFlag == DT_UPDATE) + { + CDuiString sText; + sText.SmallFormat(_T("%4d-%02d-%02d"), + m_sysTime.wYear, m_sysTime.wMonth, m_sysTime.wDay, m_sysTime.wHour, m_sysTime.wMinute); + SetText(sText); + } + } + + void CDateTimeUI::DoEvent(TEventUI& event) + { + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pParent != NULL ) m_pParent->DoEvent(event); + else CLabelUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_SETCURSOR && IsEnabled() ) + { + ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM))); + return; + } + if( event.Type == UIEVENT_WINDOWSIZE ) + { + if( m_pWindow != NULL ) m_pManager->SetFocusNeeded(this); + } + if( event.Type == UIEVENT_SCROLLWHEEL ) + { + if( m_pWindow != NULL ) return; + } + if( event.Type == UIEVENT_SETFOCUS && IsEnabled() ) + { + if( m_pWindow ) return; + m_pWindow = new CDateTimeWnd(); + ASSERT(m_pWindow); + m_pWindow->Init(this); + m_pWindow->ShowWindow(); + } + if( event.Type == UIEVENT_KILLFOCUS && IsEnabled() ) + { + Invalidate(); + } + if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK || event.Type == UIEVENT_RBUTTONDOWN) + { + if( IsEnabled() ) { + GetManager()->ReleaseCapture(); + if( IsFocused() && m_pWindow == NULL ) + { + m_pWindow = new CDateTimeWnd(); + ASSERT(m_pWindow); + } + if( m_pWindow != NULL ) + { + m_pWindow->Init(this); + m_pWindow->ShowWindow(); + } + } + return; + } + if( event.Type == UIEVENT_MOUSEMOVE ) + { + return; + } + if( event.Type == UIEVENT_BUTTONUP ) + { + return; + } + if( event.Type == UIEVENT_CONTEXTMENU ) + { + return; + } + if( event.Type == UIEVENT_MOUSEENTER ) + { + return; + } + if( event.Type == UIEVENT_MOUSELEAVE ) + { + return; + } + + CLabelUI::DoEvent(event); + } +} diff --git a/DuiLib/Control/UIDateTime.h b/DuiLib/Control/UIDateTime.h new file mode 100644 index 00000000..b05794d3 --- /dev/null +++ b/DuiLib/Control/UIDateTime.h @@ -0,0 +1,37 @@ +#ifndef __UIDATETIME_H__ +#define __UIDATETIME_H__ + +#pragma once + +namespace DuiLib +{ + class CDateTimeWnd; + + /// ʱѡؼ + class UILIB_API CDateTimeUI : public CLabelUI + { + friend class CDateTimeWnd; + public: + CDateTimeUI(); + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + + SYSTEMTIME& GetTime(); + void SetTime(SYSTEMTIME* pst); + + void SetReadOnly(bool bReadOnly); + bool IsReadOnly() const; + + void UpdateText(); + + void DoEvent(TEventUI& event); + + protected: + SYSTEMTIME m_sysTime; + int m_nDTUpdateFlag; + bool m_bReadOnly; + + CDateTimeWnd* m_pWindow; + }; +} +#endif // __UIDATETIME_H__ \ No newline at end of file diff --git a/DuiLib/Control/UIEdit.cpp b/DuiLib/Control/UIEdit.cpp new file mode 100644 index 00000000..a121f023 --- /dev/null +++ b/DuiLib/Control/UIEdit.cpp @@ -0,0 +1,626 @@ +#include "stdafx.h" +#include "UIEdit.h" + +namespace DuiLib +{ + class CEditWnd : public CWindowWnd + { + public: + CEditWnd(); + + void Init(CEditUI* pOwner); + RECT CalPos(); + + LPCTSTR GetWindowClassName() const; + LPCTSTR GetSuperClassName() const; + void OnFinalMessage(HWND hWnd); + + LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnEditChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + + protected: + CEditUI* m_pOwner; + HBRUSH m_hBkBrush; + bool m_bInit; + }; + + + CEditWnd::CEditWnd() : m_pOwner(NULL), m_hBkBrush(NULL), m_bInit(false) + { + } + + void CEditWnd::Init(CEditUI* pOwner) + { + m_pOwner = pOwner; + RECT rcPos = CalPos(); + UINT uStyle = 0; + if(m_pOwner->GetManager()->IsBackgroundTransparent()) + { + uStyle = WS_POPUP | ES_AUTOHSCROLL | WS_VISIBLE; + RECT rcWnd={0}; + ::GetWindowRect(m_pOwner->GetManager()->GetPaintWindow(), &rcWnd); + rcPos.left += rcWnd.left; + rcPos.right += rcWnd.left; + rcPos.top += rcWnd.top; + rcPos.bottom += rcWnd.top; + } + else + { + uStyle = WS_CHILD | ES_AUTOHSCROLL; + } + if( m_pOwner->IsPasswordMode() ) uStyle |= ES_PASSWORD; + Create(m_pOwner->GetManager()->GetPaintWindow(), NULL, uStyle, 0, rcPos); + HFONT hFont=NULL; + int iFontIndex=m_pOwner->GetFont(); + if (iFontIndex!=-1) + hFont=m_pOwner->GetManager()->GetFont(iFontIndex); + if (hFont==NULL) + hFont=m_pOwner->GetManager()->GetDefaultFontInfo()->hFont; + + SetWindowFont(m_hWnd, hFont, TRUE); + Edit_LimitText(m_hWnd, m_pOwner->GetMaxChar()); + if( m_pOwner->IsPasswordMode() ) Edit_SetPasswordChar(m_hWnd, m_pOwner->GetPasswordChar()); + Edit_SetText(m_hWnd, m_pOwner->GetText()); + Edit_SetModify(m_hWnd, FALSE); + SendMessage(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELPARAM(0, 0)); + Edit_Enable(m_hWnd, m_pOwner->IsEnabled() == true); + Edit_SetReadOnly(m_hWnd, m_pOwner->IsReadOnly() == true); + + //Styls + LONG styleValue = ::GetWindowLong(m_hWnd, GWL_STYLE); + styleValue |= pOwner->GetWindowStyls(); + ::SetWindowLong(GetHWND(), GWL_STYLE, styleValue); + ::ShowWindow(m_hWnd, SW_SHOWNOACTIVATE); + ::SetFocus(m_hWnd); + m_bInit = true; + } + + RECT CEditWnd::CalPos() + { + CDuiRect rcPos = m_pOwner->GetPos(); + RECT rcInset = m_pOwner->GetTextPadding(); + rcPos.left += rcInset.left; + rcPos.top += rcInset.top; + rcPos.right -= rcInset.right; + rcPos.bottom -= rcInset.bottom; + LONG lEditHeight = m_pOwner->GetManager()->GetFontInfo(m_pOwner->GetFont())->tm.tmHeight; + if( lEditHeight < rcPos.GetHeight() ) { + rcPos.top += (rcPos.GetHeight() - lEditHeight) / 2; + rcPos.bottom = rcPos.top + lEditHeight; + } + return rcPos; + } + + LPCTSTR CEditWnd::GetWindowClassName() const + { + return _T("EditWnd"); + } + + LPCTSTR CEditWnd::GetSuperClassName() const + { + return WC_EDIT; + } + + void CEditWnd::OnFinalMessage(HWND /*hWnd*/) + { + m_pOwner->Invalidate(); + // Clear reference and die + if( m_hBkBrush != NULL ) ::DeleteObject(m_hBkBrush); + m_pOwner->m_pWindow = NULL; + delete this; + } + + LRESULT CEditWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + LRESULT lRes = 0; + BOOL bHandled = TRUE; + if( uMsg == WM_KILLFOCUS ) lRes = OnKillFocus(uMsg, wParam, lParam, bHandled); + else if( uMsg == OCM_COMMAND ) { + if( GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE ) lRes = OnEditChanged(uMsg, wParam, lParam, bHandled); + else if( GET_WM_COMMAND_CMD(wParam, lParam) == EN_UPDATE ) { + RECT rcClient; + ::GetClientRect(m_hWnd, &rcClient); + ::InvalidateRect(m_hWnd, &rcClient, FALSE); + } + } + else if( uMsg == WM_KEYDOWN && TCHAR(wParam) == VK_RETURN ){ + + m_pOwner->GetManager()->SendNotify(m_pOwner, DUI_MSGTYPE_RETURN); + + } + else if( uMsg == OCM__BASE + WM_CTLCOLOREDIT || uMsg == OCM__BASE + WM_CTLCOLORSTATIC ) { + if( m_pOwner->GetNativeEditBkColor() == 0xFFFFFFFF ) return NULL; + ::SetBkMode((HDC)wParam, TRANSPARENT); + + DWORD dwTextColor; + if (m_pOwner->GetNativeEditTextColor() != 0x000000) + dwTextColor = m_pOwner->GetNativeEditTextColor(); + else + dwTextColor = m_pOwner->GetTextColor(); + + ::SetTextColor((HDC)wParam, RGB(GetBValue(dwTextColor),GetGValue(dwTextColor),GetRValue(dwTextColor))); + if( m_hBkBrush == NULL ) { + DWORD clrColor = m_pOwner->GetNativeEditBkColor(); + m_hBkBrush = ::CreateSolidBrush(RGB(GetBValue(clrColor), GetGValue(clrColor), GetRValue(clrColor))); + } + return (LRESULT)m_hBkBrush; + } + else bHandled = FALSE; + + if( !bHandled ) return CWindowWnd::HandleMessage(uMsg, wParam, lParam); + return lRes; + } + + LRESULT CEditWnd::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + LRESULT lRes = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam); + PostMessage(WM_CLOSE); + return lRes; + } + + LRESULT CEditWnd::OnEditChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) + { + if( !m_bInit ) return 0; + if( m_pOwner == NULL ) return 0; + // Copy text back + int cchLen = ::GetWindowTextLength(m_hWnd) + 1; + LPTSTR pstr = static_cast(_alloca(cchLen * sizeof(TCHAR))); + ASSERT(pstr); + if( pstr == NULL ) return 0; + ::GetWindowText(m_hWnd, pstr, cchLen); + m_pOwner->m_sText = pstr; + m_pOwner->GetManager()->SendNotify(m_pOwner, DUI_MSGTYPE_TEXTCHANGED); + return 0; + } + + + ///////////////////////////////////////////////////////////////////////////////////// + // + // + + CEditUI::CEditUI() : m_pWindow(NULL), m_uMaxChar(255), m_bReadOnly(false), + m_bPasswordMode(false), m_cPasswordChar(_T('*')), m_uButtonState(0), + m_dwEditbkColor(0xFFFFFFFF), m_dwEditTextColor(0x00000000), m_iWindowStyls(0),m_dwTipValueColor(0xFFBAC0C5) + { + SetTextPadding(CDuiRect(4, 3, 4, 3)); + SetBkColor(0xFFFFFFFF); + } + + LPCTSTR CEditUI::GetClass() const + { + return _T("EditUI"); + } + + LPVOID CEditUI::GetInterface(LPCTSTR pstrName) + { + if( _tcscmp(pstrName, DUI_CTR_EDIT) == 0 ) return static_cast(this); + return CLabelUI::GetInterface(pstrName); + } + + UINT CEditUI::GetControlFlags() const + { + if( !IsEnabled() ) return CControlUI::GetControlFlags(); + + return UIFLAG_SETCURSOR | UIFLAG_TABSTOP; + } + + void CEditUI::DoEvent(TEventUI& event) + { + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pParent != NULL ) m_pParent->DoEvent(event); + else CLabelUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_SETCURSOR && IsEnabled() ) + { + ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM))); + return; + } + if( event.Type == UIEVENT_WINDOWSIZE ) + { + if( m_pWindow != NULL ) m_pManager->SetFocusNeeded(this); + } + if( event.Type == UIEVENT_SCROLLWHEEL ) + { + if( m_pWindow != NULL ) return; + } + if( event.Type == UIEVENT_SETFOCUS && IsEnabled() ) + { + if( m_pWindow ) return; + m_pWindow = new CEditWnd(); + ASSERT(m_pWindow); + m_pWindow->Init(this); + Invalidate(); + } + if( event.Type == UIEVENT_KILLFOCUS && IsEnabled() ) + { + Invalidate(); + } + if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK || event.Type == UIEVENT_RBUTTONDOWN) + { + if( IsEnabled() ) { + GetManager()->ReleaseCapture(); + if( IsFocused() && m_pWindow == NULL ) + { + m_pWindow = new CEditWnd(); + ASSERT(m_pWindow); + m_pWindow->Init(this); + + if( PtInRect(&m_rcItem, event.ptMouse) ) + { + int nSize = GetWindowTextLength(*m_pWindow); + if( nSize == 0 ) + nSize = 1; + + Edit_SetSel(*m_pWindow, 0, nSize); + } + } + else if( m_pWindow != NULL ) + { +#if 1 + int nSize = GetWindowTextLength(*m_pWindow); + if( nSize == 0 ) + nSize = 1; + + Edit_SetSel(*m_pWindow, 0, nSize); +#else + POINT pt = event.ptMouse; + pt.x -= m_rcItem.left + m_rcTextPadding.left; + pt.y -= m_rcItem.top + m_rcTextPadding.top; + ::SendMessage(*m_pWindow, WM_LBUTTONDOWN, event.wParam, MAKELPARAM(pt.x, pt.y)); +#endif + } + } + return; + } + if( event.Type == UIEVENT_MOUSEMOVE ) + { + return; + } + if( event.Type == UIEVENT_BUTTONUP ) + { + return; + } + if( event.Type == UIEVENT_CONTEXTMENU ) + { + return; + } + if( event.Type == UIEVENT_MOUSEENTER ) + { + if( IsEnabled() ) { + m_uButtonState |= UISTATE_HOT; + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_MOUSELEAVE ) + { + if( IsEnabled() ) { + m_uButtonState &= ~UISTATE_HOT; + Invalidate(); + } + return; + } + CLabelUI::DoEvent(event); + } + + void CEditUI::SetEnabled(bool bEnable) + { + CControlUI::SetEnabled(bEnable); + if( !IsEnabled() ) { + m_uButtonState = 0; + } + } + + void CEditUI::SetText(LPCTSTR pstrText) + { + m_sText = pstrText; + if( m_pWindow != NULL ) Edit_SetText(*m_pWindow, m_sText); + Invalidate(); + } + + void CEditUI::SetMaxChar(UINT uMax) + { + m_uMaxChar = uMax; + if( m_pWindow != NULL ) Edit_LimitText(*m_pWindow, m_uMaxChar); + } + + UINT CEditUI::GetMaxChar() + { + return m_uMaxChar; + } + + void CEditUI::SetReadOnly(bool bReadOnly) + { + if( m_bReadOnly == bReadOnly ) return; + + m_bReadOnly = bReadOnly; + if( m_pWindow != NULL ) Edit_SetReadOnly(*m_pWindow, m_bReadOnly); + Invalidate(); + } + + bool CEditUI::IsReadOnly() const + { + return m_bReadOnly; + } + + void CEditUI::SetNumberOnly(bool bNumberOnly) + { + if( bNumberOnly ) + { + m_iWindowStyls |= ES_NUMBER; + } + else + { + m_iWindowStyls &= ~ES_NUMBER; + } + } + + bool CEditUI::IsNumberOnly() const + { + return m_iWindowStyls&ES_NUMBER ? true:false; + } + + int CEditUI::GetWindowStyls() const + { + return m_iWindowStyls; + } + + void CEditUI::SetPasswordMode(bool bPasswordMode) + { + if( m_bPasswordMode == bPasswordMode ) return; + m_bPasswordMode = bPasswordMode; + Invalidate(); + } + + bool CEditUI::IsPasswordMode() const + { + return m_bPasswordMode; + } + + void CEditUI::SetPasswordChar(TCHAR cPasswordChar) + { + if( m_cPasswordChar == cPasswordChar ) return; + m_cPasswordChar = cPasswordChar; + if( m_pWindow != NULL ) Edit_SetPasswordChar(*m_pWindow, m_cPasswordChar); + Invalidate(); + } + + TCHAR CEditUI::GetPasswordChar() const + { + return m_cPasswordChar; + } + + LPCTSTR CEditUI::GetNormalImage() + { + return m_sNormalImage; + } + + void CEditUI::SetNormalImage(LPCTSTR pStrImage) + { + m_sNormalImage = pStrImage; + Invalidate(); + } + + LPCTSTR CEditUI::GetHotImage() + { + return m_sHotImage; + } + + void CEditUI::SetHotImage(LPCTSTR pStrImage) + { + m_sHotImage = pStrImage; + Invalidate(); + } + + LPCTSTR CEditUI::GetFocusedImage() + { + return m_sFocusedImage; + } + + void CEditUI::SetFocusedImage(LPCTSTR pStrImage) + { + m_sFocusedImage = pStrImage; + Invalidate(); + } + + LPCTSTR CEditUI::GetDisabledImage() + { + return m_sDisabledImage; + } + + void CEditUI::SetDisabledImage(LPCTSTR pStrImage) + { + m_sDisabledImage = pStrImage; + Invalidate(); + } + + void CEditUI::SetNativeEditBkColor(DWORD dwBkColor) + { + m_dwEditbkColor = dwBkColor; + } + + DWORD CEditUI::GetNativeEditBkColor() const + { + return m_dwEditbkColor; + } + + void CEditUI::SetNativeEditTextColor( LPCTSTR pStrColor ) + { + if( *pStrColor == _T('#')) pStrColor = ::CharNext(pStrColor); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pStrColor, &pstr, 16); + + m_dwEditTextColor = clrColor; + } + + DWORD CEditUI::GetNativeEditTextColor() const + { + return m_dwEditTextColor; + } + + void CEditUI::SetSel(long nStartChar, long nEndChar) + { + if( m_pWindow != NULL ) Edit_SetSel(*m_pWindow, nStartChar,nEndChar); + } + + void CEditUI::SetSelAll() + { + SetSel(0,-1); + } + + void CEditUI::SetReplaceSel(LPCTSTR lpszReplace) + { + if( m_pWindow != NULL ) Edit_ReplaceSel(*m_pWindow, lpszReplace); + } + + void CEditUI::SetTipValue( LPCTSTR pStrTipValue ) + { + m_sTipValue = pStrTipValue; + } + + LPCTSTR CEditUI::GetTipValue() + { + return m_sTipValue.GetData(); + } + + void CEditUI::SetTipValueColor( LPCTSTR pStrColor ) + { + if( *pStrColor == _T('#')) pStrColor = ::CharNext(pStrColor); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pStrColor, &pstr, 16); + + m_dwTipValueColor = clrColor; + } + + DWORD CEditUI::GetTipValueColor() + { + return m_dwTipValueColor; + } + + void CEditUI::SetPos(RECT rc) + { + CControlUI::SetPos(rc); + if( m_pWindow != NULL ) { + RECT rcPos = m_pWindow->CalPos(); + ::SetWindowPos(m_pWindow->GetHWND(), NULL, rcPos.left, rcPos.top, rcPos.right - rcPos.left, + rcPos.bottom - rcPos.top, SWP_NOZORDER | SWP_NOACTIVATE); + } + } + + void CEditUI::SetVisible(bool bVisible) + { + CControlUI::SetVisible(bVisible); + if( !IsVisible() && m_pWindow != NULL ) m_pManager->SetFocus(NULL); + } + + void CEditUI::SetInternVisible(bool bVisible) + { + if( !IsVisible() && m_pWindow != NULL ) m_pManager->SetFocus(NULL); + } + + SIZE CEditUI::EstimateSize(SIZE szAvailable) + { + if( m_cxyFixed.cy == 0 ) return CDuiSize(m_cxyFixed.cx, m_pManager->GetFontInfo(GetFont())->tm.tmHeight + 6); + return CControlUI::EstimateSize(szAvailable); + } + + void CEditUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if( _tcscmp(pstrName, _T("readonly")) == 0 ) SetReadOnly(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("numberonly")) == 0 ) SetNumberOnly(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("password")) == 0 ) SetPasswordMode(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("passwordchar")) == 0 ) SetPasswordChar(*pstrValue); + else if( _tcscmp(pstrName, _T("maxchar")) == 0 ) SetMaxChar(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("normalimage")) == 0 ) SetNormalImage(pstrValue); + else if( _tcscmp(pstrName, _T("hotimage")) == 0 ) SetHotImage(pstrValue); + else if( _tcscmp(pstrName, _T("focusedimage")) == 0 ) SetFocusedImage(pstrValue); + else if( _tcscmp(pstrName, _T("disabledimage")) == 0 ) SetDisabledImage(pstrValue); + else if( _tcscmp(pstrName, _T("tipvalue")) == 0 ) SetTipValue(pstrValue); + else if( _tcscmp(pstrName, _T("tipvaluecolor")) == 0 ) SetTipValueColor(pstrValue); + else if( _tcscmp(pstrName, _T("nativetextcolor")) == 0 ) SetNativeEditTextColor(pstrValue); + else if( _tcscmp(pstrName, _T("nativebkcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetNativeEditBkColor(clrColor); + } + else CLabelUI::SetAttribute(pstrName, pstrValue); + } + + void CEditUI::PaintStatusImage(HDC hDC) + { + if( IsFocused() ) m_uButtonState |= UISTATE_FOCUSED; + else m_uButtonState &= ~ UISTATE_FOCUSED; + if( !IsEnabled() ) m_uButtonState |= UISTATE_DISABLED; + else m_uButtonState &= ~ UISTATE_DISABLED; + + if( (m_uButtonState & UISTATE_DISABLED) != 0 ) { + if( !m_sDisabledImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sDisabledImage) ) m_sDisabledImage.Empty(); + else return; + } + } + else if( (m_uButtonState & UISTATE_FOCUSED) != 0 ) { + if( !m_sFocusedImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sFocusedImage) ) m_sFocusedImage.Empty(); + else return; + } + } + else if( (m_uButtonState & UISTATE_HOT) != 0 ) { + if( !m_sHotImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sHotImage) ) m_sHotImage.Empty(); + else return; + } + } + + if( !m_sNormalImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sNormalImage) ) m_sNormalImage.Empty(); + else return; + } + } + + void CEditUI::PaintText(HDC hDC) + { + DWORD mCurTextColor = m_dwTextColor; + + if( m_dwTextColor == 0 ) mCurTextColor = m_dwTextColor = m_pManager->GetDefaultFontColor(); + if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor(); + + CDuiString sText; + if(GetText() == m_sTipValue || GetText() == _T("")) + { + mCurTextColor = m_dwTipValueColor; + sText = m_sTipValue; + } + else + { + sText = m_sText; + + if( m_bPasswordMode ) { + sText.Empty(); + LPCTSTR p = m_sText.GetData(); + while( *p != _T('\0') ) { + sText += m_cPasswordChar; + p = ::CharNext(p); + } + } + } + + RECT rc = m_rcItem; + rc.left += m_rcTextPadding.left; + rc.right -= m_rcTextPadding.right; + rc.top += m_rcTextPadding.top; + rc.bottom -= m_rcTextPadding.bottom; + if( IsEnabled() ) { + CRenderEngine::DrawText(hDC, m_pManager, rc, sText, mCurTextColor, \ + m_iFont, DT_SINGLELINE | m_uTextStyle); + } + else { + CRenderEngine::DrawText(hDC, m_pManager, rc, sText, m_dwDisabledTextColor, \ + m_iFont, DT_SINGLELINE | m_uTextStyle); + } + } +} diff --git a/DuiLib/Control/UIEdit.h b/DuiLib/Control/UIEdit.h new file mode 100644 index 00000000..03478892 --- /dev/null +++ b/DuiLib/Control/UIEdit.h @@ -0,0 +1,85 @@ +#ifndef __UIEDIT_H__ +#define __UIEDIT_H__ + +#pragma once + +namespace DuiLib +{ + class CEditWnd; + + class UILIB_API CEditUI : public CLabelUI + { + friend class CEditWnd; + public: + CEditUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + UINT GetControlFlags() const; + + void SetEnabled(bool bEnable = true); + void SetText(LPCTSTR pstrText); + void SetMaxChar(UINT uMax); + UINT GetMaxChar(); + void SetReadOnly(bool bReadOnly); + bool IsReadOnly() const; + void SetPasswordMode(bool bPasswordMode); + bool IsPasswordMode() const; + void SetPasswordChar(TCHAR cPasswordChar); + TCHAR GetPasswordChar() const; + void SetNumberOnly(bool bNumberOnly); + bool IsNumberOnly() const; + int GetWindowStyls() const; + + LPCTSTR GetNormalImage(); + void SetNormalImage(LPCTSTR pStrImage); + LPCTSTR GetHotImage(); + void SetHotImage(LPCTSTR pStrImage); + LPCTSTR GetFocusedImage(); + void SetFocusedImage(LPCTSTR pStrImage); + LPCTSTR GetDisabledImage(); + void SetDisabledImage(LPCTSTR pStrImage); + void SetNativeEditBkColor(DWORD dwBkColor); + DWORD GetNativeEditBkColor() const; + void SetNativeEditTextColor( LPCTSTR pStrColor ); + DWORD GetNativeEditTextColor() const; + + void SetSel(long nStartChar, long nEndChar); + void SetSelAll(); + void SetReplaceSel(LPCTSTR lpszReplace); + + void SetTipValue(LPCTSTR pStrTipValue); + LPCTSTR GetTipValue(); + void SetTipValueColor(LPCTSTR pStrColor); + DWORD GetTipValueColor(); + + void SetPos(RECT rc); + void SetVisible(bool bVisible = true); + void SetInternVisible(bool bVisible = true); + SIZE EstimateSize(SIZE szAvailable); + void DoEvent(TEventUI& event); + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + void PaintStatusImage(HDC hDC); + void PaintText(HDC hDC); + + protected: + CEditWnd* m_pWindow; + + UINT m_uMaxChar; + bool m_bReadOnly; + bool m_bPasswordMode; + TCHAR m_cPasswordChar; + UINT m_uButtonState; + CDuiString m_sNormalImage; + CDuiString m_sHotImage; + CDuiString m_sFocusedImage; + CDuiString m_sDisabledImage; + CDuiString m_sTipValue; + DWORD m_dwTipValueColor; + DWORD m_dwEditbkColor; + DWORD m_dwEditTextColor; + int m_iWindowStyls; + }; +} +#endif // __UIEDIT_H__ \ No newline at end of file diff --git a/DuiLib/Control/UIFlash.cpp b/DuiLib/Control/UIFlash.cpp new file mode 100644 index 00000000..66a38b2b --- /dev/null +++ b/DuiLib/Control/UIFlash.cpp @@ -0,0 +1,269 @@ +#include "stdafx.h" +#include "UIFlash.h" +#include + +#define DISPID_FLASHEVENT_FLASHCALL ( 0x00C5 ) +#define DISPID_FLASHEVENT_FSCOMMAND ( 0x0096 ) +#define DISPID_FLASHEVENT_ONPROGRESS ( 0x07A6 ) + +namespace DuiLib +{ + + CFlashUI::CFlashUI(void) + : m_dwRef(0) + , m_dwCookie(0) + , m_pFlash(NULL) + , m_pFlashEventHandler(NULL) + { + CDuiString strFlashCLSID=_T("{D27CDB6E-AE6D-11CF-96B8-444553540000}"); + OLECHAR szCLSID[100] = { 0 }; +#ifndef _UNICODE + ::MultiByteToWideChar(::GetACP(), 0, strFlashCLSID, -1, szCLSID, lengthof(szCLSID) - 1); +#else + _tcsncpy(szCLSID, strFlashCLSID, lengthof(szCLSID) - 1); +#endif + ::CLSIDFromString(szCLSID, &m_clsid); + } + + CFlashUI::~CFlashUI(void) + { + if (m_pFlashEventHandler) + { + m_pFlashEventHandler->Release(); + m_pFlashEventHandler=NULL; + } + ReleaseControl(); + } + + LPCTSTR CFlashUI::GetClass() const + { + return DUI_CTR_FLASH; + } + + LPVOID CFlashUI::GetInterface( LPCTSTR pstrName ) + { + if( _tcscmp(pstrName, DUI_CTR_FLASH) == 0 ) return static_cast(this); + return CActiveXUI::GetInterface(pstrName); + } + + HRESULT STDMETHODCALLTYPE CFlashUI::GetTypeInfoCount( __RPC__out UINT *pctinfo ) + { + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE CFlashUI::GetTypeInfo( UINT iTInfo, LCID lcid, __RPC__deref_out_opt ITypeInfo **ppTInfo ) + { + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE CFlashUI::GetIDsOfNames( __RPC__in REFIID riid, __RPC__in_ecount_full(cNames ) LPOLESTR *rgszNames, UINT cNames, LCID lcid, __RPC__out_ecount_full(cNames) DISPID *rgDispId ) + { + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE CFlashUI::Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr ) + { + + return S_OK; + switch(dispIdMember) + { + case DISPID_FLASHEVENT_FLASHCALL: + { + if (pDispParams->cArgs != 1 || pDispParams->rgvarg[0].vt != VT_BSTR) + return E_INVALIDARG; + return this->FlashCall(pDispParams->rgvarg[0].bstrVal); + } + case DISPID_FLASHEVENT_FSCOMMAND: + { + if( pDispParams && pDispParams->cArgs == 2 ) + { + if( pDispParams->rgvarg[0].vt == VT_BSTR && + pDispParams->rgvarg[1].vt == VT_BSTR ) + { + return FSCommand(pDispParams->rgvarg[1].bstrVal, pDispParams->rgvarg[0].bstrVal); + } + else + { + return DISP_E_TYPEMISMATCH; + } + } + else + { + return DISP_E_BADPARAMCOUNT; + } + } + case DISPID_FLASHEVENT_ONPROGRESS: + { + return OnProgress(*pDispParams->rgvarg[0].plVal); + } + case DISPID_READYSTATECHANGE: + { + return this->OnReadyStateChange(pDispParams->rgvarg[0].lVal); + } + } + + return S_OK; + } + + HRESULT STDMETHODCALLTYPE CFlashUI::QueryInterface( REFIID riid, void **ppvObject ) + { + *ppvObject = NULL; + + if( riid == IID_IUnknown) + *ppvObject = static_cast(this); + else if( riid == IID_IDispatch) + *ppvObject = static_cast(this); + else if( riid == __uuidof(_IShockwaveFlashEvents)) + *ppvObject = static_cast<_IShockwaveFlashEvents*>(this); + + if( *ppvObject != NULL ) + AddRef(); + return *ppvObject == NULL ? E_NOINTERFACE : S_OK; + } + + ULONG STDMETHODCALLTYPE CFlashUI::AddRef( void ) + { + ::InterlockedIncrement(&m_dwRef); + return m_dwRef; + } + + ULONG STDMETHODCALLTYPE CFlashUI::Release( void ) + { + ::InterlockedDecrement(&m_dwRef); + return m_dwRef; + } + + HRESULT CFlashUI::OnReadyStateChange (long newState) + { + if (m_pFlashEventHandler) + { + return m_pFlashEventHandler->OnReadyStateChange(newState); + } + return S_OK; + } + + HRESULT CFlashUI::OnProgress(long percentDone ) + { + if (m_pFlashEventHandler) + { + return m_pFlashEventHandler->OnProgress(percentDone); + } + return S_OK; + } + + HRESULT CFlashUI::FSCommand (_bstr_t command, _bstr_t args) + { + if (m_pFlashEventHandler) + { + return m_pFlashEventHandler->FSCommand(command,args); + } + return S_OK; + } + + HRESULT CFlashUI::FlashCall( _bstr_t request ) + { + if (m_pFlashEventHandler) + { + return m_pFlashEventHandler->FlashCall(request); + } + return S_OK; + } + + void CFlashUI::ReleaseControl() + { + //GetManager()->RemoveTranslateAccelerator(this); + RegisterEventHandler(FALSE); + if (m_pFlash) + { + m_pFlash->Release(); + m_pFlash=NULL; + } + } + + bool CFlashUI::DoCreateControl() + { + if (!CActiveXUI::DoCreateControl()) + return false; + //GetManager()->AddTranslateAccelerator(this); + GetControl(__uuidof(IShockwaveFlash),(LPVOID*)&m_pFlash); + RegisterEventHandler(TRUE); + return true; + } + + void CFlashUI::SetFlashEventHandler( CFlashEventHandler* pHandler ) + { + if (m_pFlashEventHandler!=NULL) + { + m_pFlashEventHandler->Release(); + } + if (pHandler==NULL) + { + m_pFlashEventHandler=pHandler; + return; + } + m_pFlashEventHandler=pHandler; + m_pFlashEventHandler->AddRef(); + } + + LRESULT CFlashUI::TranslateAccelerator( MSG *pMsg ) + { + if(pMsg->message < WM_KEYFIRST || pMsg->message > WM_KEYLAST) + return S_FALSE; + + if( m_pFlash == NULL ) + return E_NOTIMPL; + + // ǰWebڲǽ,ټ + BOOL bIsChild = FALSE; + HWND hTempWnd = NULL; + HWND hWndFocus = ::GetFocus(); + + hTempWnd = hWndFocus; + while(hTempWnd != NULL) + { + if(hTempWnd == m_hwndHost) + { + bIsChild = TRUE; + break; + } + hTempWnd = ::GetParent(hTempWnd); + } + if(!bIsChild) + return S_FALSE; + + CComPtr pObj; + if (FAILED(m_pFlash->QueryInterface(IID_IOleInPlaceActiveObject, (LPVOID *)&pObj))) + return S_FALSE; + + HRESULT hResult = pObj->TranslateAccelerator(pMsg); + return hResult; + } + + HRESULT CFlashUI::RegisterEventHandler( BOOL inAdvise ) + { + if (m_pFlash==NULL) + return S_FALSE; + + HRESULT hr=S_FALSE; + CComPtr pCPC; + CComPtr pCP; + + hr=m_pFlash->QueryInterface(IID_IConnectionPointContainer,(void **)&pCPC); + if (FAILED(hr)) + return hr; + hr=pCPC->FindConnectionPoint(__uuidof(_IShockwaveFlashEvents),&pCP); + if (FAILED(hr)) + return hr; + + if (inAdvise) + { + hr = pCP->Advise((IDispatch*)this, &m_dwCookie); + } + else + { + hr = pCP->Unadvise(m_dwCookie); + } + return hr; + } + +}; \ No newline at end of file diff --git a/DuiLib/Control/UIFlash.h b/DuiLib/Control/UIFlash.h new file mode 100644 index 00000000..58fb8a5c --- /dev/null +++ b/DuiLib/Control/UIFlash.h @@ -0,0 +1,65 @@ +#ifndef __UIFLASH_H__ +#define __UIFLASH_H__ +#pragma once + +// \Utils\Flash11.tlb ΪFlash11ӿļַڵͰ汾ڣʹע +// #import "PROGID:ShockwaveFlash.ShockwaveFlash" \ +// raw_interfaces_only, /* Don't add raw_ to method names */ \ +// named_guids, /* Named guids and declspecs */ \ +// rename("IDispatchEx","IMyDispatchEx") /* fix conflicting with IDispatchEx ant dispex.h */ +// using namespace ShockwaveFlashObjects; +#include "Utils/FlashEventHandler.h" +#include "Utils/flash11.tlh" + +class CActiveXCtrl; + +namespace DuiLib +{ + class UILIB_API CFlashUI + : public CActiveXUI + // , public IOleInPlaceSiteWindowless // ͸ģʽͼҪʵӿ + , public _IShockwaveFlashEvents + , public ITranslateAccelerator + { + public: + + CFlashUI(void); + ~CFlashUI(void); + + void SetFlashEventHandler(CFlashEventHandler* pHandler); + virtual bool DoCreateControl(); + IShockwaveFlash* m_pFlash; + + private: + virtual LPCTSTR GetClass() const; + virtual LPVOID GetInterface( LPCTSTR pstrName ); + + virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount( __RPC__out UINT *pctinfo ); + virtual HRESULT STDMETHODCALLTYPE GetTypeInfo( UINT iTInfo, LCID lcid, __RPC__deref_out_opt ITypeInfo **ppTInfo ); + virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames( __RPC__in REFIID riid, __RPC__in_ecount_full(cNames ) LPOLESTR *rgszNames, UINT cNames, LCID lcid, __RPC__out_ecount_full(cNames) DISPID *rgDispId); + virtual HRESULT STDMETHODCALLTYPE Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr ); + + virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void **ppvObject ); + virtual ULONG STDMETHODCALLTYPE AddRef( void ); + virtual ULONG STDMETHODCALLTYPE Release( void ); + + HRESULT OnReadyStateChange (long newState); + HRESULT OnProgress(long percentDone ); + HRESULT FSCommand (_bstr_t command, _bstr_t args); + HRESULT FlashCall (_bstr_t request ); + + virtual void ReleaseControl(); + HRESULT RegisterEventHandler(BOOL inAdvise); + + // ITranslateAccelerator + // DuilibϢַWebBrowser + virtual LRESULT TranslateAccelerator( MSG *pMsg ); + + private: + LONG m_dwRef; + DWORD m_dwCookie; + CFlashEventHandler* m_pFlashEventHandler; + }; +} + +#endif // __UIFLASH_H__ diff --git a/DuiLib/Control/UIGifAnim.cpp b/DuiLib/Control/UIGifAnim.cpp new file mode 100644 index 00000000..6673a812 --- /dev/null +++ b/DuiLib/Control/UIGifAnim.cpp @@ -0,0 +1,365 @@ +#include "StdAfx.h" +#include "UIGifAnim.h" + +/////////////////////////////////////////////////////////////////////////////////////// +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened +typedef DWORD ZRESULT; +typedef struct +{ + int index; // index of this file within the zip + char name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; +typedef struct +{ + int index; // index of this file within the zip + TCHAR name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; +#define OpenZip OpenZipU +#define CloseZip(hz) CloseZipU(hz) +extern HZIP OpenZipU(void *z,unsigned int len,DWORD flags); +extern ZRESULT CloseZipU(HZIP hz); +#ifdef _UNICODE +#define ZIPENTRY ZIPENTRYW +#define GetZipItem GetZipItemW +#define FindZipItem FindZipItemW +#else +#define GetZipItem GetZipItemA +#define FindZipItem FindZipItemA +#endif +extern ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze); +extern ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *ze); +extern ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze); +extern ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *ze); +extern ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags); +/////////////////////////////////////////////////////////////////////////////////////// + +namespace DuiLib +{ + + CGifAnimUI::CGifAnimUI(void) + { + m_pGifImage = NULL; + m_pPropertyItem = NULL; + m_nFrameCount = 0; + m_nFramePosition = 0; + m_bIsAutoPlay = true; + m_bIsAutoSize = false; + m_bIsPlaying = false; + + } + + + CGifAnimUI::~CGifAnimUI(void) + { + DeleteGif(); + m_pManager->KillTimer( this, EVENT_TIEM_ID ); + + } + + LPCTSTR CGifAnimUI::GetClass() const + { + return _T("GifAnimUI"); + } + + LPVOID CGifAnimUI::GetInterface( LPCTSTR pstrName ) + { + if( _tcscmp(pstrName, DUI_CTR_GIFANIM) == 0 ) return static_cast(this); + return CControlUI::GetInterface(pstrName); + } + + void CGifAnimUI::DoInit() + { + InitGifImage(); + } + + void CGifAnimUI::DoPaint( HDC hDC, const RECT& rcPaint ) + { + if( !::IntersectRect( &m_rcPaint, &rcPaint, &m_rcItem ) ) return; + if ( NULL == m_pGifImage ) + { + InitGifImage(); + } + DrawFrame( hDC ); + } + + void CGifAnimUI::DoEvent( TEventUI& event ) + { + if( event.Type == UIEVENT_TIMER ) + OnTimer( (UINT_PTR)event.wParam ); + } + + void CGifAnimUI::SetVisible(bool bVisible /* = true */) + { + CControlUI::SetVisible(bVisible); + if (bVisible) + PlayGif(); + else + StopGif(); + } + + void CGifAnimUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if( _tcscmp(pstrName, _T("bkimage")) == 0 ) SetBkImage(pstrValue); + else if( _tcscmp(pstrName, _T("autoplay")) == 0 ) { + SetAutoPlay(_tcscmp(pstrValue, _T("true")) == 0); + } + else if( _tcscmp(pstrName, _T("autosize")) == 0 ) { + SetAutoSize(_tcscmp(pstrValue, _T("true")) == 0); + } + else + CControlUI::SetAttribute(pstrName, pstrValue); + } + + void CGifAnimUI::SetBkImage(LPCTSTR pStrImage) + { + if( m_sBkImage == pStrImage || NULL == pStrImage) return; + + m_sBkImage = pStrImage; + + StopGif(); + DeleteGif(); + + Invalidate(); + + } + + LPCTSTR CGifAnimUI::GetBkImage() + { + return m_sBkImage.GetData(); + } + + void CGifAnimUI::SetAutoPlay(bool bIsAuto) + { + m_bIsAutoPlay = bIsAuto; + } + + bool CGifAnimUI::IsAutoPlay() const + { + return m_bIsAutoPlay; + } + + void CGifAnimUI::SetAutoSize(bool bIsAuto) + { + m_bIsAutoSize = bIsAuto; + } + + bool CGifAnimUI::IsAutoSize() const + { + return m_bIsAutoSize; + } + + void CGifAnimUI::PlayGif() + { + if (m_bIsPlaying || m_pGifImage == NULL) + { + return; + } + + long lPause = ((long*) m_pPropertyItem->value)[m_nFramePosition] * 10; + if ( lPause == 0 ) lPause = 100; + m_pManager->SetTimer( this, EVENT_TIEM_ID, lPause ); + + m_bIsPlaying = true; + } + + void CGifAnimUI::PauseGif() + { + if (!m_bIsPlaying || m_pGifImage == NULL) + { + return; + } + + m_pManager->KillTimer(this, EVENT_TIEM_ID); + this->Invalidate(); + m_bIsPlaying = false; + } + + void CGifAnimUI::StopGif() + { + if (!m_bIsPlaying) + { + return; + } + + m_pManager->KillTimer(this, EVENT_TIEM_ID); + m_nFramePosition = 0; + this->Invalidate(); + m_bIsPlaying = false; + } + + void CGifAnimUI::InitGifImage() + { + m_pGifImage = LoadGifFromFile(GetBkImage()); + if ( NULL == m_pGifImage ) return; + UINT nCount = 0; + nCount = m_pGifImage->GetFrameDimensionsCount(); + GUID* pDimensionIDs = new GUID[ nCount ]; + m_pGifImage->GetFrameDimensionsList( pDimensionIDs, nCount ); + m_nFrameCount = m_pGifImage->GetFrameCount( &pDimensionIDs[0] ); + int nSize = m_pGifImage->GetPropertyItemSize( PropertyTagFrameDelay ); + m_pPropertyItem = (Gdiplus::PropertyItem*) malloc( nSize ); + m_pGifImage->GetPropertyItem( PropertyTagFrameDelay, nSize, m_pPropertyItem ); + delete pDimensionIDs; + pDimensionIDs = NULL; + + if (m_bIsAutoSize) + { + SetFixedWidth(m_pGifImage->GetWidth()); + SetFixedHeight(m_pGifImage->GetHeight()); + } + if (m_bIsAutoPlay) + { + PlayGif(); + } + } + + void CGifAnimUI::DeleteGif() + { + if ( m_pGifImage != NULL ) + { + delete m_pGifImage; + m_pGifImage = NULL; + } + + if ( m_pPropertyItem != NULL ) + { + free( m_pPropertyItem ); + m_pPropertyItem = NULL; + } + m_nFrameCount = 0; + m_nFramePosition = 0; + } + + void CGifAnimUI::OnTimer( UINT_PTR idEvent ) + { + if ( idEvent != EVENT_TIEM_ID ) + return; + m_pManager->KillTimer( this, EVENT_TIEM_ID ); + this->Invalidate(); + + m_nFramePosition = (++m_nFramePosition) % m_nFrameCount; + + long lPause = ((long*) m_pPropertyItem->value)[m_nFramePosition] * 10; + if ( lPause == 0 ) lPause = 100; + m_pManager->SetTimer( this, EVENT_TIEM_ID, lPause ); + } + + void CGifAnimUI::DrawFrame( HDC hDC ) + { + if ( NULL == hDC || NULL == m_pGifImage ) return; + GUID pageGuid = Gdiplus::FrameDimensionTime; + Gdiplus::Graphics graphics( hDC ); + graphics.DrawImage( m_pGifImage, m_rcItem.left, m_rcItem.top, m_rcItem.right-m_rcItem.left, m_rcItem.bottom-m_rcItem.top ); + m_pGifImage->SelectActiveFrame( &pageGuid, m_nFramePosition ); + } + + Gdiplus::Image* CGifAnimUI::LoadGifFromFile(LPCTSTR pstrGifPath) + { + LPBYTE pData = NULL; + DWORD dwSize = 0; + + do + { + CDuiString sFile = CPaintManagerUI::GetResourcePath(); + if( CPaintManagerUI::GetResourceZip().IsEmpty() ) { + sFile += pstrGifPath; + HANDLE hFile = ::CreateFile(sFile.GetData(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, \ + FILE_ATTRIBUTE_NORMAL, NULL); + if( hFile == INVALID_HANDLE_VALUE ) break; + dwSize = ::GetFileSize(hFile, NULL); + if( dwSize == 0 ) break; + + DWORD dwRead = 0; + pData = new BYTE[ dwSize ]; + ::ReadFile( hFile, pData, dwSize, &dwRead, NULL ); + ::CloseHandle( hFile ); + + if( dwRead != dwSize ) { + delete[] pData; + pData = NULL; + break; + } + } + else { + sFile += CPaintManagerUI::GetResourceZip(); + HZIP hz = NULL; + if( CPaintManagerUI::IsCachedResourceZip() ) hz = (HZIP)CPaintManagerUI::GetResourceZipHandle(); + else hz = OpenZip((void*)sFile.GetData(), 0, 2); + if( hz == NULL ) break; + ZIPENTRY ze; + int i; + if( FindZipItem(hz, pstrGifPath, true, &i, &ze) != 0 ) break; + dwSize = ze.unc_size; + if( dwSize == 0 ) break; + pData = new BYTE[ dwSize ]; + int res = UnzipItem(hz, i, pData, dwSize, 3); + if( res != 0x00000000 && res != 0x00000600) { + delete[] pData; + pData = NULL; + if( !CPaintManagerUI::IsCachedResourceZip() ) CloseZip(hz); + break; + } + if( !CPaintManagerUI::IsCachedResourceZip() ) CloseZip(hz); + } + + } while (0); + + while (!pData) + { + //ͼƬ, ֱȥȡbitmap.m_lpstrָ· + HANDLE hFile = ::CreateFile(pstrGifPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, \ + FILE_ATTRIBUTE_NORMAL, NULL); + if( hFile == INVALID_HANDLE_VALUE ) break; + dwSize = ::GetFileSize(hFile, NULL); + if( dwSize == 0 ) break; + + DWORD dwRead = 0; + pData = new BYTE[ dwSize ]; + ::ReadFile( hFile, pData, dwSize, &dwRead, NULL ); + ::CloseHandle( hFile ); + + if( dwRead != dwSize ) { + delete[] pData; + pData = NULL; + } + break; + } + if (!pData) + { + return NULL; + } + + Gdiplus::Image* pImage = LoadGifFromMemory(pData, dwSize); + delete pData; + return pImage; + } + + Gdiplus::Image* CGifAnimUI::LoadGifFromMemory( LPVOID pBuf,size_t dwSize ) + { + HGLOBAL hMem = ::GlobalAlloc(GMEM_FIXED, dwSize); + BYTE* pMem = (BYTE*)::GlobalLock(hMem); + + memcpy(pMem, pBuf, dwSize); + + IStream* pStm = NULL; + ::CreateStreamOnHGlobal(hMem, TRUE, &pStm); + Gdiplus::Image *pImg = Gdiplus::Image::FromStream(pStm); + if(!pImg || pImg->GetLastStatus() != Gdiplus::Ok) + { + pStm->Release(); + ::GlobalUnlock(hMem); + return 0; + } + return pImg; + } + + +} \ No newline at end of file diff --git a/DuiLib/Control/UIGifAnim.h b/DuiLib/Control/UIGifAnim.h new file mode 100644 index 00000000..d6b4e8a2 --- /dev/null +++ b/DuiLib/Control/UIGifAnim.h @@ -0,0 +1,57 @@ +#ifndef GifAnimUI_h__ +#define GifAnimUI_h__ + +#pragma once + +namespace DuiLib +{ + class CControl; + +#define EVENT_TIEM_ID 100 + + class UILIB_API CGifAnimUI : public CControlUI + { + public: + CGifAnimUI(void); + ~CGifAnimUI(void); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + void DoInit() override; + void DoPaint(HDC hDC, const RECT& rcPaint) override; + void DoEvent(TEventUI& event) override; + void SetVisible(bool bVisible = true ) override; + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) override; + void SetBkImage(LPCTSTR pStrImage); + LPCTSTR GetBkImage(); + + void SetAutoPlay(bool bIsAuto = true ); + bool IsAutoPlay() const; + void SetAutoSize(bool bIsAuto = true ); + bool IsAutoSize() const; + void PlayGif(); + void PauseGif(); + void StopGif(); + + private: + void InitGifImage(); + void DeleteGif(); + void OnTimer( UINT_PTR idEvent ); + void DrawFrame( HDC hDC ); // GIFÿ֡ + Gdiplus::Image* LoadGifFromFile(LPCTSTR pstrGifPath); + Gdiplus::Image* LoadGifFromMemory( LPVOID pBuf,size_t dwSize ); + private: + Gdiplus::Image *m_pGifImage; + UINT m_nFrameCount; // gifͼƬ֡ + UINT m_nFramePosition; // ǰŵڼ֡ + Gdiplus::PropertyItem* m_pPropertyItem; // ֮֡֡ʱ + + CDuiString m_sBkImage; + bool m_bIsAutoPlay; // ǷԶgif + bool m_bIsAutoSize; // ǷԶͼƬôС + bool m_bIsPlaying; + + }; +} + +#endif // GifAnimUI_h__ diff --git a/DuiLib/Control/UILabel.cpp b/DuiLib/Control/UILabel.cpp new file mode 100644 index 00000000..48e38277 --- /dev/null +++ b/DuiLib/Control/UILabel.cpp @@ -0,0 +1,590 @@ +#include "StdAfx.h" +#include "UILabel.h" + +#include +namespace DuiLib +{ + + //Color _MakeRGB(int a, Color cl) + //{ + // return Color(a, cl.GetR(), cl.GetG(), cl.GetB()); + //} + + //Color _MakeRGB(int r, int g, int b) + //{ + // return Color(255, r, g, b); + //} + + CLabelUI::CLabelUI() : m_uTextStyle(DT_VCENTER), m_dwTextColor(0), + m_dwDisabledTextColor(0), + m_iFont(-1), + m_bShowHtml(false), + + m_EnableEffect(false), + m_gdiplusToken(0), + //m_TextRenderingHintAntiAlias(TextRenderingHintSystemDefault), + m_TransShadow(60), + m_TransText(168), + m_TransShadow1(60), + m_TransText1(168), + m_hAlign(DT_LEFT), + m_vAlign(DT_CENTER), + m_dwTextColor1(-1), + m_dwTextShadowColorA(0xff000000), + m_dwTextShadowColorB(-1), + m_GradientAngle(0), + m_EnabledStroke(false), + m_TransStroke(255), + m_dwStrokeColor(0), + m_bAutoCalcWidth(false), + m_EnabledShadow(false), + m_GradientLength(0) + { + //m_ShadowOffset.X = 0.0f; + //m_ShadowOffset.Y = 0.0f; + //m_ShadowOffset.Width = 0.0f; + //m_ShadowOffset.Height = 0.0f; + //GdiplusStartup( &m_gdiplusToken,&m_gdiplusStartupInput, NULL); + + ::ZeroMemory(&m_rcTextPadding, sizeof(m_rcTextPadding)); + } + + CLabelUI::~CLabelUI() + { + // GdiplusShutdown( m_gdiplusToken ); + } + + LPCTSTR CLabelUI::GetClass() const + { + return _T("LabelUI"); + } + + LPVOID CLabelUI::GetInterface(LPCTSTR pstrName) + { + if( _tcscmp(pstrName, _T("Label")) == 0 ) return static_cast(this); + return CControlUI::GetInterface(pstrName); + } + + void CLabelUI::SetTextStyle(UINT uStyle) + { + m_uTextStyle = uStyle; + Invalidate(); + } + + UINT CLabelUI::GetTextStyle() const + { + return m_uTextStyle; + } + + void CLabelUI::SetTextColor(DWORD dwTextColor) + { + m_dwTextColor = dwTextColor; + Invalidate(); + } + + DWORD CLabelUI::GetTextColor() const + { + return m_dwTextColor; + } + + void CLabelUI::SetDisabledTextColor(DWORD dwTextColor) + { + m_dwDisabledTextColor = dwTextColor; + Invalidate(); + } + + DWORD CLabelUI::GetDisabledTextColor() const + { + return m_dwDisabledTextColor; + } + + void CLabelUI::SetFont(int index) + { + m_iFont = index; + Invalidate(); + } + + int CLabelUI::GetFont() const + { + return m_iFont; + } + + RECT CLabelUI::GetTextPadding() const + { + return m_rcTextPadding; + } + + void CLabelUI::SetTextPadding(RECT rc) + { + m_rcTextPadding = rc; + Invalidate(); + } + + bool CLabelUI::IsShowHtml() + { + return m_bShowHtml; + } + + void CLabelUI::SetShowHtml(bool bShowHtml) + { + if( m_bShowHtml == bShowHtml ) return; + + m_bShowHtml = bShowHtml; + Invalidate(); + } + + SIZE CLabelUI::EstimateSize(SIZE szAvailable) + { + if (m_bAutoCalcWidth) + { + RECT rcText = {0}; + + CRenderEngine::DrawText(m_pManager->GetPaintDC(), m_pManager, rcText, m_sText, m_dwTextColor, m_iFont, DT_CALCRECT | m_uTextStyle); + m_cxyFixed.cx = rcText.right - rcText.left + m_rcTextPadding.left + m_rcTextPadding.right; + } + + if( m_cxyFixed.cy == 0 ) return CDuiSize(m_cxyFixed.cx, m_pManager->GetFontInfo(GetFont())->tm.tmHeight + 4); + return CControlUI::EstimateSize(szAvailable); + } + + void CLabelUI::DoEvent(TEventUI& event) + { + if( event.Type == UIEVENT_SETFOCUS ) + { + m_bFocused = true; + return; + } + if( event.Type == UIEVENT_KILLFOCUS ) + { + m_bFocused = false; + return; + } + if( event.Type == UIEVENT_MOUSEENTER ) + { + // return; + } + if( event.Type == UIEVENT_MOUSELEAVE ) + { + // return; + } + CControlUI::DoEvent(event); + } + + void CLabelUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if( _tcscmp(pstrName, _T("align")) == 0 ) { + if( _tcsstr(pstrValue, _T("left")) != NULL ) { + m_uTextStyle &= ~(DT_CENTER | DT_RIGHT | DT_SINGLELINE); + m_uTextStyle |= DT_LEFT; + } + if( _tcsstr(pstrValue, _T("center")) != NULL ) { + m_uTextStyle &= ~(DT_LEFT | DT_RIGHT ); + m_uTextStyle |= DT_CENTER; + } + if( _tcsstr(pstrValue, _T("right")) != NULL ) { + m_uTextStyle &= ~(DT_LEFT | DT_CENTER | DT_SINGLELINE); + m_uTextStyle |= DT_RIGHT; + } + } + else if( _tcscmp(pstrName, _T("valign")) == 0 ) { + if( _tcsstr(pstrValue, _T("top")) != NULL ) { + m_uTextStyle &= ~(DT_BOTTOM | DT_VCENTER); + m_uTextStyle |= (DT_TOP | DT_SINGLELINE); + } + if( _tcsstr(pstrValue, _T("vcenter")) != NULL ) { + m_uTextStyle &= ~(DT_TOP | DT_BOTTOM ); + m_uTextStyle |= (DT_VCENTER | DT_SINGLELINE); + } + if( _tcsstr(pstrValue, _T("bottom")) != NULL ) { + m_uTextStyle &= ~(DT_TOP | DT_VCENTER); + m_uTextStyle |= (DT_BOTTOM | DT_SINGLELINE); + } + } + else if( _tcscmp(pstrName, _T("endellipsis")) == 0 ) { + if( _tcscmp(pstrValue, _T("true")) == 0 ) m_uTextStyle |= DT_END_ELLIPSIS; + else m_uTextStyle &= ~DT_END_ELLIPSIS; + } + else if( _tcscmp(pstrName, _T("font")) == 0 ) SetFont(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("textcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("disabledtextcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetDisabledTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("textpadding")) == 0 ) { + RECT rcTextPadding = { 0 }; + LPTSTR pstr = NULL; + rcTextPadding.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + rcTextPadding.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcTextPadding.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcTextPadding.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetTextPadding(rcTextPadding); + } + else if( _tcscmp(pstrName, _T("showhtml")) == 0 ) SetShowHtml(_tcscmp(pstrValue, _T("true")) == 0); + + else if( _tcscmp(pstrName, _T("enabledeffect")) == 0 ) SetEnabledEffect(_tcscmp(pstrValue, _T("true")) == 0); + //else if( _tcscmp(pstrName, _T("rhaa")) == 0 ) SetTextRenderingHintAntiAlias(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("transshadow")) == 0 ) SetTransShadow(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("transtext")) == 0 ) SetTransText(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("transshadow1")) == 0 ) SetTransShadow1(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("transtext1")) == 0 ) SetTransText1(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("gradientangle")) == 0 ) SetGradientAngle(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("enabledstroke")) == 0 ) SetEnabledStroke(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("enabledshadow")) == 0 ) SetEnabledShadow(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("transstroke")) == 0 ) SetTransStroke(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("gradientlength")) == 0 ) SetGradientLength(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("shadowoffset")) == 0 ){ + LPTSTR pstr = NULL; + int offsetx = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + int offsety = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetShadowOffset(offsetx,offsety); + } + else if( _tcscmp(pstrName, _T("textcolor1")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetTextColor1(clrColor); + } + else if( _tcscmp(pstrName, _T("textshadowcolora")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetTextShadowColorA(clrColor); + } + else if( _tcscmp(pstrName, _T("textshadowcolorb")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetTextShadowColorB(clrColor); + } + else if( _tcscmp(pstrName, _T("strokecolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetStrokeColor(clrColor); + } + else if( _tcscmp(pstrName, _T("autocalcwidth")) == 0 ) { + SetAutoCalcWidth(_tcscmp(pstrValue, _T("true")) == 0); + } + else CControlUI::SetAttribute(pstrName, pstrValue); + } + + void CLabelUI::PaintText(HDC hDC) + { + if( m_dwTextColor == 0 ) m_dwTextColor = m_pManager->GetDefaultFontColor(); + if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor(); + + RECT rc = m_rcItem; + rc.left += m_rcTextPadding.left; + rc.right -= m_rcTextPadding.right; + rc.top += m_rcTextPadding.top; + rc.bottom -= m_rcTextPadding.bottom; + + if(!GetEnabledEffect()) + { + if( m_sText.IsEmpty() ) return; + int nLinks = 0; + if( IsEnabled() ) { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + NULL, NULL, nLinks, DT_SINGLELINE | m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + m_iFont, DT_SINGLELINE | m_uTextStyle); + } + else { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + NULL, NULL, nLinks, DT_SINGLELINE | m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + m_iFont, DT_SINGLELINE | m_uTextStyle); + } + } + else + { + //ʹGDI+ʹЧ + ASSERT(FALSE); + } + // else + // { + // Font nFont(hDC,m_pManager->GetFont(GetFont())); + // + // Graphics nGraphics(hDC); + // nGraphics.SetTextRenderingHint(m_TextRenderingHintAntiAlias); + // + // StringFormat format; + // format.SetAlignment((StringAlignment)m_hAlign); + // format.SetLineAlignment((StringAlignment)m_vAlign); + // + // RectF nRc((float)rc.left,(float)rc.top,(float)rc.right-rc.left,(float)rc.bottom-rc.top); + // RectF nShadowRc = nRc; + // nShadowRc.X += m_ShadowOffset.X; + // nShadowRc.Y += m_ShadowOffset.Y; + // + // int nGradientLength = GetGradientLength(); + // + // if(nGradientLength == 0) + // nGradientLength = (rc.bottom-rc.top); + // + // LinearGradientBrush nLineGrBrushA(Point(GetGradientAngle(), 0),Point(0,nGradientLength),_MakeRGB(GetTransShadow(),GetTextShadowColorA()),_MakeRGB(GetTransShadow1(),GetTextShadowColorB() == -1?GetTextShadowColorA():GetTextShadowColorB())); + // LinearGradientBrush nLineGrBrushB(Point(GetGradientAngle(), 0),Point(0,nGradientLength),_MakeRGB(GetTransText(),GetTextColor()),_MakeRGB(GetTransText1(),GetTextColor1() == -1?GetTextColor():GetTextColor1())); + // + // if(GetEnabledStroke() && GetStrokeColor() > 0) + // { + // LinearGradientBrush nLineGrBrushStroke(Point(GetGradientAngle(),0),Point(0,rc.bottom-rc.top+2),_MakeRGB(GetTransStroke(),GetStrokeColor()),_MakeRGB(GetTransStroke(),GetStrokeColor())); + // + //#ifdef _UNICODE + // nRc.Offset(-1,0); + // nGraphics.DrawString(m_TextValue,m_TextValue.GetLength(),&nFont,nRc,&format,&nLineGrBrushStroke); + // nRc.Offset(2,0); + // nGraphics.DrawString(m_TextValue,m_TextValue.GetLength(),&nFont,nRc,&format,&nLineGrBrushStroke); + // nRc.Offset(-1,-1); + // nGraphics.DrawString(m_TextValue,m_TextValue.GetLength(),&nFont,nRc,&format,&nLineGrBrushStroke); + // nRc.Offset(0,2); + // nGraphics.DrawString(m_TextValue,m_TextValue.GetLength(),&nFont,nRc,&format,&nLineGrBrushStroke); + // nRc.Offset(0,-1); + //#else + // USES_CONVERSION; + // wstring mTextValue = A2W(m_TextValue.GetData()); + // + // nRc.Offset(-1,0); + // nGraphics.DrawString(mTextValue.c_str(),mTextValue.length(),&nFont,nRc,&format,&nLineGrBrushStroke); + // nRc.Offset(2,0); + // nGraphics.DrawString(mTextValue.c_str(),mTextValue.length(),&nFont,nRc,&format,&nLineGrBrushStroke); + // nRc.Offset(-1,-1); + // nGraphics.DrawString(mTextValue.c_str(),mTextValue.length(),&nFont,nRc,&format,&nLineGrBrushStroke); + // nRc.Offset(0,2); + // nGraphics.DrawString(mTextValue.c_str(),mTextValue.length(),&nFont,nRc,&format,&nLineGrBrushStroke); + // nRc.Offset(0,-1); + //#endif + // + // } + //#ifdef _UNICODE + // if(GetEnabledShadow() && (GetTextShadowColorA() > 0 || GetTextShadowColorB() > 0)) + // nGraphics.DrawString(m_TextValue,m_TextValue.GetLength(),&nFont,nShadowRc,&format,&nLineGrBrushA); + // + // nGraphics.DrawString(m_TextValue,m_TextValue.GetLength(),&nFont,nRc,&format,&nLineGrBrushB); + //#else + // USES_CONVERSION; + // wstring mTextValue = A2W(m_TextValue.GetData()); + // + // if(GetEnabledShadow() && (GetTextShadowColorA() > 0 || GetTextShadowColorB() > 0)) + // nGraphics.DrawString(mTextValue.c_str(),mTextValue.length(),&nFont,nShadowRc,&format,&nLineGrBrushA); + // + // nGraphics.DrawString(mTextValue.c_str(),mTextValue.length(),&nFont,nRc,&format,&nLineGrBrushB); + //#endif + // + // } + } + + bool CLabelUI::GetAutoCalcWidth() const + { + return m_bAutoCalcWidth; + } + + void CLabelUI::SetAutoCalcWidth(bool bAutoCalcWidth) + { + m_bAutoCalcWidth = bAutoCalcWidth; + } + + void CLabelUI::SetTransShadow( int _TransShadow ) + { + m_TransShadow = _TransShadow; + } + + int CLabelUI::GetTransShadow() + { + return m_TransShadow; + } + + //void CLabelUI::SetTextRenderingHintAntiAlias( int _TextRenderingHintAntiAlias ) + //{ + // if(_TextRenderingHintAntiAlias < 0 || _TextRenderingHintAntiAlias > 5) + // _TextRenderingHintAntiAlias = 0; + // m_TextRenderingHintAntiAlias = (TextRenderingHint)_TextRenderingHintAntiAlias; + //} + + //int CLabelUI::GetTextRenderingHintAntiAlias() + //{ + // return m_TextRenderingHintAntiAlias; + //} + + void CLabelUI::SetShadowOffset( int _offset,int _angle ) + { + if(_angle > 180 || _angle < -180) + return; + + RECT rc = m_rcItem; + + if(_angle >= 0 && _angle <= 180) + rc.top -= _offset; + else if(_angle > -180 && _angle < 0) + rc.top += _offset; + + if(_angle > -90 && _angle <= 90) + rc.left -= _offset; + else if( _angle > 90 || _angle < -90) + rc.left += _offset; + + //m_ShadowOffset.X = (float)rc.top; + //m_ShadowOffset.Y = (float)rc.left; + } + + //RectF CLabelUI::GetShadowOffset() + //{ + // return m_ShadowOffset; + //} + + void CLabelUI::SetText( LPCTSTR pstrText ) + { + if(!GetEnabledEffect()) + return CControlUI::SetText(pstrText); + + m_TextValue = pstrText; + } + + CDuiString CLabelUI::GetText() const + { + if(!m_EnableEffect) + return CControlUI::GetText(); + return m_TextValue; + } + + void CLabelUI::SetEnabledEffect( bool _EnabledEffect ) + { + m_EnableEffect = _EnabledEffect; + } + + bool CLabelUI::GetEnabledEffect() + { + return m_EnableEffect; + } + + void CLabelUI::SetTextColor1( DWORD _TextColor1 ) + { + m_dwTextColor1 = _TextColor1; + } + + DWORD CLabelUI::GetTextColor1() + { + return m_dwTextColor1; + } + + void CLabelUI::SetTextShadowColorA( DWORD _TextShadowColorA ) + { + m_dwTextShadowColorA = _TextShadowColorA; + } + + DWORD CLabelUI::GetTextShadowColorA() + { + return m_dwTextShadowColorA; + } + + void CLabelUI::SetTextShadowColorB( DWORD _TextShadowColorB ) + { + m_dwTextShadowColorB = _TextShadowColorB; + } + + DWORD CLabelUI::GetTextShadowColorB() + { + return m_dwTextShadowColorB; + } + + void CLabelUI::SetTransText( int _TransText ) + { + m_TransText = _TransText; + } + + int CLabelUI::GetTransText() + { + return m_TransText; + } + + void CLabelUI::SetTransShadow1( int _TransShadow ) + { + m_TransShadow1 = _TransShadow; + } + + int CLabelUI::GetTransShadow1() + { + return m_TransShadow1; + } + + void CLabelUI::SetTransText1( int _TransText ) + { + m_TransText1 = _TransText; + } + + int CLabelUI::GetTransText1() + { + return m_TransText1; + } + + void CLabelUI::SetGradientAngle( int _SetGradientAngle ) + { + m_GradientAngle = _SetGradientAngle; + } + + int CLabelUI::GetGradientAngle() + { + return m_GradientAngle; + } + + void CLabelUI::SetEnabledStroke( bool _EnabledStroke ) + { + m_EnabledStroke = _EnabledStroke; + } + + bool CLabelUI::GetEnabledStroke() + { + return m_EnabledStroke; + } + + void CLabelUI::SetTransStroke( int _TransStroke ) + { + m_TransStroke = _TransStroke; + } + + int CLabelUI::GetTransStroke() + { + return m_TransStroke; + } + + void CLabelUI::SetStrokeColor( DWORD _StrokeColor ) + { + m_dwStrokeColor = _StrokeColor; + } + + DWORD CLabelUI::GetStrokeColor() + { + return m_dwStrokeColor; + } + + void CLabelUI::SetEnabledShadow( bool _EnabledShadowe ) + { + m_EnabledShadow = _EnabledShadowe; + } + + bool CLabelUI::GetEnabledShadow() + { + return m_EnabledShadow; + } + + void CLabelUI::SetGradientLength( int _GradientLength ) + { + m_GradientLength = _GradientLength; + } + + int CLabelUI::GetGradientLength() + { + return m_GradientLength; + } + +} \ No newline at end of file diff --git a/DuiLib/Control/UILabel.h b/DuiLib/Control/UILabel.h new file mode 100644 index 00000000..d1548f1e --- /dev/null +++ b/DuiLib/Control/UILabel.h @@ -0,0 +1,103 @@ +#ifndef __UILABEL_H__ +#define __UILABEL_H__ + +#pragma once + +namespace DuiLib +{ + class UILIB_API CLabelUI : public CControlUI + { + public: + CLabelUI(); + ~CLabelUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + + void SetTextStyle(UINT uStyle); + UINT GetTextStyle() const; + void SetTextColor(DWORD dwTextColor); + DWORD GetTextColor() const; + void SetDisabledTextColor(DWORD dwTextColor); + DWORD GetDisabledTextColor() const; + void SetFont(int index); + int GetFont() const; + RECT GetTextPadding() const; + void SetTextPadding(RECT rc); + bool IsShowHtml(); + void SetShowHtml(bool bShowHtml = true); + + SIZE EstimateSize(SIZE szAvailable); + void DoEvent(TEventUI& event); + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + void PaintText(HDC hDC); + + virtual bool GetAutoCalcWidth() const; + virtual void SetAutoCalcWidth(bool bAutoCalcWidth); + + void SetEnabledEffect(bool _EnabledEffect); + bool GetEnabledEffect(); + void SetText(LPCTSTR pstrText); + CDuiString GetText() const; + void SetTransShadow(int _TransShadow); + int GetTransShadow(); + void SetTransShadow1(int _TransShadow); + int GetTransShadow1(); + void SetTransText(int _TransText); + int GetTransText(); + void SetTransText1(int _TransText); + int GetTransText1(); + void SetTransStroke(int _TransStroke); + int GetTransStroke(); + void SetGradientLength(int _GradientLength); + int GetGradientLength(); + void SetShadowOffset(int _offset,int _angle); + void SetTextColor1(DWORD _TextColor1); + DWORD GetTextColor1(); + void SetTextShadowColorA(DWORD _TextShadowColorA); + DWORD GetTextShadowColorA(); + void SetTextShadowColorB(DWORD _TextShadowColorB); + DWORD GetTextShadowColorB(); + void SetStrokeColor(DWORD _StrokeColor); + DWORD GetStrokeColor(); + void SetGradientAngle(int _SetGradientAngle); + int GetGradientAngle(); + void SetEnabledStroke(bool _EnabledStroke); + bool GetEnabledStroke(); + void SetEnabledShadow(bool _EnabledShadowe); + bool GetEnabledShadow(); + + protected: + DWORD m_dwTextColor; + DWORD m_dwDisabledTextColor; + int m_iFont; + UINT m_uTextStyle; + RECT m_rcTextPadding; + bool m_bShowHtml; + bool m_bAutoCalcWidth; + + int m_hAlign; + int m_vAlign; + int m_TransShadow; + int m_TransShadow1; + int m_TransText; + int m_TransText1; + int m_TransStroke; + int m_GradientLength; + int m_GradientAngle; + bool m_EnableEffect; + bool m_EnabledStroke; + bool m_EnabledShadow; + DWORD m_dwTextColor1; + DWORD m_dwTextShadowColorA; + DWORD m_dwTextShadowColorB; + DWORD m_dwStrokeColor; + + CDuiString m_TextValue; + ULONG_PTR m_gdiplusToken; + + }; +} + +#endif // __UILABEL_H__ \ No newline at end of file diff --git a/DuiLib/Control/UIList.cpp b/DuiLib/Control/UIList.cpp new file mode 100644 index 00000000..a851ea3e --- /dev/null +++ b/DuiLib/Control/UIList.cpp @@ -0,0 +1,2593 @@ +#include "StdAfx.h" + +namespace DuiLib { + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +CListUI::CListUI() : m_pCallback(NULL), m_bScrollSelect(false), m_iCurSel(-1), m_iExpandedItem(-1) +{ + m_pList = new CListBodyUI(this); + m_pHeader = new CListHeaderUI; + + Add(m_pHeader); + CVerticalLayoutUI::Add(m_pList); + + m_ListInfo.nColumns = 0; + m_ListInfo.nFont = -1; + m_ListInfo.uTextStyle = DT_VCENTER; // m_uTextStyle(DT_VCENTER | DT_END_ELLIPSIS) + m_ListInfo.dwTextColor = 0xFF000000; + m_ListInfo.dwBkColor = 0; + m_ListInfo.bAlternateBk = false; + m_ListInfo.dwSelectedTextColor = 0xFF000000; + m_ListInfo.dwSelectedBkColor = 0xFFC1E3FF; + m_ListInfo.dwHotTextColor = 0xFF000000; + m_ListInfo.dwHotBkColor = 0xFFE9F5FF; + m_ListInfo.dwDisabledTextColor = 0xFFCCCCCC; + m_ListInfo.dwDisabledBkColor = 0xFFFFFFFF; + m_ListInfo.dwLineColor = 0; + m_ListInfo.bShowHtml = false; + m_ListInfo.bMultiExpandable = false; + ::ZeroMemory(&m_ListInfo.rcTextPadding, sizeof(m_ListInfo.rcTextPadding)); + ::ZeroMemory(&m_ListInfo.rcColumn, sizeof(m_ListInfo.rcColumn)); +} + +LPCTSTR CListUI::GetClass() const +{ + return _T("ListUI"); +} + +UINT CListUI::GetControlFlags() const +{ + return UIFLAG_TABSTOP; +} + +LPVOID CListUI::GetInterface(LPCTSTR pstrName) +{ + if( _tcscmp(pstrName, DUI_CTR_LIST) == 0 ) return static_cast(this); + if( _tcscmp(pstrName, _T("IList")) == 0 ) return static_cast(this); + if( _tcscmp(pstrName, _T("IListOwner")) == 0 ) return static_cast(this); + return CVerticalLayoutUI::GetInterface(pstrName); +} + +CControlUI* CListUI::GetItemAt(int iIndex) const +{ + return m_pList->GetItemAt(iIndex); +} + +int CListUI::GetItemIndex(CControlUI* pControl) const +{ + if( pControl->GetInterface(_T("ListHeader")) != NULL ) return CVerticalLayoutUI::GetItemIndex(pControl); + // We also need to recognize header sub-items + if( _tcsstr(pControl->GetClass(), _T("ListHeaderItemUI")) != NULL ) return m_pHeader->GetItemIndex(pControl); + + return m_pList->GetItemIndex(pControl); +} + +bool CListUI::SetItemIndex(CControlUI* pControl, int iIndex) +{ + if( pControl->GetInterface(_T("ListHeader")) != NULL ) return CVerticalLayoutUI::SetItemIndex(pControl, iIndex); + // We also need to recognize header sub-items + if( _tcsstr(pControl->GetClass(), _T("ListHeaderItemUI")) != NULL ) return m_pHeader->SetItemIndex(pControl, iIndex); + + int iOrginIndex = m_pList->GetItemIndex(pControl); + if( iOrginIndex == -1 ) return false; + if( iOrginIndex == iIndex ) return true; + + IListItemUI* pSelectedListItem = NULL; + if( m_iCurSel >= 0 ) pSelectedListItem = + static_cast(GetItemAt(m_iCurSel)->GetInterface(_T("ListItem"))); + if( !m_pList->SetItemIndex(pControl, iIndex) ) return false; + int iMinIndex = min(iOrginIndex, iIndex); + int iMaxIndex = max(iOrginIndex, iIndex); + for(int i = iMinIndex; i < iMaxIndex + 1; ++i) { + CControlUI* p = m_pList->GetItemAt(i); + IListItemUI* pListItem = static_cast(p->GetInterface(_T("ListItem"))); + if( pListItem != NULL ) { + pListItem->SetIndex(i); + } + } + if( m_iCurSel >= 0 && pSelectedListItem != NULL ) m_iCurSel = pSelectedListItem->GetIndex(); + return true; +} + +int CListUI::GetCount() const +{ + return m_pList->GetCount(); +} + +bool CListUI::Add(CControlUI* pControl) +{ + // Override the Add() method so we can add items specifically to + // the intended widgets. Headers are assumed to be + // answer the correct interface so we can add multiple list headers. + if( pControl->GetInterface(_T("ListHeader")) != NULL ) { + if( m_pHeader != pControl && m_pHeader->GetCount() == 0 ) { + CVerticalLayoutUI::Remove(m_pHeader); + m_pHeader = static_cast(pControl); + } + m_ListInfo.nColumns = MIN(m_pHeader->GetCount(), UILIST_MAX_COLUMNS); + return CVerticalLayoutUI::AddAt(pControl, 0); + } + // We also need to recognize header sub-items + if( _tcsstr(pControl->GetClass(), _T("ListHeaderItemUI")) != NULL ) { + bool ret = m_pHeader->Add(pControl); + m_ListInfo.nColumns = MIN(m_pHeader->GetCount(), UILIST_MAX_COLUMNS); + return ret; + } + // The list items should know about us + IListItemUI* pListItem = static_cast(pControl->GetInterface(_T("ListItem"))); + if( pListItem != NULL ) { + pListItem->SetOwner(this); + pListItem->SetIndex(GetCount()); + } + return m_pList->Add(pControl); +} + +bool CListUI::AddAt(CControlUI* pControl, int iIndex) +{ + // Override the AddAt() method so we can add items specifically to + // the intended widgets. Headers and are assumed to be + // answer the correct interface so we can add multiple list headers. + if( pControl->GetInterface(_T("ListHeader")) != NULL ) { + if( m_pHeader != pControl && m_pHeader->GetCount() == 0 ) { + CVerticalLayoutUI::Remove(m_pHeader); + m_pHeader = static_cast(pControl); + } + m_ListInfo.nColumns = MIN(m_pHeader->GetCount(), UILIST_MAX_COLUMNS); + return CVerticalLayoutUI::AddAt(pControl, 0); + } + // We also need to recognize header sub-items + if( _tcsstr(pControl->GetClass(), _T("ListHeaderItemUI")) != NULL ) { + bool ret = m_pHeader->AddAt(pControl, iIndex); + m_ListInfo.nColumns = MIN(m_pHeader->GetCount(), UILIST_MAX_COLUMNS); + return ret; + } + if (!m_pList->AddAt(pControl, iIndex)) return false; + + // The list items should know about us + IListItemUI* pListItem = static_cast(pControl->GetInterface(_T("ListItem"))); + if( pListItem != NULL ) { + pListItem->SetOwner(this); + pListItem->SetIndex(iIndex); + } + + for(int i = iIndex + 1; i < m_pList->GetCount(); ++i) { + CControlUI* p = m_pList->GetItemAt(i); + pListItem = static_cast(p->GetInterface(_T("ListItem"))); + if( pListItem != NULL ) { + pListItem->SetIndex(i); + } + } + if( m_iCurSel >= iIndex ) m_iCurSel += 1; + return true; +} + +bool CListUI::Remove(CControlUI* pControl) +{ + if( pControl->GetInterface(_T("ListHeader")) != NULL ) return CVerticalLayoutUI::Remove(pControl); + // We also need to recognize header sub-items + if( _tcsstr(pControl->GetClass(), _T("ListHeaderItemUI")) != NULL ) return m_pHeader->Remove(pControl); + + int iIndex = m_pList->GetItemIndex(pControl); + if (iIndex == -1) return false; + + if (!m_pList->RemoveAt(iIndex)) return false; + + for(int i = iIndex; i < m_pList->GetCount(); ++i) { + CControlUI* p = m_pList->GetItemAt(i); + IListItemUI* pListItem = static_cast(p->GetInterface(_T("ListItem"))); + if( pListItem != NULL ) { + pListItem->SetIndex(i); + } + } + + if( iIndex == m_iCurSel && m_iCurSel >= 0 ) { + int iSel = m_iCurSel; + m_iCurSel = -1; + SelectItem(FindSelectable(iSel, false)); + } + else if( iIndex < m_iCurSel ) m_iCurSel -= 1; + return true; +} + +bool CListUI::RemoveAt(int iIndex) +{ + if (!m_pList->RemoveAt(iIndex)) return false; + + for(int i = iIndex; i < m_pList->GetCount(); ++i) { + CControlUI* p = m_pList->GetItemAt(i); + IListItemUI* pListItem = static_cast(p->GetInterface(_T("ListItem"))); + if( pListItem != NULL ) pListItem->SetIndex(i); + } + + if( iIndex == m_iCurSel && m_iCurSel >= 0 ) { + int iSel = m_iCurSel; + m_iCurSel = -1; + SelectItem(FindSelectable(iSel, false)); + } + else if( iIndex < m_iCurSel ) m_iCurSel -= 1; + return true; +} + +void CListUI::RemoveAll() +{ + m_iCurSel = -1; + m_iExpandedItem = -1; + m_pList->RemoveAll(); +} + +void CListUI::SetPos(RECT rc) +{ + CVerticalLayoutUI::SetPos(rc); + + if( m_pHeader == NULL ) return; + // Determine general list information and the size of header columns + m_ListInfo.nColumns = MIN(m_pHeader->GetCount(), UILIST_MAX_COLUMNS); + // The header/columns may or may not be visible at runtime. In either case + // we should determine the correct dimensions... + + if( !m_pHeader->IsVisible() ) { + for( int it = 0; it < m_pHeader->GetCount(); it++ ) { + static_cast(m_pHeader->GetItemAt(it))->SetInternVisible(true); + } + m_pHeader->SetPos(CDuiRect(rc.left, 0, rc.right, 0)); + } + int iOffset = m_pList->GetScrollPos().cx; + for( int i = 0; i < m_ListInfo.nColumns; i++ ) { + CControlUI* pControl = static_cast(m_pHeader->GetItemAt(i)); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) continue; + + RECT rcPos = pControl->GetPos(); + if( iOffset > 0 ) { + rcPos.left -= iOffset; + rcPos.right -= iOffset; + pControl->SetPos(rcPos); + } + m_ListInfo.rcColumn[i] = pControl->GetPos(); + } + if( !m_pHeader->IsVisible() ) { + for( int it = 0; it < m_pHeader->GetCount(); it++ ) { + static_cast(m_pHeader->GetItemAt(it))->SetInternVisible(false); + } + } + m_pList->SetPos(m_pList->GetPos()); + +} + +void CListUI::DoEvent(TEventUI& event) +{ + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pParent != NULL ) m_pParent->DoEvent(event); + else CVerticalLayoutUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_SETFOCUS ) + { + m_bFocused = true; + return; + } + if( event.Type == UIEVENT_KILLFOCUS ) + { + m_bFocused = false; + return; + } + + switch( event.Type ) { + case UIEVENT_KEYDOWN: + switch( event.chKey ) { + case VK_UP: + SelectItem(FindSelectable(m_iCurSel - 1, false), true); + return; + case VK_DOWN: + SelectItem(FindSelectable(m_iCurSel + 1, true), true); + return; + case VK_PRIOR: + PageUp(); + return; + case VK_NEXT: + PageDown(); + return; + case VK_HOME: + SelectItem(FindSelectable(0, false), true); + return; + case VK_END: + SelectItem(FindSelectable(GetCount() - 1, true), true); + return; + case VK_RETURN: + if( m_iCurSel != -1 ) GetItemAt(m_iCurSel)->Activate(); + return; + } + break; + case UIEVENT_SCROLLWHEEL: + { + switch( LOWORD(event.wParam) ) { + case SB_LINEUP: + if( m_bScrollSelect ) SelectItem(FindSelectable(m_iCurSel - 1, false), true); + else LineUp(); + return; + case SB_LINEDOWN: + if( m_bScrollSelect ) SelectItem(FindSelectable(m_iCurSel + 1, true), true); + else LineDown(); + return; + } + } + break; + } + CVerticalLayoutUI::DoEvent(event); +} + +CListHeaderUI* CListUI::GetHeader() const +{ + return m_pHeader; +} + +CContainerUI* CListUI::GetList() const +{ + return m_pList; +} + +bool CListUI::GetScrollSelect() +{ + return m_bScrollSelect; +} + +void CListUI::SetScrollSelect(bool bScrollSelect) +{ + m_bScrollSelect = bScrollSelect; +} + +int CListUI::GetCurSel() const +{ + return m_iCurSel; +} + +int CListUI::GetCurSelActivate() const +{ + return m_iCurSelActivate; +} + +bool CListUI::SelectItem(int iIndex, bool bTakeFocus) +{ + if( iIndex == m_iCurSel ) return true; + + int iOldSel = m_iCurSel; + // We should first unselect the currently selected item + if( m_iCurSel >= 0 ) { + CControlUI* pControl = GetItemAt(m_iCurSel); + if( pControl != NULL) { + IListItemUI* pListItem = static_cast(pControl->GetInterface(_T("ListItem"))); + if( pListItem != NULL ) pListItem->Select(false); + } + + m_iCurSel = -1; + } + if( iIndex < 0 ) return false; + + CControlUI* pControl = GetItemAt(iIndex); + if( pControl == NULL ) return false; + // if( !pControl->IsVisible() ) return false; + // if( !pControl->IsEnabled() ) return false; + + IListItemUI* pListItem = static_cast(pControl->GetInterface(_T("ListItem"))); + if( pListItem == NULL ) return false; + m_iCurSel = iIndex; + if( !pListItem->Select(true) ) { + m_iCurSel = -1; + return false; + } + EnsureVisible(m_iCurSel); + if( bTakeFocus ) pControl->SetFocus(); + if( m_pManager != NULL ) { + m_pManager->SendNotify(this, DUI_MSGTYPE_ITEMSELECT, m_iCurSel, iOldSel); + } + + return true; +} + +bool CListUI::SelectItemActivate(int iIndex) +{ + if (! SelectItem(iIndex, true)) + { + return false; + } + + m_iCurSelActivate = iIndex; + return true; +} + +TListInfoUI* CListUI::GetListInfo() +{ + return &m_ListInfo; +} + +bool CListUI::IsDelayedDestroy() const +{ + return m_pList->IsDelayedDestroy(); +} + +void CListUI::SetDelayedDestroy(bool bDelayed) +{ + m_pList->SetDelayedDestroy(bDelayed); +} + +int CListUI::GetChildPadding() const +{ + return m_pList->GetChildPadding(); +} + +void CListUI::SetChildPadding(int iPadding) +{ + m_pList->SetChildPadding(iPadding); +} + +void CListUI::SetItemFont(int index) +{ + m_ListInfo.nFont = index; + NeedUpdate(); +} + +void CListUI::SetItemTextStyle(UINT uStyle) +{ + m_ListInfo.uTextStyle = uStyle; + NeedUpdate(); +} + +void CListUI::SetItemTextPadding(RECT rc) +{ + m_ListInfo.rcTextPadding = rc; + NeedUpdate(); +} + +RECT CListUI::GetItemTextPadding() const +{ + return m_ListInfo.rcTextPadding; +} + +void CListUI::SetItemTextColor(DWORD dwTextColor) +{ + m_ListInfo.dwTextColor = dwTextColor; + Invalidate(); +} + +void CListUI::SetItemBkColor(DWORD dwBkColor) +{ + m_ListInfo.dwBkColor = dwBkColor; + Invalidate(); +} + +void CListUI::SetItemBkImage(LPCTSTR pStrImage) +{ + m_ListInfo.sBkImage = pStrImage; + Invalidate(); +} + +void CListUI::SetAlternateBk(bool bAlternateBk) +{ + m_ListInfo.bAlternateBk = bAlternateBk; + Invalidate(); +} + +DWORD CListUI::GetItemTextColor() const +{ + return m_ListInfo.dwTextColor; +} + +DWORD CListUI::GetItemBkColor() const +{ + return m_ListInfo.dwBkColor; +} + +LPCTSTR CListUI::GetItemBkImage() const +{ + return m_ListInfo.sBkImage; +} + +bool CListUI::IsAlternateBk() const +{ + return m_ListInfo.bAlternateBk; +} + +void CListUI::SetSelectedItemTextColor(DWORD dwTextColor) +{ + m_ListInfo.dwSelectedTextColor = dwTextColor; + Invalidate(); +} + +void CListUI::SetSelectedItemBkColor(DWORD dwBkColor) +{ + m_ListInfo.dwSelectedBkColor = dwBkColor; + Invalidate(); +} + +void CListUI::SetSelectedItemImage(LPCTSTR pStrImage) +{ + m_ListInfo.sSelectedImage = pStrImage; + Invalidate(); +} + +DWORD CListUI::GetSelectedItemTextColor() const +{ + return m_ListInfo.dwSelectedTextColor; +} + +DWORD CListUI::GetSelectedItemBkColor() const +{ + return m_ListInfo.dwSelectedBkColor; +} + +LPCTSTR CListUI::GetSelectedItemImage() const +{ + return m_ListInfo.sSelectedImage; +} + +void CListUI::SetHotItemTextColor(DWORD dwTextColor) +{ + m_ListInfo.dwHotTextColor = dwTextColor; + Invalidate(); +} + +void CListUI::SetHotItemBkColor(DWORD dwBkColor) +{ + m_ListInfo.dwHotBkColor = dwBkColor; + Invalidate(); +} + +void CListUI::SetHotItemImage(LPCTSTR pStrImage) +{ + m_ListInfo.sHotImage = pStrImage; + Invalidate(); +} + +DWORD CListUI::GetHotItemTextColor() const +{ + return m_ListInfo.dwHotTextColor; +} +DWORD CListUI::GetHotItemBkColor() const +{ + return m_ListInfo.dwHotBkColor; +} + +LPCTSTR CListUI::GetHotItemImage() const +{ + return m_ListInfo.sHotImage; +} + +void CListUI::SetDisabledItemTextColor(DWORD dwTextColor) +{ + m_ListInfo.dwDisabledTextColor = dwTextColor; + Invalidate(); +} + +void CListUI::SetDisabledItemBkColor(DWORD dwBkColor) +{ + m_ListInfo.dwDisabledBkColor = dwBkColor; + Invalidate(); +} + +void CListUI::SetDisabledItemImage(LPCTSTR pStrImage) +{ + m_ListInfo.sDisabledImage = pStrImage; + Invalidate(); +} + +DWORD CListUI::GetDisabledItemTextColor() const +{ + return m_ListInfo.dwDisabledTextColor; +} + +DWORD CListUI::GetDisabledItemBkColor() const +{ + return m_ListInfo.dwDisabledBkColor; +} + +LPCTSTR CListUI::GetDisabledItemImage() const +{ + return m_ListInfo.sDisabledImage; +} + +DWORD CListUI::GetItemLineColor() const +{ + return m_ListInfo.dwLineColor; +} + +void CListUI::SetItemLineColor(DWORD dwLineColor) +{ + m_ListInfo.dwLineColor = dwLineColor; + Invalidate(); +} + +bool CListUI::IsItemShowHtml() +{ + return m_ListInfo.bShowHtml; +} + +void CListUI::SetItemShowHtml(bool bShowHtml) +{ + if( m_ListInfo.bShowHtml == bShowHtml ) return; + + m_ListInfo.bShowHtml = bShowHtml; + NeedUpdate(); +} + +void CListUI::SetMultiExpanding(bool bMultiExpandable) +{ + m_ListInfo.bMultiExpandable = bMultiExpandable; +} + +bool CListUI::ExpandItem(int iIndex, bool bExpand /*= true*/) +{ + if( m_iExpandedItem >= 0 && !m_ListInfo.bMultiExpandable) { + CControlUI* pControl = GetItemAt(m_iExpandedItem); + if( pControl != NULL ) { + IListItemUI* pItem = static_cast(pControl->GetInterface(_T("ListItem"))); + if( pItem != NULL ) pItem->Expand(false); + } + m_iExpandedItem = -1; + } + if( bExpand ) { + CControlUI* pControl = GetItemAt(iIndex); + if( pControl == NULL ) return false; + if( !pControl->IsVisible() ) return false; + IListItemUI* pItem = static_cast(pControl->GetInterface(_T("ListItem"))); + if( pItem == NULL ) return false; + m_iExpandedItem = iIndex; + if( !pItem->Expand(true) ) { + m_iExpandedItem = -1; + return false; + } + } + NeedUpdate(); + return true; +} + +int CListUI::GetExpandedItem() const +{ + return m_iExpandedItem; +} + +void CListUI::EnsureVisible(int iIndex) +{ + if( m_iCurSel < 0 ) return; + RECT rcItem = m_pList->GetItemAt(iIndex)->GetPos(); + RECT rcList = m_pList->GetPos(); + RECT rcListInset = m_pList->GetInset(); + + rcList.left += rcListInset.left; + rcList.top += rcListInset.top; + rcList.right -= rcListInset.right; + rcList.bottom -= rcListInset.bottom; + + CScrollBarUI* pHorizontalScrollBar = m_pList->GetHorizontalScrollBar(); + if( pHorizontalScrollBar && pHorizontalScrollBar->IsVisible() ) rcList.bottom -= pHorizontalScrollBar->GetFixedHeight(); + + int iPos = m_pList->GetScrollPos().cy; + if( rcItem.top >= rcList.top && rcItem.bottom < rcList.bottom ) return; + int dx = 0; + if( rcItem.top < rcList.top ) dx = rcItem.top - rcList.top; + if( rcItem.bottom > rcList.bottom ) dx = rcItem.bottom - rcList.bottom; + Scroll(0, dx); +} + +void CListUI::Scroll(int dx, int dy) +{ + if( dx == 0 && dy == 0 ) return; + SIZE sz = m_pList->GetScrollPos(); + m_pList->SetScrollPos(CDuiSize(sz.cx + dx, sz.cy + dy)); +} + +void CListUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) +{ + if( _tcscmp(pstrName, _T("header")) == 0 ) GetHeader()->SetVisible(_tcscmp(pstrValue, _T("hidden")) != 0); + else if( _tcscmp(pstrName, _T("headerbkimage")) == 0 ) GetHeader()->SetBkImage(pstrValue); + else if( _tcscmp(pstrName, _T("scrollselect")) == 0 ) SetScrollSelect(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("multiexpanding")) == 0 ) SetMultiExpanding(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("itemfont")) == 0 ) m_ListInfo.nFont = _ttoi(pstrValue); + else if( _tcscmp(pstrName, _T("itemalign")) == 0 ) { + if( _tcsstr(pstrValue, _T("left")) != NULL ) { + m_ListInfo.uTextStyle &= ~(DT_CENTER | DT_RIGHT); + m_ListInfo.uTextStyle |= DT_LEFT; + } + if( _tcsstr(pstrValue, _T("center")) != NULL ) { + m_ListInfo.uTextStyle &= ~(DT_LEFT | DT_RIGHT); + m_ListInfo.uTextStyle |= DT_CENTER; + } + if( _tcsstr(pstrValue, _T("right")) != NULL ) { + m_ListInfo.uTextStyle &= ~(DT_LEFT | DT_CENTER); + m_ListInfo.uTextStyle |= DT_RIGHT; + } + } + else if( _tcscmp(pstrName, _T("itemendellipsis")) == 0 ) { + if( _tcscmp(pstrValue, _T("true")) == 0 ) m_ListInfo.uTextStyle |= DT_END_ELLIPSIS; + else m_ListInfo.uTextStyle &= ~DT_END_ELLIPSIS; + } + else if( _tcscmp(pstrName, _T("itemtextpadding")) == 0 ) { + RECT rcTextPadding = { 0 }; + LPTSTR pstr = NULL; + rcTextPadding.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + rcTextPadding.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcTextPadding.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcTextPadding.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetItemTextPadding(rcTextPadding); + } + else if( _tcscmp(pstrName, _T("itemtextcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetItemTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itembkcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetItemBkColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itembkimage")) == 0 ) SetItemBkImage(pstrValue); + else if( _tcscmp(pstrName, _T("itemaltbk")) == 0 ) SetAlternateBk(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("itemselectedtextcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetSelectedItemTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itemselectedbkcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetSelectedItemBkColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itemselectedimage")) == 0 ) SetSelectedItemImage(pstrValue); + else if( _tcscmp(pstrName, _T("itemhottextcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetHotItemTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itemhotbkcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetHotItemBkColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itemhotimage")) == 0 ) SetHotItemImage(pstrValue); + else if( _tcscmp(pstrName, _T("itemdisabledtextcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetDisabledItemTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itemdisabledbkcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetDisabledItemBkColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itemdisabledimage")) == 0 ) SetDisabledItemImage(pstrValue); + else if( _tcscmp(pstrName, _T("itemlinecolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetItemLineColor(clrColor); + } + else if( _tcscmp(pstrName, _T("itemshowhtml")) == 0 ) SetItemShowHtml(_tcscmp(pstrValue, _T("true")) == 0); + else CVerticalLayoutUI::SetAttribute(pstrName, pstrValue); +} + +IListCallbackUI* CListUI::GetTextCallback() const +{ + return m_pCallback; +} + +void CListUI::SetTextCallback(IListCallbackUI* pCallback) +{ + m_pCallback = pCallback; +} + +SIZE CListUI::GetScrollPos() const +{ + return m_pList->GetScrollPos(); +} + +SIZE CListUI::GetScrollRange() const +{ + return m_pList->GetScrollRange(); +} + +void CListUI::SetScrollPos(SIZE szPos) +{ + m_pList->SetScrollPos(szPos); +} + +void CListUI::LineUp() +{ + m_pList->LineUp(); +} + +void CListUI::LineDown() +{ + m_pList->LineDown(); +} + +void CListUI::PageUp() +{ + m_pList->PageUp(); +} + +void CListUI::PageDown() +{ + m_pList->PageDown(); +} + +void CListUI::HomeUp() +{ + m_pList->HomeUp(); +} + +void CListUI::EndDown() +{ + m_pList->EndDown(); +} + +void CListUI::LineLeft() +{ + m_pList->LineLeft(); +} + +void CListUI::LineRight() +{ + m_pList->LineRight(); +} + +void CListUI::PageLeft() +{ + m_pList->PageLeft(); +} + +void CListUI::PageRight() +{ + m_pList->PageRight(); +} + +void CListUI::HomeLeft() +{ + m_pList->HomeLeft(); +} + +void CListUI::EndRight() +{ + m_pList->EndRight(); +} + +void CListUI::EnableScrollBar(bool bEnableVertical, bool bEnableHorizontal) +{ + m_pList->EnableScrollBar(bEnableVertical, bEnableHorizontal); +} + +CScrollBarUI* CListUI::GetVerticalScrollBar() const +{ + return m_pList->GetVerticalScrollBar(); +} + +CScrollBarUI* CListUI::GetHorizontalScrollBar() const +{ + return m_pList->GetHorizontalScrollBar(); +} + +BOOL CListUI::SortItems(PULVCompareFunc pfnCompare, UINT_PTR dwData) +{ + if (!m_pList) + return FALSE; + return m_pList->SortItems(pfnCompare, dwData); +} +///////////////////////////////////////////////////////////////////////////////////// +// +// + + +CListBodyUI::CListBodyUI(CListUI* pOwner) : m_pOwner(pOwner) +{ + ASSERT(m_pOwner); +} + +BOOL CListBodyUI::SortItems(PULVCompareFunc pfnCompare, UINT_PTR dwData) +{ + if (!pfnCompare) + return FALSE; + m_pCompareFunc = pfnCompare; + CControlUI **pData = (CControlUI **)m_items.GetData(); + qsort_s(m_items.GetData(), m_items.GetSize(), sizeof(CControlUI*), CListBodyUI::ItemComareFunc, this); + IListItemUI *pItem = NULL; + for (int i = 0; i < m_items.GetSize(); ++i) + { + pItem = (IListItemUI*)(static_cast(m_items[i])->GetInterface(TEXT("ListItem"))); + if (pItem) + { + pItem->SetIndex(i); + pItem->Select(false); + } + } + m_pOwner->SelectItem(-1); + if (m_pManager) + { + SetPos(GetPos()); + Invalidate(); + } + + return TRUE; +} + +int __cdecl CListBodyUI::ItemComareFunc(void *pvlocale, const void *item1, const void *item2) +{ + CListBodyUI *pThis = (CListBodyUI*)pvlocale; + if (!pThis || !item1 || !item2) + return 0; + return pThis->ItemComareFunc(item1, item2); +} + +int __cdecl CListBodyUI::ItemComareFunc(const void *item1, const void *item2) +{ + CControlUI *pControl1 = *(CControlUI**)item1; + CControlUI *pControl2 = *(CControlUI**)item2; + return m_pCompareFunc((UINT_PTR)pControl1, (UINT_PTR)pControl2, m_compareData); +} + +void CListBodyUI::SetScrollPos(SIZE szPos) +{ + int cx = 0; + int cy = 0; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) { + int iLastScrollPos = m_pVerticalScrollBar->GetScrollPos(); + m_pVerticalScrollBar->SetScrollPos(szPos.cy); + cy = m_pVerticalScrollBar->GetScrollPos() - iLastScrollPos; + } + + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) { + int iLastScrollPos = m_pHorizontalScrollBar->GetScrollPos(); + m_pHorizontalScrollBar->SetScrollPos(szPos.cx); + cx = m_pHorizontalScrollBar->GetScrollPos() - iLastScrollPos; + } + + if( cx == 0 && cy == 0 ) return; + + if( cx != 0 && m_pOwner ) { + CListHeaderUI* pHeader = m_pOwner->GetHeader(); + if( pHeader == NULL ) return; + TListInfoUI* pInfo = m_pOwner->GetListInfo(); + pInfo->nColumns = MIN(pHeader->GetCount(), UILIST_MAX_COLUMNS); + + if( !pHeader->IsVisible() ) { + for( int it = 0; it < pHeader->GetCount(); it++ ) { + static_cast(pHeader->GetItemAt(it))->SetInternVisible(true); + } + } + for( int i = 0; i < pInfo->nColumns; i++ ) { + CControlUI* pControl = static_cast(pHeader->GetItemAt(i)); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) continue; + + RECT rcPos = pControl->GetPos(); + rcPos.left -= cx; + rcPos.right -= cx; + pControl->SetPos(rcPos); + pInfo->rcColumn[i] = pControl->GetPos(); + } + if( !pHeader->IsVisible() ) { + for( int it = 0; it < pHeader->GetCount(); it++ ) { + static_cast(pHeader->GetItemAt(it))->SetInternVisible(false); + } + } + } + + RECT rcPos; + for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) { + CControlUI* pControl = static_cast(m_items[it2]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) continue; + + rcPos = pControl->GetPos(); + rcPos.left -= cx; + rcPos.right -= cx; + rcPos.top -= cy; + rcPos.bottom -= cy; + pControl->SetPos(rcPos); + } + + Invalidate(); + + +} + +void CListBodyUI::SetPos(RECT rc) +{ + CControlUI::SetPos(rc); + rc = m_rcItem; + + // Adjust for inset + rc.left += m_rcInset.left; + rc.top += m_rcInset.top; + rc.right -= m_rcInset.right; + rc.bottom -= m_rcInset.bottom; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth(); + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight(); + + // Determine the minimum size + SIZE szAvailable = { rc.right - rc.left, rc.bottom - rc.top }; + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) + szAvailable.cx += m_pHorizontalScrollBar->GetScrollRange(); + + int cxNeeded = 0; + int nAdjustables = 0; + int cyFixed = 0; + int nEstimateNum = 0; + for( int it1 = 0; it1 < m_items.GetSize(); it1++ ) { + CControlUI* pControl = static_cast(m_items[it1]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) continue; + SIZE sz = pControl->EstimateSize(szAvailable); + if( sz.cy == 0 ) { + nAdjustables++; + } + else { + if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight(); + if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight(); + } + cyFixed += sz.cy + pControl->GetPadding().top + pControl->GetPadding().bottom; + + RECT rcPadding = pControl->GetPadding(); + sz.cx = MAX(sz.cx, 0); + if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); + if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); + cxNeeded = MAX(cxNeeded, sz.cx); + nEstimateNum++; + } + cyFixed += (nEstimateNum - 1) * m_iChildPadding; + + if( m_pOwner ) { + CListHeaderUI* pHeader = m_pOwner->GetHeader(); + if( pHeader != NULL && pHeader->GetCount() > 0 ) { + cxNeeded = MAX(0, pHeader->EstimateSize(CDuiSize(rc.right - rc.left, rc.bottom - rc.top)).cx); + if ( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible()) + { + RECT rcHeader = pHeader->GetPos(); + pHeader->SetPos(rcHeader); + } + } + } + + // Place elements + int cyNeeded = 0; + int cyExpand = 0; + if( nAdjustables > 0 ) cyExpand = MAX(0, (szAvailable.cy - cyFixed) / nAdjustables); + // Position the elements + SIZE szRemaining = szAvailable; + int iPosY = rc.top; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) { + iPosY -= m_pVerticalScrollBar->GetScrollPos(); + } + int iPosX = rc.left; + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) { + iPosX -= m_pHorizontalScrollBar->GetScrollPos(); + } + int iAdjustable = 0; + int cyFixedRemaining = cyFixed; + for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) { + CControlUI* pControl = static_cast(m_items[it2]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) { + SetFloatPos(it2); + continue; + } + + RECT rcPadding = pControl->GetPadding(); + szRemaining.cy -= rcPadding.top; + SIZE sz = pControl->EstimateSize(szRemaining); + if( sz.cy == 0 ) { + iAdjustable++; + sz.cy = cyExpand; + // Distribute remaining to last element (usually round-off left-overs) + if( iAdjustable == nAdjustables ) { + sz.cy = MAX(0, szRemaining.cy - rcPadding.bottom - cyFixedRemaining); + } + if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight(); + if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight(); + } + else { + if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight(); + if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight(); + cyFixedRemaining -= sz.cy; + } + + sz.cx = MAX(cxNeeded, szAvailable.cx - rcPadding.left - rcPadding.right); + + if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); + if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); + + RECT rcCtrl = { iPosX + rcPadding.left, iPosY + rcPadding.top, iPosX + rcPadding.left + sz.cx, iPosY + sz.cy + rcPadding.top + rcPadding.bottom }; + pControl->SetPos(rcCtrl); + + iPosY += sz.cy + m_iChildPadding + rcPadding.top + rcPadding.bottom; + cyNeeded += sz.cy + rcPadding.top + rcPadding.bottom; + szRemaining.cy -= sz.cy + m_iChildPadding + rcPadding.bottom; + } + cyNeeded += (nEstimateNum - 1) * m_iChildPadding; + + if( m_pHorizontalScrollBar != NULL ) { + if( cxNeeded > rc.right - rc.left ) { + if( m_pHorizontalScrollBar->IsVisible() ) { + m_pHorizontalScrollBar->SetScrollRange(cxNeeded - (rc.right - rc.left)); + } + else { + m_pHorizontalScrollBar->SetVisible(true); + m_pHorizontalScrollBar->SetScrollRange(cxNeeded - (rc.right - rc.left)); + m_pHorizontalScrollBar->SetScrollPos(0); + rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight(); + } + } + else { + if( m_pHorizontalScrollBar->IsVisible() ) { + m_pHorizontalScrollBar->SetVisible(false); + m_pHorizontalScrollBar->SetScrollRange(0); + m_pHorizontalScrollBar->SetScrollPos(0); + rc.bottom += m_pHorizontalScrollBar->GetFixedHeight(); + } + } + } + + // Process the scrollbar + ProcessScrollBar(rc, cxNeeded, cyNeeded); +} + +void CListBodyUI::DoEvent(TEventUI& event) +{ + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pOwner != NULL ) m_pOwner->DoEvent(event); + else CControlUI::DoEvent(event); + return; + } + + if( m_pOwner != NULL ) m_pOwner->DoEvent(event); else CControlUI::DoEvent(event); +} + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +CListHeaderUI::CListHeaderUI(): +m_bIsScaleHeader(false) +{ +} + +LPCTSTR CListHeaderUI::GetClass() const +{ + return _T("ListHeaderUI"); +} + +LPVOID CListHeaderUI::GetInterface(LPCTSTR pstrName) +{ + if( _tcscmp(pstrName, DUI_CTR_LISTHEADER) == 0 ) return this; + return CHorizontalLayoutUI::GetInterface(pstrName); +} + +SIZE CListHeaderUI::EstimateSize(SIZE szAvailable) +{ + SIZE cXY = {0, m_cxyFixed.cy}; + if( cXY.cy == 0 && m_pManager != NULL ) { + for( int it = 0; it < m_items.GetSize(); it++ ) { + cXY.cy = MAX(cXY.cy,static_cast(m_items[it])->EstimateSize(szAvailable).cy); + } + int nMin = m_pManager->GetDefaultFontInfo()->tm.tmHeight + 6; + cXY.cy = MAX(cXY.cy,nMin); + } + + for( int it = 0; it < m_items.GetSize(); it++ ) { + cXY.cx += static_cast(m_items[it])->EstimateSize(szAvailable).cx; + } + + return cXY; +} + +void CListHeaderUI::SetPos(RECT rc) +{ + CControlUI::SetPos(rc); + rc = m_rcItem; + + // Adjust for inset + rc.left += m_rcInset.left; + rc.top += m_rcInset.top; + rc.right -= m_rcInset.right; + rc.bottom -= m_rcInset.bottom; + + if( m_items.GetSize() == 0) { + return; + } + + + // Determine the width of elements that are sizeable + SIZE szAvailable = { rc.right - rc.left, rc.bottom - rc.top }; + + int nAdjustables = 0; + int cxFixed = 0; + int nEstimateNum = 0; + for( int it1 = 0; it1 < m_items.GetSize(); it1++ ) { + CControlUI* pControl = static_cast(m_items[it1]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) continue; + SIZE sz = pControl->EstimateSize(szAvailable); + if( sz.cx == 0 ) { + nAdjustables++; + } + else { + if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); + if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); + } + cxFixed += sz.cx + pControl->GetPadding().left + pControl->GetPadding().right; + nEstimateNum++; + } + cxFixed += (nEstimateNum - 1) * m_iChildPadding; + + int cxExpand = 0; + int cxNeeded = 0; + if( nAdjustables > 0 ) cxExpand = MAX(0, (szAvailable.cx - cxFixed) / nAdjustables); + // Position the elements + SIZE szRemaining = szAvailable; + int iPosX = rc.left; + + int iAdjustable = 0; + int cxFixedRemaining = cxFixed; + + int nHeaderWidth = GetWidth(); + CListUI *pList = static_cast(GetParent()); + if (pList != NULL) + { + CScrollBarUI* pVScroll = pList->GetVerticalScrollBar(); + if (pVScroll != NULL) + nHeaderWidth -= pVScroll->GetWidth(); + } + for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) { + CControlUI* pControl = static_cast(m_items[it2]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) { + SetFloatPos(it2); + continue; + } + RECT rcPadding = pControl->GetPadding(); + szRemaining.cx -= rcPadding.left; + + SIZE sz = {0,0}; + if (m_bIsScaleHeader) + { + CListHeaderItemUI* pHeaderItem = static_cast(pControl); + sz.cx = int(nHeaderWidth * (float)pHeaderItem->GetScale() / 100); + } + else + { + sz = pControl->EstimateSize(szRemaining); + } + + if( sz.cx == 0 ) { + iAdjustable++; + sz.cx = cxExpand; + // Distribute remaining to last element (usually round-off left-overs) + if( iAdjustable == nAdjustables ) { + sz.cx = MAX(0, szRemaining.cx - rcPadding.right - cxFixedRemaining); + } + if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); + if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); + } + else { + if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); + if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); + + cxFixedRemaining -= sz.cx; + } + + sz.cy = pControl->GetFixedHeight(); + if( sz.cy == 0 ) sz.cy = rc.bottom - rc.top - rcPadding.top - rcPadding.bottom; + if( sz.cy < 0 ) sz.cy = 0; + if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight(); + if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight(); + + RECT rcCtrl = { iPosX + rcPadding.left, rc.top + rcPadding.top, iPosX + sz.cx + rcPadding.left + rcPadding.right, rc.top + rcPadding.top + sz.cy}; + pControl->SetPos(rcCtrl); + iPosX += sz.cx + m_iChildPadding + rcPadding.left + rcPadding.right; + cxNeeded += sz.cx + rcPadding.left + rcPadding.right; + szRemaining.cx -= sz.cx + m_iChildPadding + rcPadding.right; + } + cxNeeded += (nEstimateNum - 1) * m_iChildPadding; +} + +void CListHeaderUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) +{ + if( _tcscmp(pstrName, _T("scaleheader")) == 0 ) SetScaleHeader(_tcscmp(pstrValue, _T("true")) == 0); + else CHorizontalLayoutUI::SetAttribute(pstrName, pstrValue); +} + +void CListHeaderUI::SetScaleHeader(bool bIsScale) +{ + m_bIsScaleHeader = bIsScale; +} + +bool CListHeaderUI::IsScaleHeader() const +{ + return m_bIsScaleHeader; +} +///////////////////////////////////////////////////////////////////////////////////// +// +// + +CListHeaderItemUI::CListHeaderItemUI() : m_bDragable(true), m_uButtonState(0), m_iSepWidth(4), +m_uTextStyle(DT_VCENTER | DT_CENTER | DT_SINGLELINE), m_dwTextColor(0), m_iFont(-1), m_bShowHtml(false),m_nScale(0) +{ + SetTextPadding(CDuiRect(2, 0, 2, 0)); + ptLastMouse.x = ptLastMouse.y = 0; + SetMinWidth(16); +} + +LPCTSTR CListHeaderItemUI::GetClass() const +{ + return _T("ListHeaderItemUI"); +} + +LPVOID CListHeaderItemUI::GetInterface(LPCTSTR pstrName) +{ + if( _tcscmp(pstrName, DUI_CTR_LISTHEADERITEM) == 0 ) return this; + return CContainerUI::GetInterface(pstrName); +} + +UINT CListHeaderItemUI::GetControlFlags() const +{ + if( IsEnabled() && m_iSepWidth != 0 ) return UIFLAG_SETCURSOR; + else return 0; +} + +void CListHeaderItemUI::SetEnabled(bool bEnable) +{ + CContainerUI::SetEnabled(bEnable); + if( !IsEnabled() ) { + m_uButtonState = 0; + } +} + +bool CListHeaderItemUI::IsDragable() const +{ + return m_bDragable; +} + +void CListHeaderItemUI::SetDragable(bool bDragable) +{ + m_bDragable = bDragable; + if ( !m_bDragable ) m_uButtonState &= ~UISTATE_CAPTURED; +} + +DWORD CListHeaderItemUI::GetSepWidth() const +{ + return m_iSepWidth; +} + +void CListHeaderItemUI::SetSepWidth(int iWidth) +{ + m_iSepWidth = iWidth; +} + +DWORD CListHeaderItemUI::GetTextStyle() const +{ + return m_uTextStyle; +} + +void CListHeaderItemUI::SetTextStyle(UINT uStyle) +{ + m_uTextStyle = uStyle; + Invalidate(); +} + +DWORD CListHeaderItemUI::GetTextColor() const +{ + return m_dwTextColor; +} + + +void CListHeaderItemUI::SetTextColor(DWORD dwTextColor) +{ + m_dwTextColor = dwTextColor; +} + +RECT CListHeaderItemUI::GetTextPadding() const +{ + return m_rcTextPadding; +} + +void CListHeaderItemUI::SetTextPadding(RECT rc) +{ + m_rcTextPadding = rc; + Invalidate(); +} + +void CListHeaderItemUI::SetFont(int index) +{ + m_iFont = index; +} + +bool CListHeaderItemUI::IsShowHtml() +{ + return m_bShowHtml; +} + +void CListHeaderItemUI::SetShowHtml(bool bShowHtml) +{ + if( m_bShowHtml == bShowHtml ) return; + + m_bShowHtml = bShowHtml; + Invalidate(); +} + +LPCTSTR CListHeaderItemUI::GetNormalImage() const +{ + return m_sNormalImage; +} + +void CListHeaderItemUI::SetNormalImage(LPCTSTR pStrImage) +{ + m_sNormalImage = pStrImage; + Invalidate(); +} + +LPCTSTR CListHeaderItemUI::GetHotImage() const +{ + return m_sHotImage; +} + +void CListHeaderItemUI::SetHotImage(LPCTSTR pStrImage) +{ + m_sHotImage = pStrImage; + Invalidate(); +} + +LPCTSTR CListHeaderItemUI::GetPushedImage() const +{ + return m_sPushedImage; +} + +void CListHeaderItemUI::SetPushedImage(LPCTSTR pStrImage) +{ + m_sPushedImage = pStrImage; + Invalidate(); +} + +LPCTSTR CListHeaderItemUI::GetFocusedImage() const +{ + return m_sFocusedImage; +} + +void CListHeaderItemUI::SetFocusedImage(LPCTSTR pStrImage) +{ + m_sFocusedImage = pStrImage; + Invalidate(); +} + +LPCTSTR CListHeaderItemUI::GetSepImage() const +{ + return m_sSepImage; +} + +void CListHeaderItemUI::SetSepImage(LPCTSTR pStrImage) +{ + m_sSepImage = pStrImage; + Invalidate(); +} + +void CListHeaderItemUI::SetScale(int nScale) +{ + m_nScale = nScale; +} + +int CListHeaderItemUI::GetScale() const +{ + return m_nScale; +} + +void CListHeaderItemUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) +{ + if( _tcscmp(pstrName, _T("dragable")) == 0 ) SetDragable(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("sepwidth")) == 0 ) SetSepWidth(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("align")) == 0 ) { + if( _tcsstr(pstrValue, _T("left")) != NULL ) { + m_uTextStyle &= ~(DT_CENTER | DT_RIGHT); + m_uTextStyle |= DT_LEFT; + } + if( _tcsstr(pstrValue, _T("center")) != NULL ) { + m_uTextStyle &= ~(DT_LEFT | DT_RIGHT); + m_uTextStyle |= DT_CENTER; + } + if( _tcsstr(pstrValue, _T("right")) != NULL ) { + m_uTextStyle &= ~(DT_LEFT | DT_CENTER); + m_uTextStyle |= DT_RIGHT; + } + } + else if( _tcscmp(pstrName, _T("endellipsis")) == 0 ) { + if( _tcscmp(pstrValue, _T("true")) == 0 ) m_uTextStyle |= DT_END_ELLIPSIS; + else m_uTextStyle &= ~DT_END_ELLIPSIS; + } + else if( _tcscmp(pstrName, _T("font")) == 0 ) SetFont(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("textcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("textpadding")) == 0 ) { + RECT rcTextPadding = { 0 }; + LPTSTR pstr = NULL; + rcTextPadding.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + rcTextPadding.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcTextPadding.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcTextPadding.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetTextPadding(rcTextPadding); + } + else if( _tcscmp(pstrName, _T("showhtml")) == 0 ) SetShowHtml(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("normalimage")) == 0 ) SetNormalImage(pstrValue); + else if( _tcscmp(pstrName, _T("hotimage")) == 0 ) SetHotImage(pstrValue); + else if( _tcscmp(pstrName, _T("pushedimage")) == 0 ) SetPushedImage(pstrValue); + else if( _tcscmp(pstrName, _T("focusedimage")) == 0 ) SetFocusedImage(pstrValue); + else if( _tcscmp(pstrName, _T("sepimage")) == 0 ) SetSepImage(pstrValue); + else if( _tcscmp(pstrName, _T("scale")) == 0 ) { + LPTSTR pstr = NULL; + SetScale(_tcstol(pstrValue, &pstr, 10)); + + } + else CContainerUI::SetAttribute(pstrName, pstrValue); +} + +void CListHeaderItemUI::DoEvent(TEventUI& event) +{ + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pParent != NULL ) m_pParent->DoEvent(event); + else CContainerUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_SETFOCUS ) + { + Invalidate(); + } + if( event.Type == UIEVENT_KILLFOCUS ) + { + Invalidate(); + } + if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK ) + { + if( !IsEnabled() ) return; + RECT rcSeparator = GetThumbRect(); + if (m_iSepWidth>=0)//111024 by cddjr, ӷָ򣬷û϶ + rcSeparator.left-=4; + else + rcSeparator.right+=4; + if( ::PtInRect(&rcSeparator, event.ptMouse) ) { + if( m_bDragable ) { + m_uButtonState |= UISTATE_CAPTURED; + ptLastMouse = event.ptMouse; + } + } + else { + m_uButtonState |= UISTATE_PUSHED; + m_pManager->SendNotify(this, DUI_MSGTYPE_HEADERCLICK); + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_BUTTONUP ) + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { + m_uButtonState &= ~UISTATE_CAPTURED; + if( GetParent() ) + GetParent()->NeedParentUpdate(); + } + else if( (m_uButtonState & UISTATE_PUSHED) != 0 ) { + m_uButtonState &= ~UISTATE_PUSHED; + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_MOUSEMOVE ) + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { + RECT rc = m_rcItem; + if( m_iSepWidth >= 0 ) { + rc.right -= ptLastMouse.x - event.ptMouse.x; + } + else { + rc.left -= ptLastMouse.x - event.ptMouse.x; + } + + if( rc.right - rc.left > GetMinWidth() ) { + m_cxyFixed.cx = rc.right - rc.left; + ptLastMouse = event.ptMouse; + if( GetParent() ) + GetParent()->NeedParentUpdate(); + } + } + return; + } + if( event.Type == UIEVENT_SETCURSOR ) + { + RECT rcSeparator = GetThumbRect(); + if (m_iSepWidth>=0)//111024 by cddjr, ӷָ򣬷û϶ + rcSeparator.left-=4; + else + rcSeparator.right+=4; + if( IsEnabled() && m_bDragable && ::PtInRect(&rcSeparator, event.ptMouse) ) { + ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZEWE))); + return; + } + } + if( event.Type == UIEVENT_MOUSEENTER ) + { + if( IsEnabled() ) { + m_uButtonState |= UISTATE_HOT; + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_MOUSELEAVE ) + { + if( IsEnabled() ) { + m_uButtonState &= ~UISTATE_HOT; + Invalidate(); + } + return; + } + CContainerUI::DoEvent(event); +} + +SIZE CListHeaderItemUI::EstimateSize(SIZE szAvailable) +{ + if( m_cxyFixed.cy == 0 ) return CDuiSize(m_cxyFixed.cx, m_pManager->GetDefaultFontInfo()->tm.tmHeight + 14); + return CContainerUI::EstimateSize(szAvailable); +} + +RECT CListHeaderItemUI::GetThumbRect() const +{ + if( m_iSepWidth >= 0 ) return CDuiRect(m_rcItem.right - m_iSepWidth, m_rcItem.top, m_rcItem.right, m_rcItem.bottom); + else return CDuiRect(m_rcItem.left, m_rcItem.top, m_rcItem.left - m_iSepWidth, m_rcItem.bottom); +} + +void CListHeaderItemUI::PaintStatusImage(HDC hDC) +{ + if( IsFocused() ) m_uButtonState |= UISTATE_FOCUSED; + else m_uButtonState &= ~ UISTATE_FOCUSED; + + if( (m_uButtonState & UISTATE_PUSHED) != 0 ) { + if( m_sPushedImage.IsEmpty() && !m_sNormalImage.IsEmpty() ) DrawImage(hDC, (LPCTSTR)m_sNormalImage); + if( !DrawImage(hDC, (LPCTSTR)m_sPushedImage) ) m_sPushedImage.Empty(); + } + else if( (m_uButtonState & UISTATE_HOT) != 0 ) { + if( m_sHotImage.IsEmpty() && !m_sNormalImage.IsEmpty() ) DrawImage(hDC, (LPCTSTR)m_sNormalImage); + if( !DrawImage(hDC, (LPCTSTR)m_sHotImage) ) m_sHotImage.Empty(); + } + else if( (m_uButtonState & UISTATE_FOCUSED) != 0 ) { + if( m_sFocusedImage.IsEmpty() && !m_sNormalImage.IsEmpty() ) DrawImage(hDC, (LPCTSTR)m_sNormalImage); + if( !DrawImage(hDC, (LPCTSTR)m_sFocusedImage) ) m_sFocusedImage.Empty(); + } + else { + if( !m_sNormalImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sNormalImage) ) m_sNormalImage.Empty(); + } + } + + if( !m_sSepImage.IsEmpty() ) { + RECT rcThumb = GetThumbRect(); + rcThumb.left -= m_rcItem.left; + rcThumb.top -= m_rcItem.top; + rcThumb.right -= m_rcItem.left; + rcThumb.bottom -= m_rcItem.top; + + m_sSepImageModify.Empty(); + m_sSepImageModify.SmallFormat(_T("dest='%d,%d,%d,%d'"), rcThumb.left, rcThumb.top, rcThumb.right, rcThumb.bottom); + if( !DrawImage(hDC, (LPCTSTR)m_sSepImage, (LPCTSTR)m_sSepImageModify) ) m_sSepImage.Empty(); + } +} + +void CListHeaderItemUI::PaintText(HDC hDC) +{ + if( m_dwTextColor == 0 ) m_dwTextColor = m_pManager->GetDefaultFontColor(); + + RECT rcText = m_rcItem; + rcText.left += m_rcTextPadding.left; + rcText.top += m_rcTextPadding.top; + rcText.right -= m_rcTextPadding.right; + rcText.bottom -= m_rcTextPadding.bottom; + + if( m_sText.IsEmpty() ) return; + int nLinks = 0; + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rcText, m_sText, m_dwTextColor, \ + NULL, NULL, nLinks, DT_SINGLELINE | m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rcText, m_sText, m_dwTextColor, \ + m_iFont, DT_SINGLELINE | m_uTextStyle); +} + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +CListElementUI::CListElementUI() : +m_iIndex(-1), +m_pOwner(NULL), +m_bSelected(false), +m_uButtonState(0) +{ +} + +LPCTSTR CListElementUI::GetClass() const +{ + return _T("ListElementUI"); +} + +UINT CListElementUI::GetControlFlags() const +{ + return UIFLAG_WANTRETURN; +} + +LPVOID CListElementUI::GetInterface(LPCTSTR pstrName) +{ + if( _tcscmp(pstrName, DUI_CTR_LISTITEM) == 0 ) return static_cast(this); + if( _tcscmp(pstrName, DUI_CTR_LISTELEMENT) == 0 ) return static_cast(this); + return CControlUI::GetInterface(pstrName); +} + +IListOwnerUI* CListElementUI::GetOwner() +{ + return m_pOwner; +} + +void CListElementUI::SetOwner(CControlUI* pOwner) +{ + m_pOwner = static_cast(pOwner->GetInterface(_T("IListOwner"))); +} + +void CListElementUI::SetVisible(bool bVisible) +{ + CControlUI::SetVisible(bVisible); + if( !IsVisible() && m_bSelected) + { + m_bSelected = false; + if( m_pOwner != NULL ) m_pOwner->SelectItem(-1); + } +} + +void CListElementUI::SetEnabled(bool bEnable) +{ + CControlUI::SetEnabled(bEnable); + if( !IsEnabled() ) { + m_uButtonState = 0; + } +} + +int CListElementUI::GetIndex() const +{ + return m_iIndex; +} + +void CListElementUI::SetIndex(int iIndex) +{ + m_iIndex = iIndex; +} + +void CListElementUI::Invalidate() +{ + if( !IsVisible() ) return; + + if( GetParent() ) { + CContainerUI* pParentContainer = static_cast(GetParent()->GetInterface(_T("Container"))); + if( pParentContainer ) { + RECT rc = pParentContainer->GetPos(); + RECT rcInset = pParentContainer->GetInset(); + rc.left += rcInset.left; + rc.top += rcInset.top; + rc.right -= rcInset.right; + rc.bottom -= rcInset.bottom; + CScrollBarUI* pVerticalScrollBar = pParentContainer->GetVerticalScrollBar(); + if( pVerticalScrollBar && pVerticalScrollBar->IsVisible() ) rc.right -= pVerticalScrollBar->GetFixedWidth(); + CScrollBarUI* pHorizontalScrollBar = pParentContainer->GetHorizontalScrollBar(); + if( pHorizontalScrollBar && pHorizontalScrollBar->IsVisible() ) rc.bottom -= pHorizontalScrollBar->GetFixedHeight(); + + RECT invalidateRc = m_rcItem; + if( !::IntersectRect(&invalidateRc, &m_rcItem, &rc) ) + { + return; + } + + CControlUI* pParent = GetParent(); + RECT rcTemp; + RECT rcParent; + while( pParent = pParent->GetParent() ) + { + rcTemp = invalidateRc; + rcParent = pParent->GetPos(); + if( !::IntersectRect(&invalidateRc, &rcTemp, &rcParent) ) + { + return; + } + } + + if( m_pManager != NULL ) m_pManager->Invalidate(invalidateRc); + } + else { + CControlUI::Invalidate(); + } + } + else { + CControlUI::Invalidate(); + } +} + +bool CListElementUI::Activate() +{ + if( !CControlUI::Activate() ) return false; + if( m_pManager != NULL ) m_pManager->SendNotify(this, DUI_MSGTYPE_ITEMACTIVATE); + return true; +} + +bool CListElementUI::IsSelected() const +{ + return m_bSelected; +} + +bool CListElementUI::Select(bool bSelect) +{ + if( !IsEnabled() ) return false; + if( bSelect == m_bSelected ) return true; + m_bSelected = bSelect; + if( bSelect && m_pOwner != NULL ) m_pOwner->SelectItem(m_iIndex); + Invalidate(); + + return true; +} + +bool CListElementUI::IsExpanded() const +{ + return false; +} + +bool CListElementUI::Expand(bool /*bExpand = true*/) +{ + return false; +} + +void CListElementUI::DoEvent(TEventUI& event) +{ + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pOwner != NULL ) m_pOwner->DoEvent(event); + else CControlUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_DBLCLICK ) + { + if( IsEnabled() ) { + Activate(); + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_KEYDOWN && IsEnabled() ) + { + if( event.chKey == VK_RETURN ) { + Activate(); + Invalidate(); + return; + } + } + // An important twist: The list-item will send the event not to its immediate + // parent but to the "attached" list. A list may actually embed several components + // in its path to the item, but key-presses etc. needs to go to the actual list. + if( m_pOwner != NULL ) m_pOwner->DoEvent(event); else CControlUI::DoEvent(event); +} + +void CListElementUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) +{ + if( _tcscmp(pstrName, _T("selected")) == 0 ) Select(); + else CControlUI::SetAttribute(pstrName, pstrValue); +} + +void CListElementUI::DrawItemBk(HDC hDC, const RECT& rcItem) +{ + ASSERT(m_pOwner); + if( m_pOwner == NULL ) return; + TListInfoUI* pInfo = m_pOwner->GetListInfo(); + DWORD iBackColor = 0; + if( !pInfo->bAlternateBk || m_iIndex % 2 == 0 ) iBackColor = pInfo->dwBkColor; + if( (m_uButtonState & UISTATE_HOT) != 0 ) { + iBackColor = pInfo->dwHotBkColor; + } + if( IsSelected() ) { + iBackColor = pInfo->dwSelectedBkColor; + } + if( !IsEnabled() ) { + iBackColor = pInfo->dwDisabledBkColor; + } + + if ( iBackColor != 0 ) { + CRenderEngine::DrawColor(hDC, m_rcItem, GetAdjustColor(iBackColor)); + } + + if( !IsEnabled() ) { + if( !pInfo->sDisabledImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)pInfo->sDisabledImage) ) pInfo->sDisabledImage.Empty(); + else return; + } + } + if( IsSelected() ) { + if( !pInfo->sSelectedImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)pInfo->sSelectedImage) ) pInfo->sSelectedImage.Empty(); + else return; + } + } + if( (m_uButtonState & UISTATE_HOT) != 0 ) { + if( !pInfo->sHotImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)pInfo->sHotImage) ) pInfo->sHotImage.Empty(); + else return; + } + } + + if( !m_sBkImage.IsEmpty() ) { + if( !pInfo->bAlternateBk || m_iIndex % 2 == 0 ) { + if( !DrawImage(hDC, (LPCTSTR)m_sBkImage) ) m_sBkImage.Empty(); + } + } + + if( m_sBkImage.IsEmpty() ) { + if( !pInfo->sBkImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)pInfo->sBkImage) ) pInfo->sBkImage.Empty(); + else return; + } + } + + if ( pInfo->dwLineColor != 0 ) { + RECT rcLine = { m_rcItem.left, m_rcItem.bottom - 1, m_rcItem.right, m_rcItem.bottom - 1 }; + CRenderEngine::DrawLine(hDC, rcLine, 1, GetAdjustColor(pInfo->dwLineColor)); + } +} + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +CListLabelElementUI::CListLabelElementUI() +{ +} + +LPCTSTR CListLabelElementUI::GetClass() const +{ + return _T("ListLabelElementUI"); +} + +LPVOID CListLabelElementUI::GetInterface(LPCTSTR pstrName) +{ + if( _tcscmp(pstrName, DUI_CTR_LISTLABELELEMENT) == 0 ) return static_cast(this); + return CListElementUI::GetInterface(pstrName); +} + +void CListLabelElementUI::DoEvent(TEventUI& event) +{ + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pOwner != NULL ) m_pOwner->DoEvent(event); + else CListElementUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_RBUTTONDOWN ) + { + if( IsEnabled() ){ + // m_pManager->SendNotify(this, DUI_MSGTYPE_ITEMCLICK); + Select(); + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_BUTTONUP ) + { + if( IsEnabled() ){ + m_pManager->SendNotify(this, DUI_MSGTYPE_ITEMCLICK); + } + return; + } + + if( event.Type == UIEVENT_MOUSEMOVE ) + { + return; + } + + if( event.Type == UIEVENT_MOUSEENTER ) + { + if( IsEnabled() ) { + m_uButtonState |= UISTATE_HOT; + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_MOUSELEAVE ) + { + if( (m_uButtonState & UISTATE_HOT) != 0 ) { + m_uButtonState &= ~UISTATE_HOT; + Invalidate(); + } + return; + } + CListElementUI::DoEvent(event); +} + +SIZE CListLabelElementUI::EstimateSize(SIZE szAvailable) +{ + if( m_pOwner == NULL ) return CDuiSize(0, 0); + + TListInfoUI* pInfo = m_pOwner->GetListInfo(); + SIZE cXY = m_cxyFixed; + if( cXY.cy == 0 && m_pManager != NULL ) { + cXY.cy = m_pManager->GetFontInfo(pInfo->nFont)->tm.tmHeight + 8; + cXY.cy += pInfo->rcTextPadding.top + pInfo->rcTextPadding.bottom; + } + + if( cXY.cx == 0 && m_pManager != NULL ) { + RECT rcText = { 0, 0, 9999, cXY.cy }; + if( pInfo->bShowHtml ) { + int nLinks = 0; + CRenderEngine::DrawHtmlText(m_pManager->GetPaintDC(), m_pManager, rcText, m_sText, 0, NULL, NULL, nLinks, DT_SINGLELINE | DT_CALCRECT | pInfo->uTextStyle & ~DT_RIGHT & ~DT_CENTER); + } + else { + CRenderEngine::DrawText(m_pManager->GetPaintDC(), m_pManager, rcText, m_sText, 0, pInfo->nFont, DT_SINGLELINE | DT_CALCRECT | pInfo->uTextStyle & ~DT_RIGHT & ~DT_CENTER); + } + cXY.cx = rcText.right - rcText.left + pInfo->rcTextPadding.left + pInfo->rcTextPadding.right; + } + + return cXY; +} + +void CListLabelElementUI::DoPaint(HDC hDC, const RECT& rcPaint) +{ + if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return; + DrawItemBk(hDC, m_rcItem); + DrawItemText(hDC, m_rcItem); +} + +void CListLabelElementUI::DrawItemText(HDC hDC, const RECT& rcItem) +{ + if( m_sText.IsEmpty() ) return; + + if( m_pOwner == NULL ) return; + TListInfoUI* pInfo = m_pOwner->GetListInfo(); + DWORD iTextColor = pInfo->dwTextColor; + if( (m_uButtonState & UISTATE_HOT) != 0 ) { + iTextColor = pInfo->dwHotTextColor; + } + if( IsSelected() ) { + iTextColor = pInfo->dwSelectedTextColor; + } + if( !IsEnabled() ) { + iTextColor = pInfo->dwDisabledTextColor; + } + int nLinks = 0; + RECT rcText = rcItem; + rcText.left += pInfo->rcTextPadding.left; + rcText.right -= pInfo->rcTextPadding.right; + rcText.top += pInfo->rcTextPadding.top; + rcText.bottom -= pInfo->rcTextPadding.bottom; + + if( pInfo->bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rcText, m_sText, iTextColor, \ + NULL, NULL, nLinks, DT_SINGLELINE | pInfo->uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rcText, m_sText, iTextColor, \ + pInfo->nFont, DT_SINGLELINE | pInfo->uTextStyle); +} + + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +CListTextElementUI::CListTextElementUI() : m_nLinks(0), m_nHoverLink(-1), m_pOwner(NULL) +{ + ::ZeroMemory(&m_rcLinks, sizeof(m_rcLinks)); +} + +CListTextElementUI::~CListTextElementUI() +{ + CDuiString* pText; + for( int it = 0; it < m_aTexts.GetSize(); it++ ) { + pText = static_cast(m_aTexts[it]); + if( pText ) delete pText; + } + m_aTexts.Empty(); +} + +LPCTSTR CListTextElementUI::GetClass() const +{ + return _T("ListTextElementUI"); +} + +LPVOID CListTextElementUI::GetInterface(LPCTSTR pstrName) +{ + if( _tcscmp(pstrName, DUI_CTR_LISTTEXTELEMENT) == 0 ) return static_cast(this); + return CListLabelElementUI::GetInterface(pstrName); +} + +UINT CListTextElementUI::GetControlFlags() const +{ + return UIFLAG_WANTRETURN | ( (IsEnabled() && m_nLinks > 0) ? UIFLAG_SETCURSOR : 0); +} + +LPCTSTR CListTextElementUI::GetText(int iIndex) const +{ + CDuiString* pText = static_cast(m_aTexts.GetAt(iIndex)); + if( pText ) return pText->GetData(); + return NULL; +} + +void CListTextElementUI::SetText(int iIndex, LPCTSTR pstrText) +{ + if( m_pOwner == NULL ) return; + TListInfoUI* pInfo = m_pOwner->GetListInfo(); + if( iIndex < 0 || iIndex >= pInfo->nColumns ) return; + while( m_aTexts.GetSize() < pInfo->nColumns ) { m_aTexts.Add(NULL); } + + CDuiString* pText = static_cast(m_aTexts[iIndex]); + if( (pText == NULL && pstrText == NULL) || (pText && *pText == pstrText) ) return; + + if ( pText ) //by cddjr 2011/10/20 + pText->Assign(pstrText); + else + m_aTexts.SetAt(iIndex, new CDuiString(pstrText)); + Invalidate(); +} + +void CListTextElementUI::SetOwner(CControlUI* pOwner) +{ + CListElementUI::SetOwner(pOwner); + m_pOwner = static_cast(pOwner->GetInterface(_T("IList"))); +} + +CDuiString* CListTextElementUI::GetLinkContent(int iIndex) +{ + if( iIndex >= 0 && iIndex < m_nLinks ) return &m_sLinks[iIndex]; + return NULL; +} + +void CListTextElementUI::DoEvent(TEventUI& event) +{ + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pOwner != NULL ) m_pOwner->DoEvent(event); + else CListLabelElementUI::DoEvent(event); + return; + } + + // When you hover over a link + if( event.Type == UIEVENT_SETCURSOR ) { + for( int i = 0; i < m_nLinks; i++ ) { + if( ::PtInRect(&m_rcLinks[i], event.ptMouse) ) { + ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND))); + return; + } + } + } + if( event.Type == UIEVENT_BUTTONUP && IsEnabled() ) { + for( int i = 0; i < m_nLinks; i++ ) { + if( ::PtInRect(&m_rcLinks[i], event.ptMouse) ) { + m_pManager->SendNotify(this, DUI_MSGTYPE_LINK, i); + return; + } + } + } + if( m_nLinks > 0 && event.Type == UIEVENT_MOUSEMOVE ) { + int nHoverLink = -1; + for( int i = 0; i < m_nLinks; i++ ) { + if( ::PtInRect(&m_rcLinks[i], event.ptMouse) ) { + nHoverLink = i; + break; + } + } + + if(m_nHoverLink != nHoverLink) { + Invalidate(); + m_nHoverLink = nHoverLink; + } + } + if( m_nLinks > 0 && event.Type == UIEVENT_MOUSELEAVE ) { + if(m_nHoverLink != -1) { + Invalidate(); + m_nHoverLink = -1; + } + } + CListLabelElementUI::DoEvent(event); +} + +SIZE CListTextElementUI::EstimateSize(SIZE szAvailable) +{ + TListInfoUI* pInfo = NULL; + if( m_pOwner ) pInfo = m_pOwner->GetListInfo(); + + SIZE cXY = m_cxyFixed; + if( cXY.cy == 0 && m_pManager != NULL ) { + cXY.cy = m_pManager->GetFontInfo(pInfo->nFont)->tm.tmHeight + 8; + if( pInfo ) cXY.cy += pInfo->rcTextPadding.top + pInfo->rcTextPadding.bottom; + } + + return cXY; +} + +void CListTextElementUI::DrawItemText(HDC hDC, const RECT& rcItem) +{ + if( m_pOwner == NULL ) return; + TListInfoUI* pInfo = m_pOwner->GetListInfo(); + DWORD iTextColor = pInfo->dwTextColor; + + if( (m_uButtonState & UISTATE_HOT) != 0 ) { + iTextColor = pInfo->dwHotTextColor; + } + if( IsSelected() ) { + iTextColor = pInfo->dwSelectedTextColor; + } + if( !IsEnabled() ) { + iTextColor = pInfo->dwDisabledTextColor; + } + IListCallbackUI* pCallback = m_pOwner->GetTextCallback(); + //ASSERT(pCallback); + //if( pCallback == NULL ) return; + + m_nLinks = 0; + int nLinks = lengthof(m_rcLinks); + for( int i = 0; i < pInfo->nColumns; i++ ) + { + RECT rcItem = { pInfo->rcColumn[i].left, m_rcItem.top, pInfo->rcColumn[i].right, m_rcItem.bottom }; + rcItem.left += pInfo->rcTextPadding.left; + rcItem.right -= pInfo->rcTextPadding.right; + rcItem.top += pInfo->rcTextPadding.top; + rcItem.bottom -= pInfo->rcTextPadding.bottom; + + CDuiString strText;//ʹLPCTSTR̫ by cddjr 2011/10/20 + if( pCallback ) strText = pCallback->GetItemText(this, m_iIndex, i); + else strText.Assign(GetText(i)); + if( pInfo->bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rcItem, strText.GetData(), iTextColor, \ + &m_rcLinks[m_nLinks], &m_sLinks[m_nLinks], nLinks, DT_SINGLELINE | pInfo->uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rcItem, strText.GetData(), iTextColor, \ + pInfo->nFont, DT_SINGLELINE | pInfo->uTextStyle); + + m_nLinks += nLinks; + nLinks = lengthof(m_rcLinks) - m_nLinks; + } + for( int i = m_nLinks; i < lengthof(m_rcLinks); i++ ) { + ::ZeroMemory(m_rcLinks + i, sizeof(RECT)); + ((CDuiString*)(m_sLinks + i))->Empty(); + } +} + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +CListContainerElementUI::CListContainerElementUI() : +m_iIndex(-1), +m_pOwner(NULL), +m_bSelected(false), +m_uButtonState(0) +{ +} + +LPCTSTR CListContainerElementUI::GetClass() const +{ + return _T("ListContainerElementUI"); +} + +UINT CListContainerElementUI::GetControlFlags() const +{ + return UIFLAG_WANTRETURN; +} + +LPVOID CListContainerElementUI::GetInterface(LPCTSTR pstrName) +{ + if( _tcscmp(pstrName, DUI_CTR_LISTITEM) == 0 ) return static_cast(this); + if( _tcscmp(pstrName, DUI_CTR_LISTCONTAINERELEMENT) == 0 ) return static_cast(this); + return CContainerUI::GetInterface(pstrName); +} + +IListOwnerUI* CListContainerElementUI::GetOwner() +{ + return m_pOwner; +} + +void CListContainerElementUI::SetOwner(CControlUI* pOwner) +{ + m_pOwner = static_cast(pOwner->GetInterface(_T("IListOwner"))); +} + +void CListContainerElementUI::SetVisible(bool bVisible) +{ + CContainerUI::SetVisible(bVisible); + if( !IsVisible() && m_bSelected) + { + m_bSelected = false; + if( m_pOwner != NULL ) m_pOwner->SelectItem(-1); + } +} + +void CListContainerElementUI::SetEnabled(bool bEnable) +{ + CControlUI::SetEnabled(bEnable); + if( !IsEnabled() ) { + m_uButtonState = 0; + } +} + +int CListContainerElementUI::GetIndex() const +{ + return m_iIndex; +} + +void CListContainerElementUI::SetIndex(int iIndex) +{ + m_iIndex = iIndex; +} + +void CListContainerElementUI::Invalidate() +{ + if( !IsVisible() ) return; + + if( GetParent() ) { + CContainerUI* pParentContainer = static_cast(GetParent()->GetInterface(_T("Container"))); + if( pParentContainer ) { + RECT rc = pParentContainer->GetPos(); + RECT rcInset = pParentContainer->GetInset(); + rc.left += rcInset.left; + rc.top += rcInset.top; + rc.right -= rcInset.right; + rc.bottom -= rcInset.bottom; + CScrollBarUI* pVerticalScrollBar = pParentContainer->GetVerticalScrollBar(); + if( pVerticalScrollBar && pVerticalScrollBar->IsVisible() ) rc.right -= pVerticalScrollBar->GetFixedWidth(); + CScrollBarUI* pHorizontalScrollBar = pParentContainer->GetHorizontalScrollBar(); + if( pHorizontalScrollBar && pHorizontalScrollBar->IsVisible() ) rc.bottom -= pHorizontalScrollBar->GetFixedHeight(); + + RECT invalidateRc = m_rcItem; + if( !::IntersectRect(&invalidateRc, &m_rcItem, &rc) ) + { + return; + } + + CControlUI* pParent = GetParent(); + RECT rcTemp; + RECT rcParent; + while( pParent = pParent->GetParent() ) + { + rcTemp = invalidateRc; + rcParent = pParent->GetPos(); + if( !::IntersectRect(&invalidateRc, &rcTemp, &rcParent) ) + { + return; + } + } + + if( m_pManager != NULL ) m_pManager->Invalidate(invalidateRc); + } + else { + CContainerUI::Invalidate(); + } + } + else { + CContainerUI::Invalidate(); + } +} + +bool CListContainerElementUI::Activate() +{ + if( !CContainerUI::Activate() ) return false; + if( m_pManager != NULL ) m_pManager->SendNotify(this, DUI_MSGTYPE_ITEMACTIVATE); + return true; +} + +bool CListContainerElementUI::IsSelected() const +{ + return m_bSelected; +} + +bool CListContainerElementUI::Select(bool bSelect) +{ + if( !IsEnabled() ) return false; + if( bSelect == m_bSelected ) return true; + m_bSelected = bSelect; + if( bSelect && m_pOwner != NULL ) m_pOwner->SelectItem(m_iIndex); + Invalidate(); + + return true; +} + +bool CListContainerElementUI::IsExpanded() const +{ + return false; +} + +bool CListContainerElementUI::Expand(bool /*bExpand = true*/) +{ + return false; +} + +void CListContainerElementUI::DoEvent(TEventUI& event) +{ + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pOwner != NULL ) m_pOwner->DoEvent(event); + else CContainerUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_DBLCLICK ) + { + if( IsEnabled() ) { + Activate(); + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_KEYDOWN && IsEnabled() ) + { + if( event.chKey == VK_RETURN ) { + Activate(); + Invalidate(); + return; + } + } + if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_RBUTTONDOWN ) + { + if( IsEnabled() ){ + // m_pManager->SendNotify(this, DUI_MSGTYPE_ITEMCLICK); + Select(); + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_BUTTONUP ) + { + if( IsEnabled() ){ + m_pManager->SendNotify(this, DUI_MSGTYPE_ITEMCLICK); + } + return; + } + if( event.Type == UIEVENT_MOUSEMOVE ) + { + return; + } + if( event.Type == UIEVENT_MOUSEENTER ) + { + if( IsEnabled() ) { + m_uButtonState |= UISTATE_HOT; + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_MOUSELEAVE ) + { + if( (m_uButtonState & UISTATE_HOT) != 0 ) { + m_uButtonState &= ~UISTATE_HOT; + Invalidate(); + } + return; + } + + // An important twist: The list-item will send the event not to its immediate + // parent but to the "attached" list. A list may actually embed several components + // in its path to the item, but key-presses etc. needs to go to the actual list. + if( m_pOwner != NULL ) m_pOwner->DoEvent(event); else CControlUI::DoEvent(event); +} + +void CListContainerElementUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) +{ + if( _tcscmp(pstrName, _T("selected")) == 0 ) Select(); + else CContainerUI::SetAttribute(pstrName, pstrValue); +} + +void CListContainerElementUI::DoPaint(HDC hDC, const RECT& rcPaint) +{ + if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return; + DrawItemBk(hDC, m_rcItem); + CContainerUI::DoPaint(hDC, rcPaint); +} + +void CListContainerElementUI::DrawItemText(HDC hDC, const RECT& rcItem) +{ + return; +} + +void CListContainerElementUI::DrawItemBk(HDC hDC, const RECT& rcItem) +{ + ASSERT(m_pOwner); + if( m_pOwner == NULL ) return; + TListInfoUI* pInfo = m_pOwner->GetListInfo(); + DWORD iBackColor = 0; + if( !pInfo->bAlternateBk || m_iIndex % 2 == 0 ) iBackColor = pInfo->dwBkColor; + + if( (m_uButtonState & UISTATE_HOT) != 0 ) { + iBackColor = pInfo->dwHotBkColor; + } + if( IsSelected() ) { + iBackColor = pInfo->dwSelectedBkColor; + } + if( !IsEnabled() ) { + iBackColor = pInfo->dwDisabledBkColor; + } + if ( iBackColor != 0 ) { + CRenderEngine::DrawColor(hDC, m_rcItem, GetAdjustColor(iBackColor)); + } + + if( !IsEnabled() ) { + if( !pInfo->sDisabledImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)pInfo->sDisabledImage) ) pInfo->sDisabledImage.Empty(); + else return; + } + } + if( IsSelected() ) { + if( !pInfo->sSelectedImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)pInfo->sSelectedImage) ) pInfo->sSelectedImage.Empty(); + else return; + } + } + if( (m_uButtonState & UISTATE_HOT) != 0 ) { + if( !pInfo->sHotImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)pInfo->sHotImage) ) pInfo->sHotImage.Empty(); + else return; + } + } + if( !m_sBkImage.IsEmpty() ) { + if( !pInfo->bAlternateBk || m_iIndex % 2 == 0 ) { + if( !DrawImage(hDC, (LPCTSTR)m_sBkImage) ) m_sBkImage.Empty(); + } + } + + if( m_sBkImage.IsEmpty() ) { + if( !pInfo->sBkImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)pInfo->sBkImage) ) pInfo->sBkImage.Empty(); + else return; + } + } + + if ( pInfo->dwLineColor != 0 ) { + RECT rcLine = { m_rcItem.left, m_rcItem.bottom - 1, m_rcItem.right, m_rcItem.bottom - 1 }; + CRenderEngine::DrawLine(hDC, rcLine, 1, GetAdjustColor(pInfo->dwLineColor)); + } +} + +void CListContainerElementUI::SetPos(RECT rc) +{ + CHorizontalLayoutUI::SetPos(rc); + if( m_pOwner == NULL ) return; + + CListUI* pList = static_cast(m_pOwner); + if (pList == NULL || _tcscmp(_T("ListUI"), pList->GetClass()) != 0 ) return; + + CListHeaderUI *pHeader = pList->GetHeader(); + if (pHeader == NULL || !pHeader->IsVisible()) + return; + + int nCount = m_items.GetSize(); + for (int i = 0; i < nCount; i++) + { + CControlUI *pListItem = static_cast(m_items[i]); + + CControlUI *pHeaderItem = pHeader->GetItemAt(i); + if (pHeaderItem == NULL) + return; + + RECT rcHeaderItem = pHeaderItem->GetPos(); + if (pListItem != NULL && !(rcHeaderItem.left ==0 && rcHeaderItem.right ==0) ) + { + RECT rt = pListItem->GetPos(); + rt.left =rcHeaderItem.left; + rt.right = rcHeaderItem.right; + pListItem->SetPos(rt); + } + + } + +} +} // namespace DuiLib diff --git a/DuiLib/Control/UIList.h b/DuiLib/Control/UIList.h new file mode 100644 index 00000000..ab7ca85a --- /dev/null +++ b/DuiLib/Control/UIList.h @@ -0,0 +1,464 @@ +#ifndef __UILIST_H__ +#define __UILIST_H__ + +#pragma once +#include "Layout/UIVerticalLayout.h" +#include "Layout/UIHorizontalLayout.h" + +namespace DuiLib { +///////////////////////////////////////////////////////////////////////////////////// +// + +typedef int (CALLBACK *PULVCompareFunc)(UINT_PTR, UINT_PTR, UINT_PTR); + +class CListHeaderUI; + +#define UILIST_MAX_COLUMNS 32 + +typedef struct tagTListInfoUI +{ + int nColumns; + RECT rcColumn[UILIST_MAX_COLUMNS]; + int nFont; + UINT uTextStyle; + RECT rcTextPadding; + DWORD dwTextColor; + DWORD dwBkColor; + CDuiString sBkImage; + bool bAlternateBk; + DWORD dwSelectedTextColor; + DWORD dwSelectedBkColor; + CDuiString sSelectedImage; + DWORD dwHotTextColor; + DWORD dwHotBkColor; + CDuiString sHotImage; + DWORD dwDisabledTextColor; + DWORD dwDisabledBkColor; + CDuiString sDisabledImage; + DWORD dwLineColor; + bool bShowHtml; + bool bMultiExpandable; +} TListInfoUI; + + +///////////////////////////////////////////////////////////////////////////////////// +// + +class IListCallbackUI +{ +public: + virtual LPCTSTR GetItemText(CControlUI* pList, int iItem, int iSubItem) = 0; +}; + +class IListOwnerUI +{ +public: + virtual TListInfoUI* GetListInfo() = 0; + virtual int GetCurSel() const = 0; + virtual bool SelectItem(int iIndex, bool bTakeFocus = false) = 0; + virtual void DoEvent(TEventUI& event) = 0; +}; + +class IListUI : public IListOwnerUI +{ +public: + virtual CListHeaderUI* GetHeader() const = 0; + virtual CContainerUI* GetList() const = 0; + virtual IListCallbackUI* GetTextCallback() const = 0; + virtual void SetTextCallback(IListCallbackUI* pCallback) = 0; + virtual bool ExpandItem(int iIndex, bool bExpand = true) = 0; + virtual int GetExpandedItem() const = 0; +}; + +class IListItemUI +{ +public: + virtual int GetIndex() const = 0; + virtual void SetIndex(int iIndex) = 0; + virtual IListOwnerUI* GetOwner() = 0; + virtual void SetOwner(CControlUI* pOwner) = 0; + virtual bool IsSelected() const = 0; + virtual bool Select(bool bSelect = true) = 0; + virtual bool IsExpanded() const = 0; + virtual bool Expand(bool bExpand = true) = 0; + virtual void DrawItemText(HDC hDC, const RECT& rcItem) = 0; +}; + + +///////////////////////////////////////////////////////////////////////////////////// +// + +class CListBodyUI; +class CListHeaderUI; + +class UILIB_API CListUI : public CVerticalLayoutUI, public IListUI +{ +public: + CListUI(); + + LPCTSTR GetClass() const; + UINT GetControlFlags() const; + LPVOID GetInterface(LPCTSTR pstrName); + + bool GetScrollSelect(); + void SetScrollSelect(bool bScrollSelect); + int GetCurSel() const; + int GetCurSelActivate() const; + bool SelectItem(int iIndex, bool bTakeFocus = false); + bool SelectItemActivate(int iIndex); // ˫ѡ + + CListHeaderUI* GetHeader() const; + CContainerUI* GetList() const; + TListInfoUI* GetListInfo(); + + CControlUI* GetItemAt(int iIndex) const; + int GetItemIndex(CControlUI* pControl) const; + bool SetItemIndex(CControlUI* pControl, int iIndex); + int GetCount() const; + bool Add(CControlUI* pControl); + bool AddAt(CControlUI* pControl, int iIndex); + bool Remove(CControlUI* pControl); + bool RemoveAt(int iIndex); + void RemoveAll(); + + void EnsureVisible(int iIndex); + void Scroll(int dx, int dy); + + bool IsDelayedDestroy() const; + void SetDelayedDestroy(bool bDelayed); + int GetChildPadding() const; + void SetChildPadding(int iPadding); + + void SetItemFont(int index); + void SetItemTextStyle(UINT uStyle); + void SetItemTextPadding(RECT rc); + void SetItemTextColor(DWORD dwTextColor); + void SetItemBkColor(DWORD dwBkColor); + void SetItemBkImage(LPCTSTR pStrImage); + void SetAlternateBk(bool bAlternateBk); + void SetSelectedItemTextColor(DWORD dwTextColor); + void SetSelectedItemBkColor(DWORD dwBkColor); + void SetSelectedItemImage(LPCTSTR pStrImage); + void SetHotItemTextColor(DWORD dwTextColor); + void SetHotItemBkColor(DWORD dwBkColor); + void SetHotItemImage(LPCTSTR pStrImage); + void SetDisabledItemTextColor(DWORD dwTextColor); + void SetDisabledItemBkColor(DWORD dwBkColor); + void SetDisabledItemImage(LPCTSTR pStrImage); + void SetItemLineColor(DWORD dwLineColor); + bool IsItemShowHtml(); + void SetItemShowHtml(bool bShowHtml = true); + RECT GetItemTextPadding() const; + DWORD GetItemTextColor() const; + DWORD GetItemBkColor() const; + LPCTSTR GetItemBkImage() const; + bool IsAlternateBk() const; + DWORD GetSelectedItemTextColor() const; + DWORD GetSelectedItemBkColor() const; + LPCTSTR GetSelectedItemImage() const; + DWORD GetHotItemTextColor() const; + DWORD GetHotItemBkColor() const; + LPCTSTR GetHotItemImage() const; + DWORD GetDisabledItemTextColor() const; + DWORD GetDisabledItemBkColor() const; + LPCTSTR GetDisabledItemImage() const; + DWORD GetItemLineColor() const; + + void SetMultiExpanding(bool bMultiExpandable); + int GetExpandedItem() const; + bool ExpandItem(int iIndex, bool bExpand = true); + + void SetPos(RECT rc); + void DoEvent(TEventUI& event); + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + IListCallbackUI* GetTextCallback() const; + void SetTextCallback(IListCallbackUI* pCallback); + + SIZE GetScrollPos() const; + SIZE GetScrollRange() const; + void SetScrollPos(SIZE szPos); + void LineUp(); + void LineDown(); + void PageUp(); + void PageDown(); + void HomeUp(); + void EndDown(); + void LineLeft(); + void LineRight(); + void PageLeft(); + void PageRight(); + void HomeLeft(); + void EndRight(); + void EnableScrollBar(bool bEnableVertical = true, bool bEnableHorizontal = false); + virtual CScrollBarUI* GetVerticalScrollBar() const; + virtual CScrollBarUI* GetHorizontalScrollBar() const; + BOOL SortItems(PULVCompareFunc pfnCompare, UINT_PTR dwData); +protected: + bool m_bScrollSelect; + int m_iCurSel; + int m_iCurSelActivate; // ˫ + int m_iExpandedItem; + IListCallbackUI* m_pCallback; + CListBodyUI* m_pList; + CListHeaderUI* m_pHeader; + TListInfoUI m_ListInfo; + +}; + +///////////////////////////////////////////////////////////////////////////////////// +// + + +class UILIB_API CListBodyUI : public CVerticalLayoutUI +{ +public: + CListBodyUI(CListUI* pOwner); + + void SetScrollPos(SIZE szPos); + void SetPos(RECT rc); + void DoEvent(TEventUI& event); + BOOL SortItems(PULVCompareFunc pfnCompare, UINT_PTR dwData); +protected: + static int __cdecl ItemComareFunc(void *pvlocale, const void *item1, const void *item2); + int __cdecl ItemComareFunc(const void *item1, const void *item2); +protected: + CListUI* m_pOwner; + PULVCompareFunc m_pCompareFunc; + UINT_PTR m_compareData; +}; + +///////////////////////////////////////////////////////////////////////////////////// +// + +class UILIB_API CListHeaderUI : public CHorizontalLayoutUI +{ +public: + CListHeaderUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + + SIZE EstimateSize(SIZE szAvailable); + void SetPos(RECT rc); + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + void SetScaleHeader(bool bIsScale); + bool IsScaleHeader() const; + +private: + bool m_bIsScaleHeader; +}; + + +///////////////////////////////////////////////////////////////////////////////////// +// + +class UILIB_API CListHeaderItemUI : public CContainerUI +{ +public: + CListHeaderItemUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + UINT GetControlFlags() const; + + void SetEnabled(bool bEnable = true); + + bool IsDragable() const; + void SetDragable(bool bDragable); + DWORD GetSepWidth() const; + void SetSepWidth(int iWidth); + DWORD GetTextStyle() const; + void SetTextStyle(UINT uStyle); + DWORD GetTextColor() const; + void SetTextColor(DWORD dwTextColor); + void SetTextPadding(RECT rc); + RECT GetTextPadding() const; + void SetFont(int index); + bool IsShowHtml(); + void SetShowHtml(bool bShowHtml = true); + LPCTSTR GetNormalImage() const; + void SetNormalImage(LPCTSTR pStrImage); + LPCTSTR GetHotImage() const; + void SetHotImage(LPCTSTR pStrImage); + LPCTSTR GetPushedImage() const; + void SetPushedImage(LPCTSTR pStrImage); + LPCTSTR GetFocusedImage() const; + void SetFocusedImage(LPCTSTR pStrImage); + LPCTSTR GetSepImage() const; + void SetSepImage(LPCTSTR pStrImage); + void SetScale(int nScale); + int GetScale() const; + + void DoEvent(TEventUI& event); + SIZE EstimateSize(SIZE szAvailable); + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + RECT GetThumbRect() const; + + void PaintText(HDC hDC); + void PaintStatusImage(HDC hDC); + +protected: + POINT ptLastMouse; + bool m_bDragable; + UINT m_uButtonState; + int m_iSepWidth; + DWORD m_dwTextColor; + int m_iFont; + UINT m_uTextStyle; + bool m_bShowHtml; + RECT m_rcTextPadding; + CDuiString m_sNormalImage; + CDuiString m_sHotImage; + CDuiString m_sPushedImage; + CDuiString m_sFocusedImage; + CDuiString m_sSepImage; + CDuiString m_sSepImageModify; + int m_nScale; +}; + + +///////////////////////////////////////////////////////////////////////////////////// +// + +class UILIB_API CListElementUI : public CControlUI, public IListItemUI +{ +public: + CListElementUI(); + + LPCTSTR GetClass() const; + UINT GetControlFlags() const; + LPVOID GetInterface(LPCTSTR pstrName); + + void SetEnabled(bool bEnable = true); + + int GetIndex() const; + void SetIndex(int iIndex); + + IListOwnerUI* GetOwner(); + void SetOwner(CControlUI* pOwner); + void SetVisible(bool bVisible = true); + + bool IsSelected() const; + bool Select(bool bSelect = true); + bool IsExpanded() const; + bool Expand(bool bExpand = true); + + void Invalidate(); // ֱCControl::Invalidateᵼ¹ˢ£дˢ + bool Activate(); + + void DoEvent(TEventUI& event); + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + void DrawItemBk(HDC hDC, const RECT& rcItem); + +protected: + int m_iIndex; + bool m_bSelected; + UINT m_uButtonState; + IListOwnerUI* m_pOwner; +}; + + +///////////////////////////////////////////////////////////////////////////////////// +// + +class UILIB_API CListLabelElementUI : public CListElementUI +{ +public: + CListLabelElementUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + + void DoEvent(TEventUI& event); + SIZE EstimateSize(SIZE szAvailable); + void DoPaint(HDC hDC, const RECT& rcPaint); + + void DrawItemText(HDC hDC, const RECT& rcItem); +}; + + +///////////////////////////////////////////////////////////////////////////////////// +// + +class UILIB_API CListTextElementUI : public CListLabelElementUI +{ +public: + CListTextElementUI(); + ~CListTextElementUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + UINT GetControlFlags() const; + + LPCTSTR GetText(int iIndex) const; + void SetText(int iIndex, LPCTSTR pstrText); + + void SetOwner(CControlUI* pOwner); + CDuiString* GetLinkContent(int iIndex); + + void DoEvent(TEventUI& event); + SIZE EstimateSize(SIZE szAvailable); + + void DrawItemText(HDC hDC, const RECT& rcItem); + +protected: + enum { MAX_LINK = 8 }; + int m_nLinks; + RECT m_rcLinks[MAX_LINK]; + CDuiString m_sLinks[MAX_LINK]; + int m_nHoverLink; + IListUI* m_pOwner; + CStdPtrArray m_aTexts; +}; + +///////////////////////////////////////////////////////////////////////////////////// +// + +class UILIB_API CListContainerElementUI : public CHorizontalLayoutUI, public IListItemUI +{ +public: + CListContainerElementUI(); + + LPCTSTR GetClass() const; + UINT GetControlFlags() const; + LPVOID GetInterface(LPCTSTR pstrName); + + int GetIndex() const; + void SetIndex(int iIndex); + + IListOwnerUI* GetOwner(); + void SetOwner(CControlUI* pOwner); + void SetVisible(bool bVisible = true); + void SetEnabled(bool bEnable = true); + + bool IsSelected() const; + bool Select(bool bSelect = true); + bool IsExpanded() const; + bool Expand(bool bExpand = true); + + void Invalidate(); // ֱCControl::Invalidateᵼ¹ˢ£дˢ + bool Activate(); + + void DoEvent(TEventUI& event); + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + void DoPaint(HDC hDC, const RECT& rcPaint); + + virtual void DrawItemText(HDC hDC, const RECT& rcItem); + virtual void DrawItemBk(HDC hDC, const RECT& rcItem); + + void SetPos(RECT rc); + +protected: + int m_iIndex; + bool m_bSelected; + UINT m_uButtonState; + IListOwnerUI* m_pOwner; + +}; + +} // namespace DuiLib + +#endif // __UILIST_H__ diff --git a/DuiLib/Control/UIMenu.cpp b/DuiLib/Control/UIMenu.cpp new file mode 100644 index 00000000..d6bdc087 --- /dev/null +++ b/DuiLib/Control/UIMenu.cpp @@ -0,0 +1,977 @@ +#include "StdAfx.h" + +#include "UIMenu.h" + +namespace DuiLib { + +///////////////////////////////////////////////////////////////////////////////////// +// + +CMenuUI::CMenuUI() +{ + if (GetHeader() != NULL) + GetHeader()->SetVisible(false); +} + +CMenuUI::~CMenuUI() +{} + +LPCTSTR CMenuUI::GetClass() const +{ + return _T("MenuUI"); +} + +LPVOID CMenuUI::GetInterface(LPCTSTR pstrName) +{ + if( _tcsicmp(pstrName, _T("Menu")) == 0 ) return static_cast(this); + return CListUI::GetInterface(pstrName); +} + +void CMenuUI::DoEvent(TEventUI& event) +{ + return __super::DoEvent(event); +} + +bool CMenuUI::Add(CControlUI* pControl) +{ + CMenuElementUI* pMenuItem = static_cast(pControl->GetInterface(_T("MenuElement"))); + if (pMenuItem == NULL) + return false; + + for (int i = 0; i < pMenuItem->GetCount(); ++i) + { + if (pMenuItem->GetItemAt(i)->GetInterface(_T("MenuElement")) != NULL) + { + (static_cast(pMenuItem->GetItemAt(i)->GetInterface(_T("MenuElement"))))->SetInternVisible(false); + } + } + return CListUI::Add(pControl); +} + +bool CMenuUI::AddAt(CControlUI* pControl, int iIndex) +{ + CMenuElementUI* pMenuItem = static_cast(pControl->GetInterface(_T("MenuElement"))); + if (pMenuItem == NULL) + return false; + + for (int i = 0; i < pMenuItem->GetCount(); ++i) + { + if (pMenuItem->GetItemAt(i)->GetInterface(_T("MenuElement")) != NULL) + { + (static_cast(pMenuItem->GetItemAt(i)->GetInterface(_T("MenuElement"))))->SetInternVisible(false); + } + } + return CListUI::AddAt(pControl, iIndex); +} + +int CMenuUI::GetItemIndex(CControlUI* pControl) const +{ + CMenuElementUI* pMenuItem = static_cast(pControl->GetInterface(_T("MenuElement"))); + if (pMenuItem == NULL) + return -1; + + return __super::GetItemIndex(pControl); +} + +bool CMenuUI::SetItemIndex(CControlUI* pControl, int iIndex) +{ + CMenuElementUI* pMenuItem = static_cast(pControl->GetInterface(_T("MenuElement"))); + if (pMenuItem == NULL) + return false; + + return __super::SetItemIndex(pControl, iIndex); +} + +bool CMenuUI::Remove(CControlUI* pControl) +{ + CMenuElementUI* pMenuItem = static_cast(pControl->GetInterface(_T("MenuElement"))); + if (pMenuItem == NULL) + return false; + + return __super::Remove(pControl); +} + +SIZE CMenuUI::EstimateSize(SIZE szAvailable) +{ + int cxFixed = 0; + int cyFixed = 0; + for( int it = 0; it < GetCount(); it++ ) { + CControlUI* pControl = static_cast(GetItemAt(it)); + if( !pControl->IsVisible() ) continue; + SIZE sz = pControl->EstimateSize(szAvailable); + cyFixed += sz.cy; + if( cxFixed < sz.cx ) + cxFixed = sz.cx; + } + return CDuiSize(cxFixed, cyFixed); +} + +void CMenuUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) +{ + CListUI::SetAttribute(pstrName, pstrValue); +} + +///////////////////////////////////////////////////////////////////////////////////// +// + +CMenuWnd::CMenuWnd(): +m_pOwner(NULL), +m_pLayout(), +m_xml(_T("")) +{ + m_dwAlignment = eMenuAlignment_Left | eMenuAlignment_Top; +} + +CMenuWnd::~CMenuWnd() +{ + +} + +BOOL CMenuWnd::Receive(ContextMenuParam param) +{ + switch (param.wParam) + { + case 1: + Close(); + break; + case 2: + { + HWND hParent = GetParent(m_hWnd); + while (hParent != NULL) + { + if (hParent == param.hWnd) + { + Close(); + break; + } + hParent = GetParent(hParent); + } + } + break; + default: + break; + } + + return TRUE; +} + +void CMenuWnd::Init(CMenuElementUI* pOwner, STRINGorID xml, POINT point, + CPaintManagerUI* pMainPaintManager, std::map* pMenuCheckInfo/* = NULL*/, + DWORD dwAlignment/* = eMenuAlignment_Left | eMenuAlignment_Top*/) +{ + + m_BasedPoint = point; + m_pOwner = pOwner; + m_pLayout = NULL; + m_xml = xml; + m_dwAlignment = dwAlignment; + + // һ˵Ĵ + if (pOwner == NULL) + { + ASSERT(pMainPaintManager != NULL); + CMenuWnd::GetGlobalContextMenuObserver().SetManger(pMainPaintManager); + if (pMenuCheckInfo != NULL) + CMenuWnd::GetGlobalContextMenuObserver().SetMenuCheckInfo(pMenuCheckInfo); + } + + CMenuWnd::GetGlobalContextMenuObserver().AddReceiver(this); + + Create((m_pOwner == NULL) ? pMainPaintManager->GetPaintWindow() : m_pOwner->GetManager()->GetPaintWindow(), NULL, WS_POPUP , WS_EX_TOOLWINDOW | WS_EX_TOPMOST, CDuiRect()); + + // HACK: Don't deselect the parent's caption + HWND hWndParent = m_hWnd; + while( ::GetParent(hWndParent) != NULL ) hWndParent = ::GetParent(hWndParent); + + ::ShowWindow(m_hWnd, SW_SHOW); + ::SendMessage(hWndParent, WM_NCACTIVATE, TRUE, 0L); +} + +LPCTSTR CMenuWnd::GetWindowClassName() const +{ + return _T("DuiMenuWnd"); +} + + +void CMenuWnd::Notify(TNotifyUI& msg) +{ + if( CMenuWnd::GetGlobalContextMenuObserver().GetManager() != NULL) + { + if( msg.sType == _T("click") || msg.sType == _T("valuechanged") ) + { + CMenuWnd::GetGlobalContextMenuObserver().GetManager()->SendNotify(msg, false); + } + } + +} + +CControlUI* CMenuWnd::CreateControl( LPCTSTR pstrClassName ) +{ + if (_tcsicmp(pstrClassName, _T("Menu")) == 0) + { + return new CMenuUI(); + } + else if (_tcsicmp(pstrClassName, _T("MenuElement")) == 0) + { + return new CMenuElementUI(); + } + return NULL; +} + + +void CMenuWnd::OnFinalMessage(HWND hWnd) +{ + RemoveObserver(); + if( m_pOwner != NULL ) { + for( int i = 0; i < m_pOwner->GetCount(); i++ ) { + if( static_cast(m_pOwner->GetItemAt(i)->GetInterface(_T("MenuElement"))) != NULL ) { + (static_cast(m_pOwner->GetItemAt(i)))->SetOwner(m_pOwner->GetParent()); + (static_cast(m_pOwner->GetItemAt(i)))->SetVisible(false); + (static_cast(m_pOwner->GetItemAt(i)->GetInterface(_T("MenuElement"))))->SetInternVisible(false); + } + } + m_pOwner->m_pWindow = NULL; + m_pOwner->m_uButtonState &= ~ UISTATE_PUSHED; + m_pOwner->Invalidate(); + } + delete this; +} + +LRESULT CMenuWnd::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + if( m_pOwner != NULL) { + LONG styleValue = ::GetWindowLong(*this, GWL_STYLE); + styleValue &= ~WS_CAPTION; + ::SetWindowLong(*this, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); + RECT rcClient; + ::GetClientRect(*this, &rcClient); + ::SetWindowPos(*this, NULL, rcClient.left, rcClient.top, rcClient.right - rcClient.left, \ + rcClient.bottom - rcClient.top, SWP_FRAMECHANGED); + + m_pm.Init(m_hWnd); + // The trick is to add the items to the new container. Their owner gets + // reassigned by this operation - which is why it is important to reassign + // the items back to the righfull owner/manager when the window closes. + m_pLayout = new CMenuUI(); + m_pm.UseParentResource(m_pOwner->GetManager()); + m_pLayout->SetManager(&m_pm, NULL, true); + LPCTSTR pDefaultAttributes = m_pOwner->GetManager()->GetDefaultAttributeList(_T("Menu")); + if( pDefaultAttributes ) { + m_pLayout->ApplyAttributeList(pDefaultAttributes); + } + m_pLayout->SetAutoDestroy(false); + + for( int i = 0; i < m_pOwner->GetCount(); i++ ) { + if(m_pOwner->GetItemAt(i)->GetInterface(_T("MenuElement")) != NULL ){ + (static_cast(m_pOwner->GetItemAt(i)))->SetOwner(m_pLayout); + m_pLayout->Add(static_cast(m_pOwner->GetItemAt(i))); + } + } + + CShadowUI *pShadow = m_pOwner->GetManager()->GetShadow(); + pShadow->CopyShadow(m_pm.GetShadow()); + + pShadow->ShowShadow(false); + + m_pm.AttachDialog(m_pLayout); + m_pm.AddNotifier(this); + + ResizeSubMenu(); + } + else { + m_pm.Init(m_hWnd); + + CDialogBuilder builder; + + CControlUI* pRoot = builder.Create(m_xml,UINT(0), this, &m_pm); + m_pm.GetShadow()->ShowShadow(false); + m_pm.AttachDialog(pRoot); + m_pm.AddNotifier(this); + + ResizeMenu(); + } + + m_pm.GetShadow()->ShowShadow(true); + m_pm.GetShadow()->Create(&m_pm); + return 0; +} + +CMenuUI* CMenuWnd::GetMenuUI() +{ + return static_cast(m_pm.GetRoot()); +} + +void CMenuWnd::ResizeMenu() +{ + CControlUI* pRoot = m_pm.GetRoot(); + +#if defined(WIN32) && !defined(UNDER_CE) + MONITORINFO oMonitor = {}; + oMonitor.cbSize = sizeof(oMonitor); + ::GetMonitorInfo(::MonitorFromWindow(*this, MONITOR_DEFAULTTOPRIMARY), &oMonitor); + CDuiRect rcWork = oMonitor.rcWork; +#else + CDuiRect rcWork; + GetWindowRect(m_pOwner->GetManager()->GetPaintWindow(), &rcWork); +#endif + SIZE szAvailable = { rcWork.right - rcWork.left, rcWork.bottom - rcWork.top }; + szAvailable = pRoot->EstimateSize(szAvailable); + m_pm.SetInitSize(szAvailable.cx, szAvailable.cy); + + //MenuǩΪxmlĸڵ + CMenuUI *pMenuRoot = static_cast(pRoot); + ASSERT(pMenuRoot); + + SIZE szInit = m_pm.GetInitSize(); + CDuiRect rc; + CDuiPoint point = m_BasedPoint; + rc.left = point.x; + rc.top = point.y; + rc.right = rc.left + szInit.cx; + rc.bottom = rc.top + szInit.cy; + + int nWidth = rc.GetWidth(); + int nHeight = rc.GetHeight(); + + if (m_dwAlignment & eMenuAlignment_Right) + { + rc.right = point.x; + rc.left = rc.right - nWidth; + } + + if (m_dwAlignment & eMenuAlignment_Bottom) + { + rc.bottom = point.y; + rc.top = rc.bottom - nHeight; + } + + SetForegroundWindow(m_hWnd); + MoveWindow(m_hWnd, rc.left, rc.top, rc.GetWidth(), rc.GetHeight(), FALSE); + SetWindowPos(m_hWnd, HWND_TOPMOST, rc.left, rc.top, + rc.GetWidth(), rc.GetHeight() + pMenuRoot->GetInset().bottom + pMenuRoot->GetInset().top, + SWP_SHOWWINDOW); +} + +void CMenuWnd::ResizeSubMenu() +{ + // Position the popup window in absolute space + RECT rcOwner = m_pOwner->GetPos(); + RECT rc = rcOwner; + + int cxFixed = 0; + int cyFixed = 0; + +#if defined(WIN32) && !defined(UNDER_CE) + MONITORINFO oMonitor = {}; + oMonitor.cbSize = sizeof(oMonitor); + ::GetMonitorInfo(::MonitorFromWindow(*this, MONITOR_DEFAULTTOPRIMARY), &oMonitor); + CDuiRect rcWork = oMonitor.rcWork; +#else + CDuiRect rcWork; + GetWindowRect(m_pOwner->GetManager()->GetPaintWindow(), &rcWork); +#endif + SIZE szAvailable = { rcWork.right - rcWork.left, rcWork.bottom - rcWork.top }; + + for( int it = 0; it < m_pOwner->GetCount(); it++ ) { + if(m_pOwner->GetItemAt(it)->GetInterface(_T("MenuElement")) != NULL ){ + CControlUI* pControl = static_cast(m_pOwner->GetItemAt(it)); + SIZE sz = pControl->EstimateSize(szAvailable); + cyFixed += sz.cy; + + if( cxFixed < sz.cx ) + cxFixed = sz.cx; + } + } + + RECT rcWindow; + GetWindowRect(m_pOwner->GetManager()->GetPaintWindow(), &rcWindow); + + rc.top = rcOwner.top; + rc.bottom = rc.top + cyFixed; + ::MapWindowRect(m_pOwner->GetManager()->GetPaintWindow(), HWND_DESKTOP, &rc); + rc.left = rcWindow.right; + rc.right = rc.left + cxFixed; + rc.right += 2; + + bool bReachBottom = false; + bool bReachRight = false; + LONG chRightAlgin = 0; + LONG chBottomAlgin = 0; + + RECT rcPreWindow = {0}; + MenuObserverImpl::Iterator iterator(CMenuWnd::GetGlobalContextMenuObserver()); + MenuMenuReceiverImplBase* pReceiver = iterator.next(); + while( pReceiver != NULL ) { + CMenuWnd* pContextMenu = dynamic_cast(pReceiver); + if( pContextMenu != NULL ) { + GetWindowRect(pContextMenu->GetHWND(), &rcPreWindow); + + bReachRight = rcPreWindow.left >= rcWindow.right; + bReachBottom = rcPreWindow.top >= rcWindow.bottom; + if( pContextMenu->GetHWND() == m_pOwner->GetManager()->GetPaintWindow() + || bReachBottom || bReachRight ) + break; + } + pReceiver = iterator.next(); + } + + if (bReachBottom) + { + rc.bottom = rcWindow.top; + rc.top = rc.bottom - cyFixed; + } + + if (bReachRight) + { + rc.right = rcWindow.left; + rc.left = rc.right - cxFixed; + } + + if( rc.bottom > rcWork.bottom ) + { + rc.bottom = rc.top; + rc.top = rc.bottom - cyFixed; + } + + if (rc.right > rcWork.right) + { + rc.right = rcWindow.left; + rc.left = rc.right - cxFixed; + } + + if( rc.top < rcWork.top ) + { + rc.top = rcOwner.top; + rc.bottom = rc.top + cyFixed; + } + + if (rc.left < rcWork.left) + { + rc.left = rcWindow.right; + rc.right = rc.left + cxFixed; + } + + MoveWindow(m_hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top + m_pLayout->GetInset().top + m_pLayout->GetInset().bottom, FALSE); + +} + + +LRESULT CMenuWnd::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + HWND hFocusWnd = (HWND)wParam; + + BOOL bInMenuWindowList = FALSE; + ContextMenuParam param; + param.hWnd = GetHWND(); + + MenuObserverImpl::Iterator iterator(CMenuWnd::GetGlobalContextMenuObserver()); + MenuMenuReceiverImplBase* pReceiver = iterator.next(); + while( pReceiver != NULL ) { + CMenuWnd* pContextMenu = dynamic_cast(pReceiver); + if( pContextMenu != NULL && pContextMenu->GetHWND() == hFocusWnd ) { + bInMenuWindowList = TRUE; + break; + } + pReceiver = iterator.next(); + } + + if( !bInMenuWindowList ) { + param.wParam = 1; + CMenuWnd::GetGlobalContextMenuObserver().RBroadcast(param); + + return 0; + } + return 0; +} +LRESULT CMenuWnd::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + SIZE szRoundCorner = m_pm.GetRoundCorner(); + if( !::IsIconic(*this) ) { + CDuiRect rcWnd; + ::GetWindowRect(*this, &rcWnd); + rcWnd.Offset(-rcWnd.left, -rcWnd.top); + rcWnd.right++; rcWnd.bottom++; + HRGN hRgn = ::CreateRoundRectRgn(rcWnd.left, rcWnd.top, rcWnd.right, rcWnd.bottom, szRoundCorner.cx, szRoundCorner.cy); + ::SetWindowRgn(*this, hRgn, TRUE); + ::DeleteObject(hRgn); + } + bHandled = FALSE; + return 0; +} + +LRESULT CMenuWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LRESULT lRes = 0; + BOOL bHandled = TRUE; + switch( uMsg ) + { + case WM_CREATE: + lRes = OnCreate(uMsg, wParam, lParam, bHandled); + break; + case WM_KILLFOCUS: + lRes = OnKillFocus(uMsg, wParam, lParam, bHandled); + break; + case WM_KEYDOWN: + if( wParam == VK_ESCAPE || wParam == VK_LEFT) + Close(); + break; + case WM_SIZE: + lRes = OnSize(uMsg, wParam, lParam, bHandled); + break; + case WM_CLOSE: + if( m_pOwner != NULL ) + { + m_pOwner->SetManager(m_pOwner->GetManager(), m_pOwner->GetParent(), false); + m_pOwner->SetPos(m_pOwner->GetPos()); + m_pOwner->SetFocus(); + } + break; + case WM_RBUTTONDOWN: + case WM_CONTEXTMENU: + case WM_RBUTTONUP: + case WM_RBUTTONDBLCLK: + return 0L; + break; + default: + bHandled = FALSE; + break; + } + + if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes; + return CWindowWnd::HandleMessage(uMsg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////// +// + +CMenuElementUI::CMenuElementUI(): +m_pWindow(NULL), +m_bDrawLine(false), +m_dwLineColor(DEFAULT_LINE_COLOR), +m_bCheckItem(false), +m_bShowExplandIcon(false) +{ + m_cxyFixed.cy = ITEM_DEFAULT_HEIGHT; + m_cxyFixed.cx = ITEM_DEFAULT_WIDTH; + m_szIconSize.cy = ITEM_DEFAULT_ICON_SIZE; + m_szIconSize.cx = ITEM_DEFAULT_ICON_SIZE; + + m_rcLinePadding.top = m_rcLinePadding.bottom = 0; + m_rcLinePadding.left = DEFAULT_LINE_LEFT_INSET; + m_rcLinePadding.right = DEFAULT_LINE_RIGHT_INSET; +} + +CMenuElementUI::~CMenuElementUI() +{} + +LPCTSTR CMenuElementUI::GetClass() const +{ + return _T("MenuElementUI"); +} + +LPVOID CMenuElementUI::GetInterface(LPCTSTR pstrName) +{ + if( _tcsicmp(pstrName, _T("MenuElement")) == 0 ) return static_cast(this); + return CListContainerElementUI::GetInterface(pstrName); +} + +void CMenuElementUI::DoPaint(HDC hDC, const RECT& rcPaint) +{ + if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return; + + if(m_bDrawLine) + { + RECT rcLine = { m_rcItem.left + m_rcLinePadding.left, m_rcItem.top + m_cxyFixed.cy/2, m_rcItem.right - m_rcLinePadding.right, m_rcItem.top + m_cxyFixed.cy/2 }; + CRenderEngine::DrawLine(hDC, rcLine, 1, m_dwLineColor); + } + else + { + CMenuElementUI::DrawItemBk(hDC, m_rcItem); + DrawItemText(hDC, m_rcItem); + DrawItemIcon(hDC, m_rcItem); + DrawItemExpland(hDC, m_rcItem); + for (int i = 0; i < GetCount(); ++i) + { + if (GetItemAt(i)->GetInterface(_T("MenuElement")) == NULL) + GetItemAt(i)->DoPaint(hDC, rcPaint); + } + } +} + +void CMenuElementUI::DrawItemIcon(HDC hDC, const RECT& rcItem) +{ + if ( m_strIcon != _T("") ) + { + + if (!(m_bCheckItem && !GetChecked())) + { + CDuiString pStrImage; + pStrImage.Format(_T("file='%s' dest='%d,%d,%d,%d'"), m_strIcon.GetData(), + (ITEM_DEFAULT_ICON_WIDTH - m_szIconSize.cx)/2, + (m_cxyFixed.cy - m_szIconSize.cy)/2, + (ITEM_DEFAULT_ICON_WIDTH - m_szIconSize.cx)/2 + m_szIconSize.cx, + (m_cxyFixed.cy - m_szIconSize.cy)/2 + m_szIconSize.cy); + CRenderEngine::DrawImageString(hDC, m_pManager, m_rcItem, m_rcPaint, pStrImage, _T("")); + } + } +} + +void CMenuElementUI::DrawItemExpland(HDC hDC, const RECT& rcItem) +{ + if (m_bShowExplandIcon) + { + CDuiString strExplandIcon; + strExplandIcon = GetManager()->GetDefaultAttributeList(_T("ExplandIcon")); + CDuiString strBkImage; + strBkImage.Format(_T("file='%s' dest='%d,%d,%d,%d'"), strExplandIcon.GetData(), + m_cxyFixed.cx - ITEM_DEFAULT_EXPLAND_ICON_WIDTH + (ITEM_DEFAULT_EXPLAND_ICON_WIDTH - ITEM_DEFAULT_EXPLAND_ICON_SIZE)/2, + (m_cxyFixed.cy - ITEM_DEFAULT_EXPLAND_ICON_SIZE)/2, + m_cxyFixed.cx - ITEM_DEFAULT_EXPLAND_ICON_WIDTH + (ITEM_DEFAULT_EXPLAND_ICON_WIDTH - ITEM_DEFAULT_EXPLAND_ICON_SIZE)/2 + ITEM_DEFAULT_EXPLAND_ICON_SIZE, + (m_cxyFixed.cy - ITEM_DEFAULT_EXPLAND_ICON_SIZE)/2 + ITEM_DEFAULT_EXPLAND_ICON_SIZE); + + CRenderEngine::DrawImageString(hDC, m_pManager, m_rcItem, m_rcPaint, strBkImage, _T("")); + } +} + + +void CMenuElementUI::DrawItemText(HDC hDC, const RECT& rcItem) +{ + if( m_sText.IsEmpty() ) return; + + if( m_pOwner == NULL ) return; + TListInfoUI* pInfo = m_pOwner->GetListInfo(); + DWORD iTextColor = pInfo->dwTextColor; + if( (m_uButtonState & UISTATE_HOT) != 0 ) { + iTextColor = pInfo->dwHotTextColor; + } + if( IsSelected() ) { + iTextColor = pInfo->dwSelectedTextColor; + } + if( !IsEnabled() ) { + iTextColor = pInfo->dwDisabledTextColor; + } + int nLinks = 0; + RECT rcText = rcItem; + rcText.left += pInfo->rcTextPadding.left; + rcText.right -= pInfo->rcTextPadding.right; + rcText.top += pInfo->rcTextPadding.top; + rcText.bottom -= pInfo->rcTextPadding.bottom; + + if( pInfo->bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rcText, m_sText, iTextColor, \ + NULL, NULL, nLinks, DT_SINGLELINE | pInfo->uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rcText, m_sText, iTextColor, \ + pInfo->nFont, DT_SINGLELINE | pInfo->uTextStyle); +} + + +SIZE CMenuElementUI::EstimateSize(SIZE szAvailable) +{ + SIZE cXY = {0}; + for( int it = 0; it < GetCount(); it++ ) { + CControlUI* pControl = static_cast(GetItemAt(it)); + if( !pControl->IsVisible() ) continue; + SIZE sz = pControl->EstimateSize(szAvailable); + cXY.cy += sz.cy; + if( cXY.cx < sz.cx ) + cXY.cx = sz.cx; + } + if(cXY.cy == 0) { + TListInfoUI* pInfo = m_pOwner->GetListInfo(); + + DWORD iTextColor = pInfo->dwTextColor; + if( (m_uButtonState & UISTATE_HOT) != 0 ) { + iTextColor = pInfo->dwHotTextColor; + } + if( IsSelected() ) { + iTextColor = pInfo->dwSelectedTextColor; + } + if( !IsEnabled() ) { + iTextColor = pInfo->dwDisabledTextColor; + } + + RECT rcText = { 0, 0, MAX(szAvailable.cx, m_cxyFixed.cx), 9999 }; + rcText.left += pInfo->rcTextPadding.left; + rcText.right -= pInfo->rcTextPadding.right; + if( pInfo->bShowHtml ) { + int nLinks = 0; + CRenderEngine::DrawHtmlText(m_pManager->GetPaintDC(), m_pManager, rcText, m_sText, iTextColor, NULL, NULL, nLinks, DT_CALCRECT | pInfo->uTextStyle); + } + else { + CRenderEngine::DrawText(m_pManager->GetPaintDC(), m_pManager, rcText, m_sText, iTextColor, pInfo->nFont, DT_CALCRECT | pInfo->uTextStyle); + } + cXY.cx = rcText.right - rcText.left + pInfo->rcTextPadding.left + pInfo->rcTextPadding.right + 20; + cXY.cy = rcText.bottom - rcText.top + pInfo->rcTextPadding.top + pInfo->rcTextPadding.bottom; + } + + if( m_cxyFixed.cy != 0 ) cXY.cy = m_cxyFixed.cy; + if ( cXY.cx < m_cxyFixed.cx ) + cXY.cx = m_cxyFixed.cx; + + m_cxyFixed.cy = cXY.cy; + m_cxyFixed.cx = cXY.cx; + return cXY; +} + +void CMenuElementUI::DoEvent(TEventUI& event) +{ + + if( event.Type == UIEVENT_MOUSEENTER ) + { + CListContainerElementUI::DoEvent(event); + if( m_pWindow ) return; + bool hasSubMenu = false; + for( int i = 0; i < GetCount(); ++i ) + { + if( GetItemAt(i)->GetInterface(_T("MenuElement")) != NULL ) + { + (static_cast(GetItemAt(i)->GetInterface(_T("MenuElement"))))->SetVisible(true); + (static_cast(GetItemAt(i)->GetInterface(_T("MenuElement"))))->SetInternVisible(true); + + hasSubMenu = true; + } + } + if( hasSubMenu ) + { + m_pOwner->SelectItem(GetIndex(), true); + CreateMenuWnd(); + } + else + { + ContextMenuParam param; + param.hWnd = m_pManager->GetPaintWindow(); + param.wParam = 2; + CMenuWnd::GetGlobalContextMenuObserver().RBroadcast(param); + m_pOwner->SelectItem(GetIndex(), true); + } + return; + } + + if( event.Type == UIEVENT_BUTTONUP ) + { + if( IsEnabled() ){ + CListContainerElementUI::DoEvent(event); + + if( m_pWindow ) return; + + bool hasSubMenu = false; + for( int i = 0; i < GetCount(); ++i ) { + if( GetItemAt(i)->GetInterface(_T("MenuElement")) != NULL ) { + (static_cast(GetItemAt(i)->GetInterface(_T("MenuElement"))))->SetVisible(true); + (static_cast(GetItemAt(i)->GetInterface(_T("MenuElement"))))->SetInternVisible(true); + + hasSubMenu = true; + } + } + if( hasSubMenu ) + { + CreateMenuWnd(); + } + else + { + SetChecked(!GetChecked()); + if (CMenuWnd::GetGlobalContextMenuObserver().GetManager() != NULL) + { + CDuiString* strPost = new CDuiString(GetName().GetData()); + if (!PostMessage(CMenuWnd::GetGlobalContextMenuObserver().GetManager()->GetPaintWindow(), WM_MENUCLICK, (WPARAM)(strPost), (LPARAM)(GetChecked() == TRUE))) + delete strPost; + } + ContextMenuParam param; + param.hWnd = m_pManager->GetPaintWindow(); + param.wParam = 1; + CMenuWnd::GetGlobalContextMenuObserver().RBroadcast(param); + } + } + + return; + } + + if ( event.Type == UIEVENT_KEYDOWN && event.chKey == VK_RIGHT ) + { + if( m_pWindow ) return; + bool hasSubMenu = false; + for( int i = 0; i < GetCount(); ++i ) + { + if( GetItemAt(i)->GetInterface(_T("MenuElement")) != NULL ) + { + (static_cast(GetItemAt(i)->GetInterface(_T("MenuElement"))))->SetVisible(true); + (static_cast(GetItemAt(i)->GetInterface(_T("MenuElement"))))->SetInternVisible(true); + + hasSubMenu = true; + } + } + if( hasSubMenu ) + { + m_pOwner->SelectItem(GetIndex(), true); + CreateMenuWnd(); + } + else + { + ContextMenuParam param; + param.hWnd = m_pManager->GetPaintWindow(); + param.wParam = 2; + CMenuWnd::GetGlobalContextMenuObserver().RBroadcast(param); + m_pOwner->SelectItem(GetIndex(), true); + } + + return; + } + + CListContainerElementUI::DoEvent(event); +} + +CMenuWnd* CMenuElementUI::GetMenuWnd() +{ + return m_pWindow; +} + +void CMenuElementUI::CreateMenuWnd() +{ + if( m_pWindow ) return; + + m_pWindow = new CMenuWnd(); + ASSERT(m_pWindow); + + ContextMenuParam param; + param.hWnd = m_pManager->GetPaintWindow(); + param.wParam = 2; + CMenuWnd::GetGlobalContextMenuObserver().RBroadcast(param); + + m_pWindow->Init(static_cast(this), _T(""), CDuiPoint(), NULL); +} + +void CMenuElementUI::SetLineType() +{ + m_bDrawLine = true; + if (GetFixedHeight() == 0 || GetFixedHeight() == ITEM_DEFAULT_HEIGHT ) + SetFixedHeight(DEFAULT_LINE_HEIGHT); + + SetMouseChildEnabled(false); + SetMouseEnabled(false); + SetEnabled(false); +} + +void CMenuElementUI::SetLineColor(DWORD color) +{ + m_dwLineColor = color; +} + +DWORD CMenuElementUI::GetLineColor() const +{ + return m_dwLineColor; +} +void CMenuElementUI::SetLinePadding(RECT rcInset) +{ + m_rcLinePadding = rcInset; +} + +RECT CMenuElementUI::GetLinePadding() const +{ + return m_rcLinePadding; +} + +void CMenuElementUI::SetIcon(LPCTSTR strIcon) +{ + if ( strIcon != _T("") ) + m_strIcon = strIcon; +} + +void CMenuElementUI::SetIconSize(LONG cx, LONG cy) +{ + m_szIconSize.cx = cx; + m_szIconSize.cy = cy; +} + +void CMenuElementUI::SetChecked(bool bCheck/* = true*/) +{ + if (!m_bCheckItem || CMenuWnd::GetGlobalContextMenuObserver().GetMenuCheckInfo() == NULL ) + return; + std::map::iterator it = CMenuWnd::GetGlobalContextMenuObserver().GetMenuCheckInfo()->find(GetName()); + if (it == CMenuWnd::GetGlobalContextMenuObserver().GetMenuCheckInfo()->end()) + CMenuWnd::GetGlobalContextMenuObserver().GetMenuCheckInfo()->insert(std::map::value_type(GetName(),bCheck)); + else + it->second = bCheck; + +} + +bool CMenuElementUI::GetChecked() const +{ + + if (!m_bCheckItem || CMenuWnd::GetGlobalContextMenuObserver().GetMenuCheckInfo() == NULL || CMenuWnd::GetGlobalContextMenuObserver().GetMenuCheckInfo()->size() == 0) + return false; + + std::map::iterator it = CMenuWnd::GetGlobalContextMenuObserver().GetMenuCheckInfo()->find(GetName()); + if (it != CMenuWnd::GetGlobalContextMenuObserver().GetMenuCheckInfo()->end()) + { + return it->second; + } + return false; + +} + +void CMenuElementUI::SetCheckItem(bool bCheckItem/* = false*/) +{ + m_bCheckItem = bCheckItem; +} + +bool CMenuElementUI::GetCheckItem() const +{ + return m_bCheckItem; +} + +void CMenuElementUI::SetShowExplandIcon(bool bShow) +{ + m_bShowExplandIcon = bShow; +} + +void CMenuElementUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) +{ + if( _tcscmp(pstrName, _T("icon")) == 0){ + SetIcon(pstrValue); + } + else if( _tcscmp(pstrName, _T("iconsize")) == 0 ) { + LPTSTR pstr = NULL; + LONG cx = 0, cy = 0; + cx = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + cy = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetIconSize(cx, cy); + } + else if( _tcscmp(pstrName, _T("checkitem")) == 0 ) { + SetCheckItem(_tcscmp(pstrValue, _T("true")) == 0 ? true : false); + } + else if( _tcscmp(pstrName, _T("ischeck")) == 0 ) { + if (CMenuWnd::GetGlobalContextMenuObserver().GetMenuCheckInfo() != NULL && CMenuWnd::GetGlobalContextMenuObserver().GetMenuCheckInfo()->find(GetName()) == CMenuWnd::GetGlobalContextMenuObserver().GetMenuCheckInfo()->end()) + { + SetChecked(_tcscmp(pstrValue, _T("true")) == 0 ? true : false); + } + } + else if( _tcscmp(pstrName, _T("linetype")) == 0){ + if (_tcscmp(pstrValue, _T("true")) == 0) + SetLineType(); + } + else if( _tcscmp(pstrName, _T("expland")) == 0 ) { + SetShowExplandIcon(_tcscmp(pstrValue, _T("true")) == 0 ? true : false); + } + else if( _tcscmp(pstrName, _T("linecolor")) == 0){ + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + SetLineColor(_tcstoul(pstrValue, &pstr, 16)); + } + else if( _tcscmp(pstrName, _T("linepadding")) == 0 ) { + RECT rcInset = { 0 }; + LPTSTR pstr = NULL; + rcInset.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + rcInset.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcInset.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcInset.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetLinePadding(rcInset); + } + else if ( _tcscmp(pstrName, _T("height")) == 0){ + SetFixedHeight(_ttoi(pstrValue)); + } + else + CListContainerElementUI::SetAttribute(pstrName, pstrValue); +} + +} // namespace DuiLib diff --git a/DuiLib/Control/UIMenu.h b/DuiLib/Control/UIMenu.h new file mode 100644 index 00000000..e953e217 --- /dev/null +++ b/DuiLib/Control/UIMenu.h @@ -0,0 +1,371 @@ +#ifndef __UIMENU_H__ +#define __UIMENU_H__ + +#pragma once + +#include "../Utils/observer_impl_base.h" + + +namespace DuiLib { + +struct ContextMenuParam +{ + // 1: remove all + // 2: remove the sub menu + WPARAM wParam; + HWND hWnd; +}; + +enum MenuAlignment +{ + eMenuAlignment_Left = 1 << 1, + eMenuAlignment_Top = 1 << 2, + eMenuAlignment_Right = 1 << 3, + eMenuAlignment_Bottom = 1 << 4, +}; + + +enum MenuItemDefaultInfo +{ + ITEM_DEFAULT_HEIGHT = 30, //ÿһitemĬϸ߶ȣֻ״ʱԶ壩 + ITEM_DEFAULT_WIDTH = 150, //ڵĬϿ + + ITEM_DEFAULT_ICON_WIDTH = 26, //Ĭͼռ + ITEM_DEFAULT_ICON_SIZE = 16, //ĬͼĴС + + ITEM_DEFAULT_EXPLAND_ICON_WIDTH = 20, //Ĭ¼˵չͼռ + ITEM_DEFAULT_EXPLAND_ICON_SIZE = 9, //Ĭ¼˵չͼĴС + + DEFAULT_LINE_LEFT_INSET = ITEM_DEFAULT_ICON_WIDTH + 3, //ĬϷָߵ߾ + DEFAULT_LINE_RIGHT_INSET = 7, //ĬϷָߵұ߾ + DEFAULT_LINE_HEIGHT = 6, //ĬϷָռ߶ + DEFAULT_LINE_COLOR = 0xFFBCBFC4 //ĬϷָɫ + +}; + +#define WM_MENUCLICK WM_USER + 121 //հťϢ + + +/////////////////////////////////////////////// +class MenuMenuReceiverImplBase; +class MenuMenuObserverImplBase +{ +public: + virtual void AddReceiver(MenuMenuReceiverImplBase* receiver) = 0; + virtual void RemoveReceiver(MenuMenuReceiverImplBase* receiver) = 0; + virtual BOOL RBroadcast(ContextMenuParam param) = 0; +}; +///////////////////////////////////////////////// +class MenuMenuReceiverImplBase +{ +public: + virtual void AddObserver(MenuMenuObserverImplBase* observer) = 0; + virtual void RemoveObserver() = 0; + virtual BOOL Receive(ContextMenuParam param) = 0; +}; +///////////////////////////////////////////////// + +class MenuReceiverImpl; +class UILIB_API MenuObserverImpl : public MenuMenuObserverImplBase +{ + friend class Iterator; +public: + MenuObserverImpl(): + m_pMainWndPaintManager(NULL), + m_pMenuCheckInfo(NULL) + { + pReceivers_ = new ReceiversVector; + } + + ~MenuObserverImpl() + { + if (pReceivers_ != NULL) + { + delete pReceivers_; + pReceivers_ = NULL; + } + + } + + virtual void AddReceiver(MenuMenuReceiverImplBase* receiver) + { + if (receiver == NULL) + return; + + pReceivers_->push_back(receiver); + receiver->AddObserver(this); + } + + virtual void RemoveReceiver(MenuMenuReceiverImplBase* receiver) + { + if (receiver == NULL) + return; + + ReceiversVector::iterator it = pReceivers_->begin(); + for (; it != pReceivers_->end(); ++it) + { + if (*it == receiver) + { + pReceivers_->erase(it); + break; + } + } + } + + virtual BOOL RBroadcast(ContextMenuParam param) + { + ReceiversVector::reverse_iterator it = pReceivers_->rbegin(); + for (; it != pReceivers_->rend(); ++it) + { + (*it)->Receive(param); + } + + return BOOL(); + } + + + class Iterator + { + MenuObserverImpl & _tbl; + DWORD index; + MenuMenuReceiverImplBase* ptr; + public: + Iterator( MenuObserverImpl & table ) + : _tbl( table ), index(0), ptr(NULL) + {} + + Iterator( const Iterator & v ) + : _tbl( v._tbl ), index(v.index), ptr(v.ptr) + {} + + MenuMenuReceiverImplBase* next() + { + if ( index >= _tbl.pReceivers_->size() ) + return NULL; + + for ( ; index < _tbl.pReceivers_->size(); ) + { + ptr = (*(_tbl.pReceivers_))[ index++ ]; + if ( ptr ) + return ptr; + } + return NULL; + } + }; + + virtual void SetManger(CPaintManagerUI* pManager) + { + if (pManager != NULL) + m_pMainWndPaintManager = pManager; + } + + virtual CPaintManagerUI* GetManager() const + { + return m_pMainWndPaintManager; + } + + virtual void SetMenuCheckInfo(std::map* pInfo) + { + if (pInfo != NULL) + m_pMenuCheckInfo = pInfo; + else + m_pMenuCheckInfo = NULL; + } + + virtual std::map* GetMenuCheckInfo() const + { + return m_pMenuCheckInfo; + } + +protected: + typedef std::vector ReceiversVector; + ReceiversVector *pReceivers_; + + CPaintManagerUI* m_pMainWndPaintManager; + std::map* m_pMenuCheckInfo; +}; + +//////////////////////////////////////////////////// +class UILIB_API MenuReceiverImpl : public MenuMenuReceiverImplBase +{ +public: + MenuReceiverImpl() + { + pObservers_ = new ObserversVector; + } + + ~MenuReceiverImpl() + { + if (pObservers_ != NULL) + { + delete pObservers_; + pObservers_ = NULL; + } + } + + virtual void AddObserver(MenuMenuObserverImplBase* observer) + { + pObservers_->push_back(observer); + } + + virtual void RemoveObserver() + { + ObserversVector::iterator it = pObservers_->begin(); + for (; it != pObservers_->end(); ++it) + { + (*it)->RemoveReceiver(this); + } + } + + virtual BOOL Receive(ContextMenuParam param) + { + return BOOL(); + } + +protected: + typedef std::vector ObserversVector; + ObserversVector* pObservers_; +}; + +///////////////////////////////////////////////////////////////////////////////////// +// + + +class CListUI; +class UILIB_API CMenuUI : public CListUI +{ +public: + CMenuUI(); + virtual ~CMenuUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + + virtual void DoEvent(TEventUI& event); + + virtual bool Add(CControlUI* pControl); + virtual bool AddAt(CControlUI* pControl, int iIndex); + + virtual int GetItemIndex(CControlUI* pControl) const; + virtual bool SetItemIndex(CControlUI* pControl, int iIndex); + virtual bool Remove(CControlUI* pControl); + + SIZE EstimateSize(SIZE szAvailable) override; + + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) ; +}; + +///////////////////////////////////////////////////////////////////////////////////// +// + +class CMenuElementUI; +class UILIB_API CMenuWnd : public CWindowWnd, public MenuReceiverImpl, public INotifyUI, public IDialogBuilderCallback +{ +public: + + static MenuObserverImpl& GetGlobalContextMenuObserver() + { + static MenuObserverImpl s_context_menu_observer; + return s_context_menu_observer; + } + +public: + CMenuWnd(); + ~CMenuWnd(); + + /* + * @pOwner һ˵ҪָDz˵ڲʹõ + * @xml ˵IJļ + * @point ˵Ͻ + * @pMainPaintManager ˵ĸָ + * @pMenuCheckInfo ˵ĵѡ͸ѡϢṹָ + * @dwAlignment ˵ijλãĬϳ²ࡣ + */ + void Init(CMenuElementUI* pOwner, STRINGorID xml, POINT point, + CPaintManagerUI* pMainPaintManager, std::map* pMenuCheckInfo = NULL, + DWORD dwAlignment = eMenuAlignment_Left | eMenuAlignment_Top); + LPCTSTR GetWindowClassName() const; + void OnFinalMessage(HWND hWnd); + void Notify(TNotifyUI& msg); + CControlUI* CreateControl(LPCTSTR pstrClassName); + + LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + + BOOL Receive(ContextMenuParam param); + + // ȡ˵ؼڶ̬Ӳ˵ + CMenuUI* GetMenuUI(); + + // µ˵ĴС + void ResizeMenu(); + + // µӲ˵ĴС + void ResizeSubMenu(); + +public: + + POINT m_BasedPoint; + STRINGorID m_xml; + CPaintManagerUI m_pm; + CMenuElementUI* m_pOwner; + CMenuUI* m_pLayout; + DWORD m_dwAlignment; //˵뷽ʽ +}; + +class CListContainerElementUI; +class UILIB_API CMenuElementUI : public CListContainerElementUI +{ + friend CMenuWnd; +public: + CMenuElementUI(); + ~CMenuElementUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + void DoPaint(HDC hDC, const RECT& rcPaint); + void DrawItemText(HDC hDC, const RECT& rcItem); + SIZE EstimateSize(SIZE szAvailable); + + void DoEvent(TEventUI& event); + + CMenuWnd* GetMenuWnd(); + void CreateMenuWnd(); + + void SetLineType(); + void SetLineColor(DWORD color); + DWORD GetLineColor() const; + void SetLinePadding(RECT rcInset); + RECT GetLinePadding() const; + void SetIcon(LPCTSTR strIcon); + void SetIconSize(LONG cx, LONG cy); + void DrawItemIcon(HDC hDC, const RECT& rcItem); + void SetChecked(bool bCheck = true); + bool GetChecked() const; + void SetCheckItem(bool bCheckItem = false); + bool GetCheckItem() const; + + void SetShowExplandIcon(bool bShow); + void DrawItemExpland(HDC hDC, const RECT& rcItem); + + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + +protected: + CMenuWnd* m_pWindow; + + bool m_bDrawLine; //ָ + DWORD m_dwLineColor; //ָɫ + RECT m_rcLinePadding; //ָߵұ߾ + + SIZE m_szIconSize; //ͼ + CDuiString m_strIcon; + bool m_bCheckItem; + + bool m_bShowExplandIcon; +}; + +} // namespace DuiLib + +#endif // __UIMENU_H__ diff --git a/DuiLib/Control/UIOption.cpp b/DuiLib/Control/UIOption.cpp new file mode 100644 index 00000000..cfd6b637 --- /dev/null +++ b/DuiLib/Control/UIOption.cpp @@ -0,0 +1,286 @@ +#include "stdafx.h" +#include "UIOption.h" + +namespace DuiLib +{ + COptionUI::COptionUI() : m_bSelected(false), m_dwSelectedTextColor(0) + { + } + + COptionUI::~COptionUI() + { + if( !m_sGroupName.IsEmpty() && m_pManager ) m_pManager->RemoveOptionGroup(m_sGroupName, this); + } + + LPCTSTR COptionUI::GetClass() const + { + return _T("OptionUI"); + } + + LPVOID COptionUI::GetInterface(LPCTSTR pstrName) + { + if( _tcscmp(pstrName, DUI_CTR_OPTION) == 0 ) return static_cast(this); + return CButtonUI::GetInterface(pstrName); + } + + void COptionUI::SetManager(CPaintManagerUI* pManager, CControlUI* pParent, bool bInit) + { + CControlUI::SetManager(pManager, pParent, bInit); + if( bInit && !m_sGroupName.IsEmpty() ) { + if (m_pManager) m_pManager->AddOptionGroup(m_sGroupName, this); + } + } + + LPCTSTR COptionUI::GetGroup() const + { + return m_sGroupName; + } + + void COptionUI::SetGroup(LPCTSTR pStrGroupName) + { + if( pStrGroupName == NULL ) { + if( m_sGroupName.IsEmpty() ) return; + m_sGroupName.Empty(); + } + else { + if( m_sGroupName == pStrGroupName ) return; + if (!m_sGroupName.IsEmpty() && m_pManager) m_pManager->RemoveOptionGroup(m_sGroupName, this); + m_sGroupName = pStrGroupName; + } + + if( !m_sGroupName.IsEmpty() ) { + if (m_pManager) m_pManager->AddOptionGroup(m_sGroupName, this); + } + else { + if (m_pManager) m_pManager->RemoveOptionGroup(m_sGroupName, this); + } + + Selected(m_bSelected); + } + + bool COptionUI::IsSelected() const + { + return m_bSelected; + } + + void COptionUI::Selected(bool bSelected) + { +// if( m_bSelected == bSelected ) return; + m_bSelected = bSelected; + if( m_bSelected ) m_uButtonState |= UISTATE_SELECTED; + else m_uButtonState &= ~UISTATE_SELECTED; + + if( m_pManager != NULL ) { + if( !m_sGroupName.IsEmpty() ) { + if( m_bSelected ) { + CStdPtrArray* aOptionGroup = m_pManager->GetOptionGroup(m_sGroupName); + for( int i = 0; i < aOptionGroup->GetSize(); i++ ) { + COptionUI* pControl = static_cast(aOptionGroup->GetAt(i)); + if( pControl != this ) { + pControl->Selected(false); + } + } + m_pManager->SendNotify(this, DUI_MSGTYPE_SELECTCHANGED); + } + } + else { + m_pManager->SendNotify(this, DUI_MSGTYPE_SELECTCHANGED); + } + } + + Invalidate(); + } + + bool COptionUI::Activate() + { + if( !CButtonUI::Activate() ) return false; + if( !m_sGroupName.IsEmpty() ) Selected(true); + else Selected(!m_bSelected); + + return true; + } + + void COptionUI::SetEnabled(bool bEnable) + { + CControlUI::SetEnabled(bEnable); + if( !IsEnabled() ) { + if( m_bSelected ) m_uButtonState = UISTATE_SELECTED; + else m_uButtonState = 0; + } + } + + LPCTSTR COptionUI::GetSelectedImage() + { + return m_sSelectedImage; + } + + void COptionUI::SetSelectedImage(LPCTSTR pStrImage) + { + m_sSelectedImage = pStrImage; + Invalidate(); + } + + LPCTSTR COptionUI::GetSelectedHotImage() + { + return m_sSelectedHotImage; + } + + void COptionUI::SetSelectedHotImage( LPCTSTR pStrImage ) + { + m_sSelectedHotImage = pStrImage; + Invalidate(); + } + + LPCTSTR COptionUI::GetSelectedPushedImage() + { + return m_sSelectedPushedImage; + } + + void COptionUI::SetSelectedPushedImage(LPCTSTR pStrImage) + { + m_sSelectedPushedImage = pStrImage; + Invalidate(); + } + + void COptionUI::SetSelectedTextColor(DWORD dwTextColor) + { + m_dwSelectedTextColor = dwTextColor; + } + + DWORD COptionUI::GetSelectedTextColor() + { + if (m_dwSelectedTextColor == 0) m_dwSelectedTextColor = m_pManager->GetDefaultFontColor(); + return m_dwSelectedTextColor; + } + + void COptionUI::SetSelectedBkColor( DWORD dwBkColor ) + { + m_dwSelectedBkColor = dwBkColor; + } + + DWORD COptionUI::GetSelectBkColor() + { + return m_dwSelectedBkColor; + } + + LPCTSTR COptionUI::GetForeImage() + { + return m_sForeImage; + } + + void COptionUI::SetForeImage(LPCTSTR pStrImage) + { + m_sForeImage = pStrImage; + Invalidate(); + } + + LPCTSTR COptionUI::GetSelectedForedImage() + { + return m_sSelectedForeImage; + } + + void COptionUI::SetSelectedForedImage(LPCTSTR pStrImage) + { + m_sSelectedForeImage = pStrImage; + Invalidate(); + } + + SIZE COptionUI::EstimateSize(SIZE szAvailable) + { + if( m_cxyFixed.cy == 0 ) return CDuiSize(m_cxyFixed.cx, m_pManager->GetFontInfo(GetFont())->tm.tmHeight + 8); + return CControlUI::EstimateSize(szAvailable); + } + + void COptionUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if( _tcscmp(pstrName, _T("group")) == 0 ) SetGroup(pstrValue); + else if( _tcscmp(pstrName, _T("selected")) == 0 ) Selected(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("selectedimage")) == 0 ) SetSelectedImage(pstrValue); + else if( _tcscmp(pstrName, _T("selectedhotimage")) == 0 ) SetSelectedHotImage(pstrValue); + else if( _tcscmp(pstrName, _T("selectedpushedimage")) == 0 ) SetSelectedPushedImage(pstrValue); + else if( _tcscmp(pstrName, _T("foreimage")) == 0 ) SetForeImage(pstrValue); + else if( _tcscmp(pstrName, _T("selectedforeimage")) == 0 ) SetSelectedForedImage(pstrValue); + else if( _tcscmp(pstrName, _T("selectedbkcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetSelectedBkColor(clrColor); + } + else if( _tcscmp(pstrName, _T("selectedtextcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetSelectedTextColor(clrColor); + } + else CButtonUI::SetAttribute(pstrName, pstrValue); + } + + void COptionUI::PaintStatusImage(HDC hDC) + { + + if( (m_uButtonState & UISTATE_PUSHED) != 0 && IsSelected() && !m_sSelectedPushedImage.IsEmpty()) { + if( !DrawImage(hDC, (LPCTSTR)m_sSelectedPushedImage) ) + m_sSelectedPushedImage.Empty(); + else goto Label_ForeImage; + } + else if( (m_uButtonState & UISTATE_HOT) != 0 && IsSelected() && !m_sSelectedHotImage.IsEmpty()) { + if( !DrawImage(hDC, (LPCTSTR)m_sSelectedHotImage) ) + m_sSelectedHotImage.Empty(); + else goto Label_ForeImage; + } + else if( (m_uButtonState & UISTATE_SELECTED) != 0 ) { + if( !m_sSelectedImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sSelectedImage) ) m_sSelectedImage.Empty(); + else goto Label_ForeImage; + } + else if(m_dwSelectedBkColor != 0) { + CRenderEngine::DrawColor(hDC, m_rcPaint, GetAdjustColor(m_dwSelectedBkColor)); + return; + } + } + + CButtonUI::PaintStatusImage(hDC); + +Label_ForeImage: + if ( IsSelected() && !m_sSelectedForeImage.IsEmpty()) + { + if( !DrawImage(hDC, (LPCTSTR)m_sSelectedForeImage) ) m_sSelectedForeImage.Empty(); + } + else if( !m_sForeImage.IsEmpty() ) + { + if( !DrawImage(hDC, (LPCTSTR)m_sForeImage) ) m_sForeImage.Empty(); + } + + } + + void COptionUI::PaintText(HDC hDC) + { + if( (m_uButtonState & UISTATE_SELECTED) != 0 ) + { + DWORD oldTextColor = m_dwTextColor; + if( m_dwSelectedTextColor != 0 ) m_dwTextColor = m_dwSelectedTextColor; + + if( m_dwTextColor == 0 ) m_dwTextColor = m_pManager->GetDefaultFontColor(); + if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor(); + + if( m_sText.IsEmpty() ) return; + int nLinks = 0; + RECT rc = m_rcItem; + rc.left += m_rcTextPadding.left; + rc.right -= m_rcTextPadding.right; + rc.top += m_rcTextPadding.top; + rc.bottom -= m_rcTextPadding.bottom; + + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, IsEnabled()?m_dwTextColor:m_dwDisabledTextColor, \ + NULL, NULL, nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, IsEnabled()?m_dwTextColor:m_dwDisabledTextColor, \ + m_iFont, m_uTextStyle); + + m_dwTextColor = oldTextColor; + } + else + CButtonUI::PaintText(hDC); + } +} \ No newline at end of file diff --git a/DuiLib/Control/UIOption.h b/DuiLib/Control/UIOption.h new file mode 100644 index 00000000..ce6a2221 --- /dev/null +++ b/DuiLib/Control/UIOption.h @@ -0,0 +1,70 @@ +#ifndef __UIOPTION_H__ +#define __UIOPTION_H__ + +#pragma once + +namespace DuiLib +{ + class UILIB_API COptionUI : public CButtonUI + { + public: + COptionUI(); + ~COptionUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + + void SetManager(CPaintManagerUI* pManager, CControlUI* pParent, bool bInit = true); + + bool Activate(); + void SetEnabled(bool bEnable = true); + + LPCTSTR GetSelectedImage(); + void SetSelectedImage(LPCTSTR pStrImage); + + LPCTSTR GetSelectedHotImage(); + void SetSelectedHotImage(LPCTSTR pStrImage); + + LPCTSTR GetSelectedPushedImage(); + void SetSelectedPushedImage(LPCTSTR pStrImage); + + void SetSelectedTextColor(DWORD dwTextColor); + DWORD GetSelectedTextColor(); + + void SetSelectedBkColor(DWORD dwBkColor); + DWORD GetSelectBkColor(); + + LPCTSTR GetForeImage(); + void SetForeImage(LPCTSTR pStrImage); + + LPCTSTR GetSelectedForedImage(); + void SetSelectedForedImage(LPCTSTR pStrImage); + + LPCTSTR GetGroup() const; + void SetGroup(LPCTSTR pStrGroupName = NULL); + bool IsSelected() const; + virtual void Selected(bool bSelected); + + SIZE EstimateSize(SIZE szAvailable); + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + void PaintStatusImage(HDC hDC); + void PaintText(HDC hDC); + + protected: + bool m_bSelected; + CDuiString m_sGroupName; + + DWORD m_dwSelectedBkColor; + DWORD m_dwSelectedTextColor; + + CDuiString m_sSelectedImage; + CDuiString m_sSelectedHotImage; + CDuiString m_sSelectedPushedImage; + CDuiString m_sForeImage; + CDuiString m_sSelectedForeImage; + }; + +} // namespace DuiLib + +#endif // __UIOPTION_H__ \ No newline at end of file diff --git a/DuiLib/Control/UIProgress.cpp b/DuiLib/Control/UIProgress.cpp new file mode 100644 index 00000000..5fd55508 --- /dev/null +++ b/DuiLib/Control/UIProgress.cpp @@ -0,0 +1,139 @@ +#include "stdafx.h" +#include "UIProgress.h" + +namespace DuiLib +{ + CProgressUI::CProgressUI() : m_bHorizontal(true), m_nMin(0), m_nMax(100), m_nValue(0), m_bStretchForeImage(true) + { + m_uTextStyle = DT_SINGLELINE | DT_CENTER; + SetFixedHeight(12); + } + + LPCTSTR CProgressUI::GetClass() const + { + return _T("ProgressUI"); + } + + LPVOID CProgressUI::GetInterface(LPCTSTR pstrName) + { + if( _tcscmp(pstrName, DUI_CTR_PROGRESS) == 0 ) return static_cast(this); + return CLabelUI::GetInterface(pstrName); + } + + bool CProgressUI::IsHorizontal() + { + return m_bHorizontal; + } + + void CProgressUI::SetHorizontal(bool bHorizontal) + { + if( m_bHorizontal == bHorizontal ) return; + + m_bHorizontal = bHorizontal; + Invalidate(); + } + + int CProgressUI::GetMinValue() const + { + return m_nMin; + } + + void CProgressUI::SetMinValue(int nMin) + { + m_nMin = nMin; + Invalidate(); + } + + int CProgressUI::GetMaxValue() const + { + return m_nMax; + } + + void CProgressUI::SetMaxValue(int nMax) + { + m_nMax = nMax; + Invalidate(); + } + + int CProgressUI::GetValue() const + { + return m_nValue; + } + + void CProgressUI::SetValue(int nValue) + { + if(nValue == m_nValue || nValue m_nMax) + { + return; + } + m_nValue = nValue; + Invalidate(); + } + + LPCTSTR CProgressUI::GetForeImage() const + { + return m_sForeImage; + } + + void CProgressUI::SetForeImage(LPCTSTR pStrImage) + { + if( m_sForeImage == pStrImage ) return; + + m_sForeImage = pStrImage; + Invalidate(); + } + + void CProgressUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if( _tcscmp(pstrName, _T("foreimage")) == 0 ) SetForeImage(pstrValue); + else if( _tcscmp(pstrName, _T("hor")) == 0 ) SetHorizontal(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("min")) == 0 ) SetMinValue(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("max")) == 0 ) SetMaxValue(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("value")) == 0 ) SetValue(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("isstretchfore"))==0) SetStretchForeImage(_tcscmp(pstrValue, _T("true")) == 0? true : false); + else CLabelUI::SetAttribute(pstrName, pstrValue); + } + + void CProgressUI::PaintStatusImage(HDC hDC) + { + if( m_nMax <= m_nMin ) m_nMax = m_nMin + 1; + if( m_nValue > m_nMax ) m_nValue = m_nMax; + if( m_nValue < m_nMin ) m_nValue = m_nMin; + + RECT rc = {0}; + if( m_bHorizontal ) { + rc.right = (m_nValue - m_nMin) * (m_rcItem.right - m_rcItem.left) / (m_nMax - m_nMin); + rc.bottom = m_rcItem.bottom - m_rcItem.top; + } + else { + rc.top = (m_rcItem.bottom - m_rcItem.top) * (m_nMax - m_nValue) / (m_nMax - m_nMin); + rc.right = m_rcItem.right - m_rcItem.left; + rc.bottom = m_rcItem.bottom - m_rcItem.top; + } + + if( !m_sForeImage.IsEmpty() ) { + m_sForeImageModify.Empty(); + if (m_bStretchForeImage) + m_sForeImageModify.SmallFormat(_T("dest='%d,%d,%d,%d'"), rc.left, rc.top, rc.right, rc.bottom); + else + m_sForeImageModify.SmallFormat(_T("dest='%d,%d,%d,%d' source='%d,%d,%d,%d'") + , rc.left, rc.top, rc.right, rc.bottom + , rc.left, rc.top, rc.right, rc.bottom); + + if( !DrawImage(hDC, (LPCTSTR)m_sForeImage, (LPCTSTR)m_sForeImageModify) ) m_sForeImage.Empty(); + else return; + } + } + + bool CProgressUI::IsStretchForeImage() + { + return m_bStretchForeImage; + } + + void CProgressUI::SetStretchForeImage( bool bStretchForeImage /*= true*/ ) + { + if (m_bStretchForeImage==bStretchForeImage) return; + m_bStretchForeImage=bStretchForeImage; + Invalidate(); + } +} diff --git a/DuiLib/Control/UIProgress.h b/DuiLib/Control/UIProgress.h new file mode 100644 index 00000000..293c98f6 --- /dev/null +++ b/DuiLib/Control/UIProgress.h @@ -0,0 +1,45 @@ +#ifndef __UIPROGRESS_H__ +#define __UIPROGRESS_H__ + +#pragma once + +namespace DuiLib +{ + class UILIB_API CProgressUI : public CLabelUI + { + public: + CProgressUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + + bool IsHorizontal(); + void SetHorizontal(bool bHorizontal = true); + bool IsStretchForeImage(); + void SetStretchForeImage(bool bStretchForeImage = true); + int GetMinValue() const; + void SetMinValue(int nMin); + int GetMaxValue() const; + void SetMaxValue(int nMax); + int GetValue() const; + void SetValue(int nValue); + LPCTSTR GetForeImage() const; + void SetForeImage(LPCTSTR pStrImage); + + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + void PaintStatusImage(HDC hDC); + + protected: + bool m_bHorizontal; + bool m_bStretchForeImage; + int m_nMax; + int m_nMin; + int m_nValue; + + CDuiString m_sForeImage; + CDuiString m_sForeImageModify; + }; + +} // namespace DuiLib + +#endif // __UIPROGRESS_H__ diff --git a/DuiLib/Control/UIRichEdit.cpp b/DuiLib/Control/UIRichEdit.cpp new file mode 100644 index 00000000..d1bd7865 --- /dev/null +++ b/DuiLib/Control/UIRichEdit.cpp @@ -0,0 +1,2589 @@ +#include "stdafx.h" + +// These constants are for backward compatibility. They are the +// sizes used for initialization and reset in RichEdit 1.0 + +namespace DuiLib { + +const LONG cInitTextMax = (32 * 1024) - 1; + +EXTERN_C const IID IID_ITextServices = { // 8d33f740-cf58-11ce-a89d-00aa006cadc5 + 0x8d33f740, + 0xcf58, + 0x11ce, + {0xa8, 0x9d, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5} +}; + +EXTERN_C const IID IID_ITextHost = { /* c5bdd8d0-d26e-11ce-a89e-00aa006cadc5 */ + 0xc5bdd8d0, + 0xd26e, + 0x11ce, + {0xa8, 0x9e, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5} +}; + +#ifndef LY_PER_INCH +#define LY_PER_INCH 1440 +#endif + +#ifndef HIMETRIC_PER_INCH +#define HIMETRIC_PER_INCH 2540 +#endif + +#include + +class CTxtWinHost : public ITextHost +{ +public: + CTxtWinHost(); + BOOL Init(CRichEditUI *re , const CREATESTRUCT *pcs); + virtual ~CTxtWinHost(); + + ITextServices* GetTextServices(void) { return pserv; } + void SetClientRect(RECT *prc); + RECT* GetClientRect() { return &rcClient; } + BOOL GetWordWrap(void) { return fWordWrap; } + void SetWordWrap(BOOL fWordWrap); + BOOL GetReadOnly(); + void SetReadOnly(BOOL fReadOnly); + void SetFont(HFONT hFont); + void SetColor(DWORD dwColor); + SIZEL* GetExtent(); + void SetExtent(SIZEL *psizelExtent); + void LimitText(LONG nChars); + BOOL IsCaptured(); + + BOOL GetAllowBeep(); + void SetAllowBeep(BOOL fAllowBeep); + WORD GetDefaultAlign(); + void SetDefaultAlign(WORD wNewAlign); + BOOL GetRichTextFlag(); + void SetRichTextFlag(BOOL fNew); + LONG GetDefaultLeftIndent(); + void SetDefaultLeftIndent(LONG lNewIndent); + BOOL SetSaveSelection(BOOL fSaveSelection); + HRESULT OnTxInPlaceDeactivate(); + HRESULT OnTxInPlaceActivate(LPCRECT prcClient); + BOOL GetActiveState(void) { return fInplaceActive; } + BOOL DoSetCursor(RECT *prc, POINT *pt); + void SetTransparent(BOOL fTransparent); + void GetControlRect(LPRECT prc); + LONG SetAccelPos(LONG laccelpos); + WCHAR SetPasswordChar(WCHAR chPasswordChar); + void SetDisabled(BOOL fOn); + LONG SetSelBarWidth(LONG lSelBarWidth); + BOOL GetTimerState(); + + void SetCharFormat(CHARFORMAT2W &c); + void SetParaFormat(PARAFORMAT2 &p); + + // ----------------------------- + // IUnknown interface + // ----------------------------- + virtual HRESULT _stdcall QueryInterface(REFIID riid, void **ppvObject); + virtual ULONG _stdcall AddRef(void); + virtual ULONG _stdcall Release(void); + + // ----------------------------- + // ITextHost interface + // ----------------------------- + virtual HDC TxGetDC(); + virtual INT TxReleaseDC(HDC hdc); + virtual BOOL TxShowScrollBar(INT fnBar, BOOL fShow); + virtual BOOL TxEnableScrollBar (INT fuSBFlags, INT fuArrowflags); + virtual BOOL TxSetScrollRange(INT fnBar, LONG nMinPos, INT nMaxPos, BOOL fRedraw); + virtual BOOL TxSetScrollPos (INT fnBar, INT nPos, BOOL fRedraw); + virtual void TxInvalidateRect(LPCRECT prc, BOOL fMode); + virtual void TxViewChange(BOOL fUpdate); + virtual BOOL TxCreateCaret(HBITMAP hbmp, INT xWidth, INT yHeight); + virtual BOOL TxShowCaret(BOOL fShow); + virtual BOOL TxSetCaretPos(INT x, INT y); + virtual BOOL TxSetTimer(UINT idTimer, UINT uTimeout); + virtual void TxKillTimer(UINT idTimer); + virtual void TxScrollWindowEx (INT dx, INT dy, LPCRECT lprcScroll, LPCRECT lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate, UINT fuScroll); + virtual void TxSetCapture(BOOL fCapture); + virtual void TxSetFocus(); + virtual void TxSetCursor(HCURSOR hcur, BOOL fText); + virtual BOOL TxScreenToClient (LPPOINT lppt); + virtual BOOL TxClientToScreen (LPPOINT lppt); + virtual HRESULT TxActivate( LONG * plOldState ); + virtual HRESULT TxDeactivate( LONG lNewState ); + virtual HRESULT TxGetClientRect(LPRECT prc); + virtual HRESULT TxGetViewInset(LPRECT prc); + virtual HRESULT TxGetCharFormat(const CHARFORMATW **ppCF ); + virtual HRESULT TxGetParaFormat(const PARAFORMAT **ppPF); + virtual COLORREF TxGetSysColor(int nIndex); + virtual HRESULT TxGetBackStyle(TXTBACKSTYLE *pstyle); + virtual HRESULT TxGetMaxLength(DWORD *plength); + virtual HRESULT TxGetScrollBars(DWORD *pdwScrollBar); + virtual HRESULT TxGetPasswordChar(TCHAR *pch); + virtual HRESULT TxGetAcceleratorPos(LONG *pcp); + virtual HRESULT TxGetExtent(LPSIZEL lpExtent); + virtual HRESULT OnTxCharFormatChange (const CHARFORMATW * pcf); + virtual HRESULT OnTxParaFormatChange (const PARAFORMAT * ppf); + virtual HRESULT TxGetPropertyBits(DWORD dwMask, DWORD *pdwBits); + virtual HRESULT TxNotify(DWORD iNotify, void *pv); + virtual HIMC TxImmGetContext(void); + virtual void TxImmReleaseContext(HIMC himc); + virtual HRESULT TxGetSelectionBarWidth (LONG *lSelBarWidth); + +private: + CRichEditUI *m_re; + ULONG cRefs; // Reference Count + ITextServices *pserv; // pointer to Text Services object + // Properties + + DWORD dwStyle; // style bits + + unsigned fEnableAutoWordSel :1; // enable Word style auto word selection? + unsigned fWordWrap :1; // Whether control should word wrap + unsigned fAllowBeep :1; // Whether beep is allowed + unsigned fRich :1; // Whether control is rich text + unsigned fSaveSelection :1; // Whether to save the selection when inactive + unsigned fInplaceActive :1; // Whether control is inplace active + unsigned fTransparent :1; // Whether control is transparent + unsigned fTimer :1; // A timer is set + unsigned fCaptured :1; + + LONG lSelBarWidth; // Width of the selection bar + LONG cchTextMost; // maximum text size + DWORD dwEventMask; // DoEvent mask to pass on to parent window + LONG icf; + LONG ipf; + RECT rcClient; // Client Rect for this control + SIZEL sizelExtent; // Extent array + CHARFORMAT2W cf; // Default character format + PARAFORMAT2 pf; // Default paragraph format + LONG laccelpos; // Accelerator position + WCHAR chPasswordChar; // Password character +}; + +// Convert Pixels on the X axis to Himetric +LONG DXtoHimetricX(LONG dx, LONG xPerInch) +{ + return (LONG) MulDiv(dx, HIMETRIC_PER_INCH, xPerInch); +} + +// Convert Pixels on the Y axis to Himetric +LONG DYtoHimetricY(LONG dy, LONG yPerInch) +{ + return (LONG) MulDiv(dy, HIMETRIC_PER_INCH, yPerInch); +} + +HRESULT InitDefaultCharFormat(CRichEditUI* re, CHARFORMAT2W* pcf, HFONT hfont) +{ + memset(pcf, 0, sizeof(CHARFORMAT2W)); + LOGFONT lf; + if( !hfont ) + hfont = re->GetManager()->GetFont(re->GetFont()); + ::GetObject(hfont, sizeof(LOGFONT), &lf); + + DWORD dwColor = re->GetTextColor(); + if(re->GetManager()->IsBackgroundTransparent()) + { + CRenderEngine::CheckAlphaColor(dwColor); + } + pcf->cbSize = sizeof(CHARFORMAT2W); + pcf->crTextColor = RGB(GetBValue(dwColor), GetGValue(dwColor), GetRValue(dwColor)); + LONG yPixPerInch = GetDeviceCaps(re->GetManager()->GetPaintDC(), LOGPIXELSY); + pcf->yHeight = -lf.lfHeight * LY_PER_INCH / yPixPerInch; + pcf->yOffset = 0; + pcf->dwEffects = 0; + pcf->dwMask = CFM_SIZE | CFM_OFFSET | CFM_FACE | CFM_CHARSET | CFM_COLOR | CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE; + if(lf.lfWeight >= FW_BOLD) + pcf->dwEffects |= CFE_BOLD; + if(lf.lfItalic) + pcf->dwEffects |= CFE_ITALIC; + if(lf.lfUnderline) + pcf->dwEffects |= CFE_UNDERLINE; + pcf->bCharSet = lf.lfCharSet; + pcf->bPitchAndFamily = lf.lfPitchAndFamily; +#ifdef _UNICODE + _tcscpy(pcf->szFaceName, lf.lfFaceName); +#else + //need to thunk pcf->szFaceName to a standard char string.in this case it's easy because our thunk is also our copy + MultiByteToWideChar(CP_ACP, 0, lf.lfFaceName, LF_FACESIZE, pcf->szFaceName, LF_FACESIZE) ; +#endif + + return S_OK; +} + +HRESULT InitDefaultParaFormat(CRichEditUI* re, PARAFORMAT2* ppf) +{ + memset(ppf, 0, sizeof(PARAFORMAT2)); + ppf->cbSize = sizeof(PARAFORMAT2); + ppf->dwMask = PFM_ALL; + ppf->wAlignment = PFA_LEFT; + ppf->cTabCount = 1; + ppf->rgxTabs[0] = lDefaultTab; + + return S_OK; +} + +HRESULT CreateHost(CRichEditUI *re, const CREATESTRUCT *pcs, CTxtWinHost **pptec) +{ + HRESULT hr = E_FAIL; + //GdiSetBatchLimit(1); + + CTxtWinHost *phost = new CTxtWinHost(); + if(phost) + { + if (phost->Init(re, pcs)) + { + *pptec = phost; + hr = S_OK; + } + } + + if (FAILED(hr)) + { + delete phost; + } + + return TRUE; +} + +CTxtWinHost::CTxtWinHost() : m_re(NULL) +{ + ::ZeroMemory(&cRefs, sizeof(CTxtWinHost) - offsetof(CTxtWinHost, cRefs)); + cchTextMost = cInitTextMax; + laccelpos = -1; +} + +CTxtWinHost::~CTxtWinHost() +{ + pserv->OnTxInPlaceDeactivate(); + pserv->Release(); +} + +////////////////////// Create/Init/Destruct Commands /////////////////////// + +BOOL CTxtWinHost::Init(CRichEditUI *re, const CREATESTRUCT *pcs) +{ + IUnknown *pUnk; + HRESULT hr; + + m_re = re; + // Initialize Reference count + cRefs = 1; + + // Create and cache CHARFORMAT for this control + if(FAILED(InitDefaultCharFormat(re, &cf, NULL))) + goto err; + + // Create and cache PARAFORMAT for this control + if(FAILED(InitDefaultParaFormat(re, &pf))) + goto err; + + // edit controls created without a window are multiline by default + // so that paragraph formats can be + dwStyle = ES_MULTILINE; + + // edit controls are rich by default + fRich = re->IsRich(); + + cchTextMost = re->GetLimitText(); + + if (pcs ) + { + dwStyle = pcs->style; + + if ( !(dwStyle & (ES_AUTOHSCROLL | WS_HSCROLL)) ) + { + fWordWrap = TRUE; + } + } + + if( !(dwStyle & ES_LEFT) ) + { + if(dwStyle & ES_CENTER) + pf.wAlignment = PFA_CENTER; + else if(dwStyle & ES_RIGHT) + pf.wAlignment = PFA_RIGHT; + } + + fInplaceActive = TRUE; + + // Create Text Services component + //if(FAILED(CreateTextServices(NULL, this, &pUnk))) + // goto err; + + PCreateTextServices TextServicesProc; + HMODULE hmod = LoadLibrary(_T("msftedit.dll")); + if (hmod) + { + TextServicesProc = (PCreateTextServices)GetProcAddress(hmod,"CreateTextServices"); + } + + if (TextServicesProc) + { + HRESULT hr = TextServicesProc(NULL, this, &pUnk); + } + + hr = pUnk->QueryInterface(IID_ITextServices,(void **)&pserv); + + // Whether the previous call succeeded or failed we are done + // with the private interface. + pUnk->Release(); + + if(FAILED(hr)) + { + goto err; + } + + // Set window text + if(pcs && pcs->lpszName) + { +#ifdef _UNICODE + if(FAILED(pserv->TxSetText((TCHAR *)pcs->lpszName))) + goto err; +#else + size_t iLen = _tcslen(pcs->lpszName); + LPWSTR lpText = new WCHAR[iLen + 1]; + ::ZeroMemory(lpText, (iLen + 1) * sizeof(WCHAR)); + ::MultiByteToWideChar(CP_ACP, 0, pcs->lpszName, -1, (LPWSTR)lpText, iLen) ; + if(FAILED(pserv->TxSetText((LPWSTR)lpText))) { + delete[] lpText; + goto err; + } + delete[] lpText; +#endif + } + + return TRUE; + +err: + return FALSE; +} + +///////////////////////////////// IUnknown //////////////////////////////// + + +HRESULT CTxtWinHost::QueryInterface(REFIID riid, void **ppvObject) +{ + HRESULT hr = E_NOINTERFACE; + *ppvObject = NULL; + + if (IsEqualIID(riid, IID_IUnknown) + || IsEqualIID(riid, IID_ITextHost)) + { + AddRef(); + *ppvObject = (ITextHost *) this; + hr = S_OK; + } + + return hr; +} + +ULONG CTxtWinHost::AddRef(void) +{ + return ++cRefs; +} + +ULONG CTxtWinHost::Release(void) +{ + ULONG c_Refs = --cRefs; + + if (c_Refs == 0) + { + delete this; + } + + return c_Refs; +} + +///////////////////////////////// Far East Support ////////////////////////////////////// + +HIMC CTxtWinHost::TxImmGetContext(void) +{ + return NULL; +} + +void CTxtWinHost::TxImmReleaseContext(HIMC himc) +{ + //::ImmReleaseContext( hwnd, himc ); +} + +//////////////////////////// ITextHost Interface //////////////////////////// + +HDC CTxtWinHost::TxGetDC() +{ + return m_re->GetManager()->GetPaintDC(); +} + +int CTxtWinHost::TxReleaseDC(HDC hdc) +{ + return 1; +} + +BOOL CTxtWinHost::TxShowScrollBar(INT fnBar, BOOL fShow) +{ + CScrollBarUI* pVerticalScrollBar = m_re->GetVerticalScrollBar(); + CScrollBarUI* pHorizontalScrollBar = m_re->GetHorizontalScrollBar(); + if( fnBar == SB_VERT && pVerticalScrollBar ) { + pVerticalScrollBar->SetVisible(fShow == TRUE); + } + else if( fnBar == SB_HORZ && pHorizontalScrollBar ) { + pHorizontalScrollBar->SetVisible(fShow == TRUE); + } + else if( fnBar == SB_BOTH ) { + if( pVerticalScrollBar ) pVerticalScrollBar->SetVisible(fShow == TRUE); + if( pHorizontalScrollBar ) pHorizontalScrollBar->SetVisible(fShow == TRUE); + } + return TRUE; +} + +BOOL CTxtWinHost::TxEnableScrollBar (INT fuSBFlags, INT fuArrowflags) +{ + if( fuSBFlags == SB_VERT ) { + m_re->EnableScrollBar(true, m_re->GetHorizontalScrollBar() != NULL); + m_re->GetVerticalScrollBar()->SetVisible(fuArrowflags != ESB_DISABLE_BOTH); + } + else if( fuSBFlags == SB_HORZ ) { + m_re->EnableScrollBar(m_re->GetVerticalScrollBar() != NULL, true); + m_re->GetHorizontalScrollBar()->SetVisible(fuArrowflags != ESB_DISABLE_BOTH); + } + else if( fuSBFlags == SB_BOTH ) { + m_re->EnableScrollBar(true, true); + m_re->GetVerticalScrollBar()->SetVisible(fuArrowflags != ESB_DISABLE_BOTH); + m_re->GetHorizontalScrollBar()->SetVisible(fuArrowflags != ESB_DISABLE_BOTH); + } + return TRUE; +} + +BOOL CTxtWinHost::TxSetScrollRange(INT fnBar, LONG nMinPos, INT nMaxPos, BOOL fRedraw) +{ + CScrollBarUI* pVerticalScrollBar = m_re->GetVerticalScrollBar(); + CScrollBarUI* pHorizontalScrollBar = m_re->GetHorizontalScrollBar(); + if( fnBar == SB_VERT && pVerticalScrollBar ) { + if( nMaxPos - nMinPos - rcClient.bottom + rcClient.top <= 0 ) { + pVerticalScrollBar->SetVisible(false); + } + else { + pVerticalScrollBar->SetVisible(true); + pVerticalScrollBar->SetScrollRange(nMaxPos - nMinPos - rcClient.bottom + rcClient.top); + } + } + else if( fnBar == SB_HORZ && pHorizontalScrollBar ) { + if( nMaxPos - nMinPos - rcClient.right + rcClient.left <= 0 ) { + pHorizontalScrollBar->SetVisible(false); + } + else { + pHorizontalScrollBar->SetVisible(true); + pHorizontalScrollBar->SetScrollRange(nMaxPos - nMinPos - rcClient.right + rcClient.left); + } + } + return TRUE; +} + +BOOL CTxtWinHost::TxSetScrollPos (INT fnBar, INT nPos, BOOL fRedraw) +{ + CScrollBarUI* pVerticalScrollBar = m_re->GetVerticalScrollBar(); + CScrollBarUI* pHorizontalScrollBar = m_re->GetHorizontalScrollBar(); + if( fnBar == SB_VERT && pVerticalScrollBar ) { + pVerticalScrollBar->SetScrollPos(nPos); + } + else if( fnBar == SB_HORZ && pHorizontalScrollBar ) { + pHorizontalScrollBar->SetScrollPos(nPos); + } + return TRUE; +} + +void CTxtWinHost::TxInvalidateRect(LPCRECT prc, BOOL fMode) +{ + if( prc == NULL ) { + m_re->GetManager()->Invalidate(rcClient); + return; + } + RECT rc = *prc; + m_re->GetManager()->Invalidate(rc); +} + +void CTxtWinHost::TxViewChange(BOOL fUpdate) +{ + if( m_re->OnTxViewChanged() ) m_re->Invalidate(); +} + +BOOL CTxtWinHost::TxCreateCaret(HBITMAP hbmp, INT xWidth, INT yHeight) +{ + if (m_re->GetManager()->IsBackgroundTransparent()) + { + return m_re->GetManager()->CreateCaret(hbmp, xWidth, yHeight); + } + else + { + return ::CreateCaret(m_re->GetManager()->GetPaintWindow(), hbmp, xWidth, yHeight); + } +} + +BOOL CTxtWinHost::TxShowCaret(BOOL fShow) +{ + if (m_re->GetManager()->IsBackgroundTransparent()) + { + if(m_re->GetManager()->GetCurrentCaretObject() == m_re) + { + if((m_re->IsReadOnly() || !m_re->Activate())) + { + m_re->GetManager()->ShowCaret(false); + return TRUE; + } + } + + return m_re->GetManager()->ShowCaret(fShow == TRUE); + } + else + { + if(fShow) + return ::ShowCaret(m_re->GetManager()->GetPaintWindow()); + else + return ::HideCaret(m_re->GetManager()->GetPaintWindow()); + } +} + +BOOL CTxtWinHost::TxSetCaretPos(INT x, INT y) +{ + if (m_re->GetManager()->IsBackgroundTransparent()) + { + m_re->GetManager()->SetCaretPos(m_re, x, y); + return true; + } + else + return ::SetCaretPos(x, y); + +} + +BOOL CTxtWinHost::TxSetTimer(UINT idTimer, UINT uTimeout) +{ + fTimer = TRUE; + return m_re->GetManager()->SetTimer(m_re, idTimer, uTimeout) == TRUE; +} + +void CTxtWinHost::TxKillTimer(UINT idTimer) +{ + m_re->GetManager()->KillTimer(m_re, idTimer); + fTimer = FALSE; +} + +void CTxtWinHost::TxScrollWindowEx (INT dx, INT dy, LPCRECT lprcScroll, LPCRECT lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate, UINT fuScroll) +{ + return; +} + +void CTxtWinHost::TxSetCapture(BOOL fCapture) +{ + if (fCapture) m_re->GetManager()->SetCapture(); + else m_re->GetManager()->ReleaseCapture(); + fCaptured = fCapture; +} + +void CTxtWinHost::TxSetFocus() +{ + m_re->SetFocus(); +} + +void CTxtWinHost::TxSetCursor(HCURSOR hcur, BOOL fText) +{ + ::SetCursor(hcur); +} + +BOOL CTxtWinHost::TxScreenToClient(LPPOINT lppt) +{ + return ::ScreenToClient(m_re->GetManager()->GetPaintWindow(), lppt); +} + +BOOL CTxtWinHost::TxClientToScreen(LPPOINT lppt) +{ + return ::ClientToScreen(m_re->GetManager()->GetPaintWindow(), lppt); +} + +HRESULT CTxtWinHost::TxActivate(LONG *plOldState) +{ + return S_OK; +} + +HRESULT CTxtWinHost::TxDeactivate(LONG lNewState) +{ + return S_OK; +} + +HRESULT CTxtWinHost::TxGetClientRect(LPRECT prc) +{ + *prc = rcClient; + GetControlRect(prc); + return NOERROR; +} + +HRESULT CTxtWinHost::TxGetViewInset(LPRECT prc) +{ + prc->left = prc->right = prc->top = prc->bottom = 0; + return NOERROR; +} + +HRESULT CTxtWinHost::TxGetCharFormat(const CHARFORMATW **ppCF) +{ + *ppCF = &cf; + return NOERROR; +} + +HRESULT CTxtWinHost::TxGetParaFormat(const PARAFORMAT **ppPF) +{ + *ppPF = &pf; + return NOERROR; +} + +COLORREF CTxtWinHost::TxGetSysColor(int nIndex) +{ + DWORD dwColor = ::GetSysColor(nIndex); + CRenderEngine::CheckAlphaColor(dwColor); + return dwColor; +} + +HRESULT CTxtWinHost::TxGetBackStyle(TXTBACKSTYLE *pstyle) +{ + *pstyle = !fTransparent ? TXTBACK_OPAQUE : TXTBACK_TRANSPARENT; + return NOERROR; +} + +HRESULT CTxtWinHost::TxGetMaxLength(DWORD *pLength) +{ + *pLength = cchTextMost; + return NOERROR; +} + +HRESULT CTxtWinHost::TxGetScrollBars(DWORD *pdwScrollBar) +{ + *pdwScrollBar = dwStyle & (WS_VSCROLL | WS_HSCROLL | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_DISABLENOSCROLL); + + return NOERROR; +} + +HRESULT CTxtWinHost::TxGetPasswordChar(TCHAR *pch) +{ +#ifdef _UNICODE + *pch = chPasswordChar; +#else + ::WideCharToMultiByte(CP_ACP, 0, &chPasswordChar, 1, pch, 1, NULL, NULL) ; +#endif + return NOERROR; +} + +HRESULT CTxtWinHost::TxGetAcceleratorPos(LONG *pcp) +{ + *pcp = laccelpos; + return S_OK; +} + +HRESULT CTxtWinHost::OnTxCharFormatChange(const CHARFORMATW *pcf) +{ + return S_OK; +} + +HRESULT CTxtWinHost::OnTxParaFormatChange(const PARAFORMAT *ppf) +{ + return S_OK; +} + +HRESULT CTxtWinHost::TxGetPropertyBits(DWORD dwMask, DWORD *pdwBits) +{ + DWORD dwProperties = 0; + + if (fRich) + { + dwProperties = TXTBIT_RICHTEXT; + } + + if (dwStyle & ES_MULTILINE) + { + dwProperties |= TXTBIT_MULTILINE; + } + + if (dwStyle & ES_READONLY) + { + dwProperties |= TXTBIT_READONLY; + } + + if (dwStyle & ES_PASSWORD) + { + dwProperties |= TXTBIT_USEPASSWORD; + } + + if (!(dwStyle & ES_NOHIDESEL)) + { + dwProperties |= TXTBIT_HIDESELECTION; + } + + if (fEnableAutoWordSel) + { + dwProperties |= TXTBIT_AUTOWORDSEL; + } + + if (fWordWrap) + { + dwProperties |= TXTBIT_WORDWRAP; + } + + if (fAllowBeep) + { + dwProperties |= TXTBIT_ALLOWBEEP; + } + + if (fSaveSelection) + { + dwProperties |= TXTBIT_SAVESELECTION; + } + + *pdwBits = dwProperties & dwMask; + return NOERROR; +} + + +HRESULT CTxtWinHost::TxNotify(DWORD iNotify, void *pv) +{ + if( iNotify == EN_REQUESTRESIZE ) { + RECT rc; + REQRESIZE *preqsz = (REQRESIZE *)pv; + GetControlRect(&rc); + rc.bottom = rc.top + preqsz->rc.bottom; + rc.right = rc.left + preqsz->rc.right; + SetClientRect(&rc); + } + m_re->OnTxNotify(iNotify, pv); + return S_OK; +} + +HRESULT CTxtWinHost::TxGetExtent(LPSIZEL lpExtent) +{ + *lpExtent = sizelExtent; + return S_OK; +} + +HRESULT CTxtWinHost::TxGetSelectionBarWidth (LONG *plSelBarWidth) +{ + *plSelBarWidth = lSelBarWidth; + return S_OK; +} + +void CTxtWinHost::SetWordWrap(BOOL fWordWrap) +{ + fWordWrap = fWordWrap; + pserv->OnTxPropertyBitsChange(TXTBIT_WORDWRAP, fWordWrap ? TXTBIT_WORDWRAP : 0); +} + +BOOL CTxtWinHost::GetReadOnly() +{ + return (dwStyle & ES_READONLY) != 0; +} + +void CTxtWinHost::SetReadOnly(BOOL fReadOnly) +{ + if (fReadOnly) + { + dwStyle |= ES_READONLY; + } + else + { + dwStyle &= ~ES_READONLY; + } + + pserv->OnTxPropertyBitsChange(TXTBIT_READONLY, + fReadOnly ? TXTBIT_READONLY : 0); +} + +void CTxtWinHost::SetFont(HFONT hFont) +{ + if( hFont == NULL ) return; + LOGFONT lf; + ::GetObject(hFont, sizeof(LOGFONT), &lf); + LONG yPixPerInch = ::GetDeviceCaps(m_re->GetManager()->GetPaintDC(), LOGPIXELSY); + cf.yHeight = -lf.lfHeight * LY_PER_INCH / yPixPerInch; + if(lf.lfWeight >= FW_BOLD) + cf.dwEffects |= CFE_BOLD; + if(lf.lfItalic) + cf.dwEffects |= CFE_ITALIC; + if(lf.lfUnderline) + cf.dwEffects |= CFE_UNDERLINE; + cf.bCharSet = lf.lfCharSet; + cf.bPitchAndFamily = lf.lfPitchAndFamily; +#ifdef _UNICODE + _tcscpy(cf.szFaceName, lf.lfFaceName); +#else + //need to thunk pcf->szFaceName to a standard char string.in this case it's easy because our thunk is also our copy + MultiByteToWideChar(CP_ACP, 0, lf.lfFaceName, LF_FACESIZE, cf.szFaceName, LF_FACESIZE) ; +#endif + + pserv->OnTxPropertyBitsChange(TXTBIT_CHARFORMATCHANGE, + TXTBIT_CHARFORMATCHANGE); +} + +void CTxtWinHost::SetColor(DWORD dwColor) +{ + CRenderEngine::CheckAlphaColor(dwColor); + cf.crTextColor = RGB(GetBValue(dwColor), GetGValue(dwColor), GetRValue(dwColor)); + pserv->OnTxPropertyBitsChange(TXTBIT_CHARFORMATCHANGE, + TXTBIT_CHARFORMATCHANGE); +} + +SIZEL* CTxtWinHost::GetExtent() +{ + return &sizelExtent; +} + +void CTxtWinHost::SetExtent(SIZEL *psizelExtent) +{ + sizelExtent = *psizelExtent; + pserv->OnTxPropertyBitsChange(TXTBIT_EXTENTCHANGE, TXTBIT_EXTENTCHANGE); +} + +void CTxtWinHost::LimitText(LONG nChars) +{ + cchTextMost = nChars; + if( cchTextMost <= 0 ) cchTextMost = cInitTextMax; + pserv->OnTxPropertyBitsChange(TXTBIT_MAXLENGTHCHANGE, TXTBIT_MAXLENGTHCHANGE); +} + +BOOL CTxtWinHost::IsCaptured() +{ + return fCaptured; +} + +BOOL CTxtWinHost::GetAllowBeep() +{ + return fAllowBeep; +} + +void CTxtWinHost::SetAllowBeep(BOOL fAllowBeep) +{ + fAllowBeep = fAllowBeep; + + pserv->OnTxPropertyBitsChange(TXTBIT_ALLOWBEEP, + fAllowBeep ? TXTBIT_ALLOWBEEP : 0); +} + +WORD CTxtWinHost::GetDefaultAlign() +{ + return pf.wAlignment; +} + +void CTxtWinHost::SetDefaultAlign(WORD wNewAlign) +{ + pf.wAlignment = wNewAlign; + + // Notify control of property change + pserv->OnTxPropertyBitsChange(TXTBIT_PARAFORMATCHANGE, 0); +} + +BOOL CTxtWinHost::GetRichTextFlag() +{ + return fRich; +} + +void CTxtWinHost::SetRichTextFlag(BOOL fNew) +{ + fRich = fNew; + + pserv->OnTxPropertyBitsChange(TXTBIT_RICHTEXT, + fNew ? TXTBIT_RICHTEXT : 0); +} + +LONG CTxtWinHost::GetDefaultLeftIndent() +{ + return pf.dxOffset; +} + +void CTxtWinHost::SetDefaultLeftIndent(LONG lNewIndent) +{ + pf.dxOffset = lNewIndent; + + pserv->OnTxPropertyBitsChange(TXTBIT_PARAFORMATCHANGE, 0); +} + +void CTxtWinHost::SetClientRect(RECT *prc) +{ + rcClient = *prc; + + LONG xPerInch = ::GetDeviceCaps(m_re->GetManager()->GetPaintDC(), LOGPIXELSX); + LONG yPerInch = ::GetDeviceCaps(m_re->GetManager()->GetPaintDC(), LOGPIXELSY); + sizelExtent.cx = DXtoHimetricX(rcClient.right - rcClient.left, xPerInch); + sizelExtent.cy = DYtoHimetricY(rcClient.bottom - rcClient.top, yPerInch); + + pserv->OnTxPropertyBitsChange(TXTBIT_VIEWINSETCHANGE, TXTBIT_VIEWINSETCHANGE); +} + +BOOL CTxtWinHost::SetSaveSelection(BOOL f_SaveSelection) +{ + BOOL fResult = f_SaveSelection; + + fSaveSelection = f_SaveSelection; + + // notify text services of property change + pserv->OnTxPropertyBitsChange(TXTBIT_SAVESELECTION, + fSaveSelection ? TXTBIT_SAVESELECTION : 0); + + return fResult; +} + +HRESULT CTxtWinHost::OnTxInPlaceDeactivate() +{ + HRESULT hr = pserv->OnTxInPlaceDeactivate(); + + if (SUCCEEDED(hr)) + { + fInplaceActive = FALSE; + } + + return hr; +} + +HRESULT CTxtWinHost::OnTxInPlaceActivate(LPCRECT prcClient) +{ + fInplaceActive = TRUE; + + HRESULT hr = pserv->OnTxInPlaceActivate(prcClient); + + if (FAILED(hr)) + { + fInplaceActive = FALSE; + } + + return hr; +} + +BOOL CTxtWinHost::DoSetCursor(RECT *prc, POINT *pt) +{ + RECT rc = prc ? *prc : rcClient; + + // Is this in our rectangle? + if (PtInRect(&rc, *pt)) + { + RECT *prcClient = (!fInplaceActive || prc) ? &rc : NULL; + pserv->OnTxSetCursor(DVASPECT_CONTENT, -1, NULL, NULL, m_re->GetManager()->GetPaintDC(), + NULL, prcClient, pt->x, pt->y); + + return TRUE; + } + + return FALSE; +} + +void CTxtWinHost::GetControlRect(LPRECT prc) +{ + prc->top = rcClient.top; + prc->bottom = rcClient.bottom; + prc->left = rcClient.left; + prc->right = rcClient.right; +} + +void CTxtWinHost::SetTransparent(BOOL f_Transparent) +{ + fTransparent = f_Transparent; + + // notify text services of property change + pserv->OnTxPropertyBitsChange(TXTBIT_BACKSTYLECHANGE, 0); +} + +LONG CTxtWinHost::SetAccelPos(LONG l_accelpos) +{ + LONG laccelposOld = l_accelpos; + + laccelpos = l_accelpos; + + // notify text services of property change + pserv->OnTxPropertyBitsChange(TXTBIT_SHOWACCELERATOR, 0); + + return laccelposOld; +} + +WCHAR CTxtWinHost::SetPasswordChar(WCHAR ch_PasswordChar) +{ + WCHAR chOldPasswordChar = chPasswordChar; + + chPasswordChar = ch_PasswordChar; + + // notify text services of property change + pserv->OnTxPropertyBitsChange(TXTBIT_USEPASSWORD, + (chPasswordChar != 0) ? TXTBIT_USEPASSWORD : 0); + + return chOldPasswordChar; +} + +void CTxtWinHost::SetDisabled(BOOL fOn) +{ + cf.dwMask |= CFM_COLOR | CFM_DISABLED; + cf.dwEffects |= CFE_AUTOCOLOR | CFE_DISABLED; + + if( !fOn ) + { + cf.dwEffects &= ~CFE_DISABLED; + } + + pserv->OnTxPropertyBitsChange(TXTBIT_CHARFORMATCHANGE, + TXTBIT_CHARFORMATCHANGE); +} + +LONG CTxtWinHost::SetSelBarWidth(LONG l_SelBarWidth) +{ + LONG lOldSelBarWidth = lSelBarWidth; + + lSelBarWidth = l_SelBarWidth; + + if (lSelBarWidth) + { + dwStyle |= ES_SELECTIONBAR; + } + else + { + dwStyle &= (~ES_SELECTIONBAR); + } + + pserv->OnTxPropertyBitsChange(TXTBIT_SELBARCHANGE, TXTBIT_SELBARCHANGE); + + return lOldSelBarWidth; +} + +BOOL CTxtWinHost::GetTimerState() +{ + return fTimer; +} + +void CTxtWinHost::SetCharFormat(CHARFORMAT2W &c) +{ + cf = c; +} + +void CTxtWinHost::SetParaFormat(PARAFORMAT2 &p) +{ + pf = p; +} + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +CRichEditUI::CRichEditUI() : m_pTwh(NULL), m_bVScrollBarFixing(false), m_bWantTab(true), m_bWantReturn(true), + m_bWantCtrlReturn(true), m_bRich(true), m_bReadOnly(false), m_bWordWrap(false), m_dwTextColor(0), m_iFont(-1), + m_iLimitText(cInitTextMax), m_lTwhStyle(ES_MULTILINE), m_bInited(false), m_chLeadByte(0),m_uButtonState(0), + m_dwTipValueColor(0xFFBAC0C5) +{ +#ifndef _UNICODE + m_fAccumulateDBC =true; +#else + m_fAccumulateDBC= false; + ::ZeroMemory(&m_rcTextPadding, sizeof(m_rcTextPadding)); +#endif +} + +CRichEditUI::~CRichEditUI() +{ + if( m_pTwh ) { + m_pTwh->Release(); + m_pManager->RemoveMessageFilter(this); + } +} + +LPCTSTR CRichEditUI::GetClass() const +{ + return _T("RichEditUI"); +} + +LPVOID CRichEditUI::GetInterface(LPCTSTR pstrName) +{ + if( _tcscmp(pstrName, DUI_CTR_RICHEDIT) == 0 ) return static_cast(this); + return CContainerUI::GetInterface(pstrName); +} + +UINT CRichEditUI::GetControlFlags() const +{ + if( !IsEnabled() ) return CControlUI::GetControlFlags(); + + return UIFLAG_SETCURSOR | UIFLAG_TABSTOP; +} + +bool CRichEditUI::IsWantTab() +{ + return m_bWantTab; +} + +void CRichEditUI::SetWantTab(bool bWantTab) +{ + m_bWantTab = bWantTab; +} + +bool CRichEditUI::IsWantReturn() +{ + return m_bWantReturn; +} + +void CRichEditUI::SetWantReturn(bool bWantReturn) +{ + m_bWantReturn = bWantReturn; +} + +bool CRichEditUI::IsWantCtrlReturn() +{ + return m_bWantCtrlReturn; +} + +void CRichEditUI::SetWantCtrlReturn(bool bWantCtrlReturn) +{ + m_bWantCtrlReturn = bWantCtrlReturn; +} + +bool CRichEditUI::IsRich() +{ + return m_bRich; +} + +void CRichEditUI::SetRich(bool bRich) +{ + m_bRich = bRich; + if( m_pTwh ) m_pTwh->SetRichTextFlag(bRich); +} + +bool CRichEditUI::IsReadOnly() +{ + return m_bReadOnly; +} + +void CRichEditUI::SetReadOnly(bool bReadOnly) +{ + m_bReadOnly = bReadOnly; + if( m_pTwh ) m_pTwh->SetReadOnly(bReadOnly); +} + +bool CRichEditUI::GetWordWrap() +{ + return m_bWordWrap; +} + +void CRichEditUI::SetWordWrap(bool bWordWrap) +{ + m_bWordWrap = bWordWrap; + if( m_pTwh ) m_pTwh->SetWordWrap(bWordWrap); +} + +int CRichEditUI::GetFont() +{ + return m_iFont; +} + +void CRichEditUI::SetFont(int index) +{ + m_iFont = index; + if( m_pTwh ) { + m_pTwh->SetFont(GetManager()->GetFont(m_iFont)); + } +} + +void CRichEditUI::SetFont(LPCTSTR pStrFontName, int nSize, bool bBold, bool bUnderline, bool bItalic) +{ + if( m_pTwh ) { + LOGFONT lf = { 0 }; + ::GetObject(::GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &lf); + _tcsncpy(lf.lfFaceName, pStrFontName, LF_FACESIZE); + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfHeight = -nSize; + if( bBold ) lf.lfWeight += FW_BOLD; + if( bUnderline ) lf.lfUnderline = TRUE; + if( bItalic ) lf.lfItalic = TRUE; + HFONT hFont = ::CreateFontIndirect(&lf); + if( hFont == NULL ) return; + m_pTwh->SetFont(hFont); + ::DeleteObject(hFont); + } +} + +void CRichEditUI::SetEnabled(bool bEnabled) +{ + if (m_bEnabled == bEnabled) return; + + if( m_pTwh ) { + m_pTwh->SetColor(bEnabled ? m_dwTextColor : m_pManager->GetDefaultDisabledColor()); + } + + CContainerUI::SetEnabled(bEnabled); +} + +LONG CRichEditUI::GetWinStyle() +{ + return m_lTwhStyle; +} + +void CRichEditUI::SetWinStyle(LONG lStyle) +{ + m_lTwhStyle = lStyle; +} + +DWORD CRichEditUI::GetTextColor() +{ + return m_dwTextColor; +} + +void CRichEditUI::SetTextColor(DWORD dwTextColor) +{ + m_dwTextColor = dwTextColor; + if( m_pTwh ) { + m_pTwh->SetColor(dwTextColor); + } +} + +int CRichEditUI::GetLimitText() +{ + return m_iLimitText; +} + +void CRichEditUI::SetLimitText(int iChars) +{ + m_iLimitText = iChars; + if( m_pTwh ) { + m_pTwh->LimitText(m_iLimitText); + } +} + +long CRichEditUI::GetTextLength(DWORD dwFlags) const +{ + GETTEXTLENGTHEX textLenEx; + textLenEx.flags = dwFlags; +#ifdef _UNICODE + textLenEx.codepage = 1200; +#else + textLenEx.codepage = CP_ACP; +#endif + LRESULT lResult; + TxSendMessage(EM_GETTEXTLENGTHEX, (WPARAM)&textLenEx, 0, &lResult); + return (long)lResult; +} + +CDuiString CRichEditUI::GetText() const +{ + long lLen = GetTextLength(GTL_DEFAULT); + LPTSTR lpText = NULL; + GETTEXTEX gt; + gt.flags = GT_DEFAULT; +#ifdef _UNICODE + gt.cb = sizeof(TCHAR) * (lLen + 1) ; + gt.codepage = 1200; + lpText = new TCHAR[lLen + 1]; + ::ZeroMemory(lpText, (lLen + 1) * sizeof(TCHAR)); +#else + gt.cb = sizeof(TCHAR) * lLen * 2 + 1; + gt.codepage = CP_ACP; + lpText = new TCHAR[lLen * 2 + 1]; + ::ZeroMemory(lpText, (lLen * 2 + 1) * sizeof(TCHAR)); +#endif + gt.lpDefaultChar = NULL; + gt.lpUsedDefChar = NULL; + TxSendMessage(EM_GETTEXTEX, (WPARAM)>, (LPARAM)lpText, 0); + CDuiString sText(lpText); + delete[] lpText; + return sText; +} + +void CRichEditUI::SetText(LPCTSTR pstrText) +{ + m_sText = pstrText; + if( !m_pTwh ) return; + m_pTwh->SetColor(m_dwTextColor); + SetSel(0, -1); + ReplaceSel(pstrText, FALSE); +} + +bool CRichEditUI::GetModify() const +{ + if( !m_pTwh ) return false; + LRESULT lResult; + TxSendMessage(EM_GETMODIFY, 0, 0, &lResult); + return (BOOL)lResult == TRUE; +} + +void CRichEditUI::SetModify(bool bModified) const +{ + TxSendMessage(EM_SETMODIFY, bModified, 0, 0); +} + +void CRichEditUI::GetSel(CHARRANGE &cr) const +{ + TxSendMessage(EM_EXGETSEL, 0, (LPARAM)&cr, 0); +} + +void CRichEditUI::GetSel(long& nStartChar, long& nEndChar) const +{ + CHARRANGE cr; + TxSendMessage(EM_EXGETSEL, 0, (LPARAM)&cr, 0); + nStartChar = cr.cpMin; + nEndChar = cr.cpMax; +} + +int CRichEditUI::SetSel(CHARRANGE &cr) +{ + LRESULT lResult; + TxSendMessage(EM_EXSETSEL, 0, (LPARAM)&cr, &lResult); + return (int)lResult; +} + +int CRichEditUI::SetSel(long nStartChar, long nEndChar) +{ + CHARRANGE cr; + cr.cpMin = nStartChar; + cr.cpMax = nEndChar; + LRESULT lResult; + TxSendMessage(EM_EXSETSEL, 0, (LPARAM)&cr, &lResult); + return (int)lResult; +} + +void CRichEditUI::ReplaceSel(LPCTSTR lpszNewText, bool bCanUndo) +{ +#ifdef _UNICODE + TxSendMessage(EM_REPLACESEL, (WPARAM) bCanUndo, (LPARAM)lpszNewText, 0); +#else + int iLen = _tcslen(lpszNewText); + LPWSTR lpText = new WCHAR[iLen + 1]; + ::ZeroMemory(lpText, (iLen + 1) * sizeof(WCHAR)); + ::MultiByteToWideChar(CP_ACP, 0, lpszNewText, -1, (LPWSTR)lpText, iLen) ; + TxSendMessage(EM_REPLACESEL, (WPARAM) bCanUndo, (LPARAM)lpText, 0); + delete[] lpText; +#endif +} + +void CRichEditUI::ReplaceSelW(LPCWSTR lpszNewText, bool bCanUndo) +{ + TxSendMessage(EM_REPLACESEL, (WPARAM) bCanUndo, (LPARAM)lpszNewText, 0); +} + +CDuiString CRichEditUI::GetSelText() const +{ + if( !m_pTwh ) return CDuiString(); + CHARRANGE cr; + cr.cpMin = cr.cpMax = 0; + TxSendMessage(EM_EXGETSEL, 0, (LPARAM)&cr, 0); + LPWSTR lpText = NULL; + lpText = new WCHAR[cr.cpMax - cr.cpMin + 1]; + ::ZeroMemory(lpText, (cr.cpMax - cr.cpMin + 1) * sizeof(WCHAR)); + TxSendMessage(EM_GETSELTEXT, 0, (LPARAM)lpText, 0); + CDuiString sText; + sText = (LPCWSTR)lpText; + delete[] lpText; + return sText; +} + +int CRichEditUI::SetSelAll() +{ + return SetSel(0, -1); +} + +int CRichEditUI::SetSelNone() +{ + return SetSel(-1, 0); +} + +bool CRichEditUI::GetZoom(int& nNum, int& nDen) const +{ + LRESULT lResult; + TxSendMessage(EM_GETZOOM, (WPARAM)&nNum, (LPARAM)&nDen, &lResult); + return (BOOL)lResult == TRUE; +} + +bool CRichEditUI::SetZoom(int nNum, int nDen) +{ + if (nNum < 0 || nNum > 64) return false; + if (nDen < 0 || nDen > 64) return false; + LRESULT lResult; + TxSendMessage(EM_SETZOOM, nNum, nDen, &lResult); + return (BOOL)lResult == TRUE; +} + +bool CRichEditUI::SetZoomOff() +{ + LRESULT lResult; + TxSendMessage(EM_SETZOOM, 0, 0, &lResult); + return (BOOL)lResult == TRUE; +} + +WORD CRichEditUI::GetSelectionType() const +{ + LRESULT lResult; + TxSendMessage(EM_SELECTIONTYPE, 0, 0, &lResult); + return (WORD)lResult; +} + +bool CRichEditUI::GetAutoURLDetect() const +{ + LRESULT lResult; + TxSendMessage(EM_GETAUTOURLDETECT, 0, 0, &lResult); + return (BOOL)lResult == TRUE; +} + +bool CRichEditUI::SetAutoURLDetect(bool bAutoDetect) +{ + LRESULT lResult; + TxSendMessage(EM_AUTOURLDETECT, bAutoDetect, 0, &lResult); + return (BOOL)lResult == FALSE; +} + +DWORD CRichEditUI::GetEventMask() const +{ + LRESULT lResult; + TxSendMessage(EM_GETEVENTMASK, 0, 0, &lResult); + return (DWORD)lResult; +} + +DWORD CRichEditUI::SetEventMask(DWORD dwEventMask) +{ + LRESULT lResult; + TxSendMessage(EM_SETEVENTMASK, 0, dwEventMask, &lResult); + return (DWORD)lResult; +} + +CDuiString CRichEditUI::GetTextRange(long nStartChar, long nEndChar) const +{ + TEXTRANGEW tr = { 0 }; + tr.chrg.cpMin = nStartChar; + tr.chrg.cpMax = nEndChar; + LPWSTR lpText = NULL; + lpText = new WCHAR[nEndChar - nStartChar + 1]; + ::ZeroMemory(lpText, (nEndChar - nStartChar + 1) * sizeof(WCHAR)); + tr.lpstrText = lpText; + TxSendMessage(EM_GETTEXTRANGE, 0, (LPARAM)&tr, 0); + CDuiString sText; + sText = (LPCWSTR)lpText; + delete[] lpText; + return sText; +} + +void CRichEditUI::HideSelection(bool bHide, bool bChangeStyle) +{ + TxSendMessage(EM_HIDESELECTION, bHide, bChangeStyle, 0); +} + +void CRichEditUI::ScrollCaret() +{ + TxSendMessage(EM_SCROLLCARET, 0, 0, 0); +} + +int CRichEditUI::InsertText(long nInsertAfterChar, LPCTSTR lpstrText, bool bCanUndo) +{ + int nRet = SetSel(nInsertAfterChar, nInsertAfterChar); + ReplaceSel(lpstrText, bCanUndo); + return nRet; +} + +int CRichEditUI::AppendText(LPCTSTR lpstrText, bool bCanUndo) +{ + int nRet = SetSel(-1, -1); + ReplaceSel(lpstrText, bCanUndo); + return nRet; +} + +DWORD CRichEditUI::GetDefaultCharFormat(CHARFORMAT2 &cf) const +{ + cf.cbSize = sizeof(CHARFORMAT2); + LRESULT lResult; + TxSendMessage(EM_GETCHARFORMAT, 0, (LPARAM)&cf, &lResult); + return (DWORD)lResult; +} + +bool CRichEditUI::SetDefaultCharFormat(CHARFORMAT2 &cf) +{ + if( !m_pTwh ) return false; + cf.cbSize = sizeof(CHARFORMAT2); + LRESULT lResult; + TxSendMessage(EM_SETCHARFORMAT, 0, (LPARAM)&cf, &lResult); + if( (BOOL)lResult == TRUE ) { + CHARFORMAT2W cfw; + cfw.cbSize = sizeof(CHARFORMAT2W); + TxSendMessage(EM_GETCHARFORMAT, 1, (LPARAM)&cfw, 0); + m_pTwh->SetCharFormat(cfw); + return true; + } + return false; +} + +DWORD CRichEditUI::GetSelectionCharFormat(CHARFORMAT2 &cf) const +{ + cf.cbSize = sizeof(CHARFORMAT2); + LRESULT lResult; + TxSendMessage(EM_GETCHARFORMAT, 1, (LPARAM)&cf, &lResult); + return (DWORD)lResult; +} + +bool CRichEditUI::SetSelectionCharFormat(CHARFORMAT2 &cf) +{ + if(m_pManager->IsBackgroundTransparent()) + { + CRenderEngine::CheckAlphaColor(cf.crTextColor); + CRenderEngine::CheckAlphaColor(cf.crBackColor); + } + if( !m_pTwh ) return false; + cf.cbSize = sizeof(CHARFORMAT2); + LRESULT lResult; + TxSendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf, &lResult); + return (BOOL)lResult == TRUE; +} + +bool CRichEditUI::SetWordCharFormat(CHARFORMAT2 &cf) +{ + if( !m_pTwh ) return false; + cf.cbSize = sizeof(CHARFORMAT2); + LRESULT lResult; + TxSendMessage(EM_SETCHARFORMAT, SCF_SELECTION|SCF_WORD, (LPARAM)&cf, &lResult); + return (BOOL)lResult == TRUE; +} + +DWORD CRichEditUI::GetParaFormat(PARAFORMAT2 &pf) const +{ + pf.cbSize = sizeof(PARAFORMAT2); + LRESULT lResult; + TxSendMessage(EM_GETPARAFORMAT, 0, (LPARAM)&pf, &lResult); + return (DWORD)lResult; +} + +bool CRichEditUI::SetParaFormat(PARAFORMAT2 &pf) +{ + if( !m_pTwh ) return false; + pf.cbSize = sizeof(PARAFORMAT2); + LRESULT lResult; + TxSendMessage(EM_SETPARAFORMAT, 0, (LPARAM)&pf, &lResult); + if( (BOOL)lResult == TRUE ) { + m_pTwh->SetParaFormat(pf); + return true; + } + return false; +} + +bool CRichEditUI::Redo() +{ + if( !m_pTwh ) return false; + LRESULT lResult; + TxSendMessage(EM_REDO, 0, 0, &lResult); + return (BOOL)lResult == TRUE; +} + +bool CRichEditUI::Undo() +{ + if( !m_pTwh ) return false; + LRESULT lResult; + TxSendMessage(EM_UNDO, 0, 0, &lResult); + return (BOOL)lResult == TRUE; +} + +void CRichEditUI::Clear() +{ + TxSendMessage(WM_CLEAR, 0, 0, 0); +} + +void CRichEditUI::Copy() +{ + TxSendMessage(WM_COPY, 0, 0, 0); +} + +void CRichEditUI::Cut() +{ + TxSendMessage(WM_CUT, 0, 0, 0); +} + +void CRichEditUI::Paste() +{ + TxSendMessage(WM_PASTE, 0, 0, 0); +} + +int CRichEditUI::GetLineCount() const +{ + if( !m_pTwh ) return 0; + LRESULT lResult; + TxSendMessage(EM_GETLINECOUNT, 0, 0, &lResult); + return (int)lResult; +} + +CDuiString CRichEditUI::GetLine(int nIndex, int nMaxLength) const +{ + LPWSTR lpText = NULL; + lpText = new WCHAR[nMaxLength + 1]; + ::ZeroMemory(lpText, (nMaxLength + 1) * sizeof(WCHAR)); + *(LPWORD)lpText = (WORD)nMaxLength; + TxSendMessage(EM_GETLINE, nIndex, (LPARAM)lpText, 0); + CDuiString sText; + sText = (LPCWSTR)lpText; + delete[] lpText; + return sText; +} + +int CRichEditUI::LineIndex(int nLine) const +{ + LRESULT lResult; + TxSendMessage(EM_LINEINDEX, nLine, 0, &lResult); + return (int)lResult; +} + +int CRichEditUI::LineLength(int nLine) const +{ + LRESULT lResult; + TxSendMessage(EM_LINELENGTH, nLine, 0, &lResult); + return (int)lResult; +} + +bool CRichEditUI::LineScroll(int nLines, int nChars) +{ + LRESULT lResult; + TxSendMessage(EM_LINESCROLL, nChars, nLines, &lResult); + return (BOOL)lResult == TRUE; +} + +CDuiPoint CRichEditUI::GetCharPos(long lChar) const +{ + CDuiPoint pt; + TxSendMessage(EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM)lChar, 0); + return pt; +} + +long CRichEditUI::LineFromChar(long nIndex) const +{ + if( !m_pTwh ) return 0L; + LRESULT lResult; + TxSendMessage(EM_EXLINEFROMCHAR, 0, nIndex, &lResult); + return (long)lResult; +} + +CDuiPoint CRichEditUI::PosFromChar(UINT nChar) const +{ + POINTL pt; + TxSendMessage(EM_POSFROMCHAR, (WPARAM)&pt, nChar, 0); + return CDuiPoint(pt.x, pt.y); +} + +int CRichEditUI::CharFromPos(CDuiPoint pt) const +{ + POINTL ptl = {pt.x, pt.y}; + if( !m_pTwh ) return 0; + LRESULT lResult; + TxSendMessage(EM_CHARFROMPOS, 0, (LPARAM)&ptl, &lResult); + return (int)lResult; +} + +void CRichEditUI::EmptyUndoBuffer() +{ + TxSendMessage(EM_EMPTYUNDOBUFFER, 0, 0, 0); +} + +UINT CRichEditUI::SetUndoLimit(UINT nLimit) +{ + if( !m_pTwh ) return 0; + LRESULT lResult; + TxSendMessage(EM_SETUNDOLIMIT, (WPARAM) nLimit, 0, &lResult); + return (UINT)lResult; +} + +long CRichEditUI::StreamIn(int nFormat, EDITSTREAM &es) +{ + if( !m_pTwh ) return 0L; + LRESULT lResult; + TxSendMessage(EM_STREAMIN, nFormat, (LPARAM)&es, &lResult); + return (long)lResult; +} + +long CRichEditUI::StreamOut(int nFormat, EDITSTREAM &es) +{ + if( !m_pTwh ) return 0L; + LRESULT lResult; + TxSendMessage(EM_STREAMOUT, nFormat, (LPARAM)&es, &lResult); + return (long)lResult; +} + +void CRichEditUI::DoInit() +{ + if(m_bInited) + return ; + + CREATESTRUCT cs; + cs.style = m_lTwhStyle; + cs.x = 0; + cs.y = 0; + cs.cy = 0; + cs.cx = 0; + cs.lpszName = m_sText.GetData(); + CreateHost(this, &cs, &m_pTwh); + if( m_pTwh ) { + m_pTwh->SetTransparent(TRUE); + LRESULT lResult; + m_pTwh->GetTextServices()->TxSendMessage(EM_SETLANGOPTIONS, 0, 0, &lResult); + m_pTwh->OnTxInPlaceActivate(NULL); + m_pManager->AddMessageFilter(this); + if (!m_bEnabled) + { + m_pTwh->SetColor(m_pManager->GetDefaultDisabledColor()); + } + else if (GetText() == _T("") && !m_sTipValue.IsEmpty()) + { + SetText(m_sTipValue); + m_pTwh->SetColor(m_dwTipValueColor); + } + } + + m_bInited= true; +} + +HRESULT CRichEditUI::TxSendMessage(UINT msg, WPARAM wparam, LPARAM lparam, LRESULT *plresult) const +{ + if( m_pTwh ) { + if( msg == WM_KEYDOWN && TCHAR(wparam) == VK_RETURN ) { + if( !m_bWantReturn || (::GetKeyState(VK_CONTROL) < 0 && !m_bWantCtrlReturn) ) { + if( m_pManager != NULL ) m_pManager->SendNotify((CControlUI*)this, DUI_MSGTYPE_RETURN); + return S_OK; + } + } + return m_pTwh->GetTextServices()->TxSendMessage(msg, wparam, lparam, plresult); + } + return S_FALSE; +} + +IDropTarget* CRichEditUI::GetTxDropTarget() +{ + IDropTarget *pdt = NULL; + if( m_pTwh->GetTextServices()->TxGetDropTarget(&pdt) == NOERROR ) return pdt; + return NULL; +} + +bool CRichEditUI::OnTxViewChanged() +{ + return true; +} + +bool CRichEditUI::SetDropAcceptFile(bool bAccept) +{ + LRESULT lResult; + TxSendMessage(EM_SETEVENTMASK, 0,ENM_DROPFILES|ENM_LINK, // ENM_CHANGE| ENM_CORRECTTEXT | ENM_DRAGDROPDONE | ENM_DROPFILES | ENM_IMECHANGE | ENM_LINK | ENM_OBJECTPOSITIONS | ENM_PROTECTED | ENM_REQUESTRESIZE | ENM_SCROLL | ENM_SELCHANGE | ENM_UPDATE, + &lResult); + return (BOOL)lResult == FALSE; +} + +void CRichEditUI::OnTxNotify(DWORD iNotify, void *pv) +{ + switch(iNotify) + { + case EN_CHANGE: + { + GetManager()->SendNotify(this, DUI_MSGTYPE_TEXTCHANGED); + } + break; + case EN_DROPFILES: + case EN_MSGFILTER: + case EN_OLEOPFAILED: + case EN_PROTECTED: + case EN_SAVECLIPBOARD: + case EN_SELCHANGE: + case EN_STOPNOUNDO: + case EN_LINK: + case EN_OBJECTPOSITIONS: + case EN_DRAGDROPDONE: + { + if(pv) // Fill out NMHDR portion of pv + { + LONG nId = GetWindowLong(this->GetManager()->GetPaintWindow(), GWL_ID); + NMHDR *phdr = (NMHDR *)pv; + phdr->hwndFrom = this->GetManager()->GetPaintWindow(); + phdr->idFrom = nId; + phdr->code = iNotify; + + if(SendMessage(this->GetManager()->GetPaintWindow(), WM_NOTIFY, (WPARAM) nId, (LPARAM) pv)) + { + //hr = S_FALSE; + } + } + } + break; + } +} + +// зrichʽricheditһbugһǿʱLineDownSetScrollPos޷ +// iPosΪbug +void CRichEditUI::SetScrollPos(SIZE szPos) +{ + int cx = 0; + int cy = 0; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) { + int iLastScrollPos = m_pVerticalScrollBar->GetScrollPos(); + m_pVerticalScrollBar->SetScrollPos(szPos.cy); + cy = m_pVerticalScrollBar->GetScrollPos() - iLastScrollPos; + } + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) { + int iLastScrollPos = m_pHorizontalScrollBar->GetScrollPos(); + m_pHorizontalScrollBar->SetScrollPos(szPos.cx); + cx = m_pHorizontalScrollBar->GetScrollPos() - iLastScrollPos; + } + if( cy != 0 ) { + int iPos = 0; + if( m_pTwh && !m_bRich && m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) + iPos = m_pVerticalScrollBar->GetScrollPos(); + WPARAM wParam = MAKEWPARAM(SB_THUMBPOSITION, m_pVerticalScrollBar->GetScrollPos()); + TxSendMessage(WM_VSCROLL, wParam, 0L, 0); + if( m_pTwh && !m_bRich && m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) { + if( cy > 0 && m_pVerticalScrollBar->GetScrollPos() <= iPos ) + m_pVerticalScrollBar->SetScrollPos(iPos); + } + } + if( cx != 0 ) { + WPARAM wParam = MAKEWPARAM(SB_THUMBPOSITION, m_pHorizontalScrollBar->GetScrollPos()); + TxSendMessage(WM_HSCROLL, wParam, 0L, 0); + } +} + +void CRichEditUI::LineUp() +{ + TxSendMessage(WM_VSCROLL, SB_LINEUP, 0L, 0); +} + +void CRichEditUI::LineDown() +{ + int iPos = 0; + if( m_pTwh && !m_bRich && m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) + iPos = m_pVerticalScrollBar->GetScrollPos(); + TxSendMessage(WM_VSCROLL, SB_LINEDOWN, 0L, 0); + if( m_pTwh && !m_bRich && m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) { + if( m_pVerticalScrollBar->GetScrollPos() <= iPos ) + m_pVerticalScrollBar->SetScrollPos(m_pVerticalScrollBar->GetScrollRange()); + } +} + +void CRichEditUI::PageUp() +{ + TxSendMessage(WM_VSCROLL, SB_PAGEUP, 0L, 0); +} + +void CRichEditUI::PageDown() +{ + TxSendMessage(WM_VSCROLL, SB_PAGEDOWN, 0L, 0); +} + +void CRichEditUI::HomeUp() +{ + TxSendMessage(WM_VSCROLL, SB_TOP, 0L, 0); +} + +void CRichEditUI::EndDown() +{ + TxSendMessage(WM_VSCROLL, SB_BOTTOM, 0L, 0); +} + +void CRichEditUI::LineLeft() +{ + TxSendMessage(WM_HSCROLL, SB_LINELEFT, 0L, 0); +} + +void CRichEditUI::LineRight() +{ + TxSendMessage(WM_HSCROLL, SB_LINERIGHT, 0L, 0); +} + +void CRichEditUI::PageLeft() +{ + TxSendMessage(WM_HSCROLL, SB_PAGELEFT, 0L, 0); +} + +void CRichEditUI::PageRight() +{ + TxSendMessage(WM_HSCROLL, SB_PAGERIGHT, 0L, 0); +} + +void CRichEditUI::HomeLeft() +{ + TxSendMessage(WM_HSCROLL, SB_LEFT, 0L, 0); +} + +void CRichEditUI::EndRight() +{ + TxSendMessage(WM_HSCROLL, SB_RIGHT, 0L, 0); +} + +void CRichEditUI::DoEvent(TEventUI& event) +{ + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pParent != NULL ) m_pParent->DoEvent(event); + else CControlUI::DoEvent(event); + return; + } + + else if( event.Type == UIEVENT_SETCURSOR && IsEnabled() ) + { + if( m_pTwh && m_pTwh->DoSetCursor(NULL, &event.ptMouse) ) { + return; + } + } + if( event.Type == UIEVENT_SETFOCUS ) { + if( m_pTwh ) { + m_pTwh->OnTxInPlaceActivate(NULL); + if (GetText() == m_sTipValue) + { + SetText(_T("")); + m_pTwh->SetColor(m_dwTextColor); + } + m_pTwh->GetTextServices()->TxSendMessage(WM_SETFOCUS, 0, 0, 0); + } + m_bFocused = true; + Invalidate(); + return; + } + if( event.Type == UIEVENT_KILLFOCUS ) { + if( m_pTwh ) { + m_pTwh->OnTxInPlaceActivate(NULL); + if (GetText() == _T("") && !m_sTipValue.IsEmpty()) + { + SetText(m_sTipValue); + m_pTwh->SetColor(m_dwTipValueColor); + } + m_pTwh->GetTextServices()->TxSendMessage(WM_KILLFOCUS, 0, 0, 0); + } + m_bFocused = false; + Invalidate(); + return; + } + else if( event.Type == UIEVENT_TIMER ) { + if( m_pTwh ) { + m_pTwh->GetTextServices()->TxSendMessage(WM_TIMER, event.wParam, event.lParam, 0); + } + } + else if( event.Type == UIEVENT_SCROLLWHEEL ) { + if( (event.wKeyState & MK_CONTROL) != 0 ) { + return; + } + } + else if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK ) + { + return; + } + else if( event.Type == UIEVENT_MOUSEMOVE ) + { + return; + } + else if( event.Type == UIEVENT_BUTTONUP ) + { + return; + } + else if( event.Type == UIEVENT_MOUSEENTER ) + { + if( IsEnabled() ) { + m_uButtonState |= UISTATE_HOT; + Invalidate(); + } + return; + } + else if( event.Type == UIEVENT_MOUSELEAVE ) + { + if( IsEnabled() ) { + m_uButtonState &= ~UISTATE_HOT; + Invalidate(); + } + return; + } + if( event.Type > UIEVENT__KEYBEGIN && event.Type < UIEVENT__KEYEND ) + { + return; + } + CContainerUI::DoEvent(event); +} + + +LPCTSTR CRichEditUI::GetNormalImage() +{ + return m_sNormalImage; +} + +void CRichEditUI::SetNormalImage(LPCTSTR pStrImage) +{ + m_sNormalImage = pStrImage; + Invalidate(); +} + +LPCTSTR CRichEditUI::GetHotImage() +{ + return m_sHotImage; +} + +void CRichEditUI::SetHotImage(LPCTSTR pStrImage) +{ + m_sHotImage = pStrImage; + Invalidate(); +} + +LPCTSTR CRichEditUI::GetFocusedImage() +{ + return m_sFocusedImage; +} + +void CRichEditUI::SetFocusedImage(LPCTSTR pStrImage) +{ + m_sFocusedImage = pStrImage; + Invalidate(); +} + +LPCTSTR CRichEditUI::GetDisabledImage() +{ + return m_sDisabledImage; +} + +void CRichEditUI::SetDisabledImage(LPCTSTR pStrImage) +{ + m_sDisabledImage = pStrImage; + Invalidate(); +} + +RECT CRichEditUI::GetTextPadding() const +{ + return m_rcTextPadding; +} + +void CRichEditUI::SetTextPadding(RECT rc) +{ + m_rcTextPadding = rc; + Invalidate(); +} + +void CRichEditUI::SetTipValue( LPCTSTR pStrTipValue ) +{ + m_sTipValue = pStrTipValue; +} + +LPCTSTR CRichEditUI::GetTipValue() +{ + return m_sTipValue.GetData(); +} + +void CRichEditUI::SetTipValueColor( LPCTSTR pStrColor ) +{ + if( *pStrColor == _T('#')) pStrColor = ::CharNext(pStrColor); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pStrColor, &pstr, 16); + + m_dwTipValueColor = clrColor; +} + +DWORD CRichEditUI::GetTipValueColor() +{ + return m_dwTipValueColor; +} + +void CRichEditUI::PaintStatusImage(HDC hDC) +{ + if( IsFocused() ) m_uButtonState |= UISTATE_FOCUSED; + else m_uButtonState &= ~ UISTATE_FOCUSED; + if( !IsEnabled() ) m_uButtonState |= UISTATE_DISABLED; + else m_uButtonState &= ~ UISTATE_DISABLED; + + if( (m_uButtonState & UISTATE_DISABLED) != 0 ) { + if( !m_sDisabledImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sDisabledImage) ) m_sDisabledImage.Empty(); + else return; + } + } + else if( (m_uButtonState & UISTATE_FOCUSED) != 0 ) { + if( !m_sFocusedImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sFocusedImage) ) m_sFocusedImage.Empty(); + else return; + } + } + else if( (m_uButtonState & UISTATE_HOT ) != 0 ) { + if( !m_sHotImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sHotImage) ) m_sHotImage.Empty(); + else return; + } + } + + if( !m_sNormalImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sNormalImage) ) m_sNormalImage.Empty(); + else return; + } +} + +SIZE CRichEditUI::EstimateSize(SIZE szAvailable) +{ + //return CDuiSize(m_rcItem); // ַʽڵһôС֮ʹС + return CContainerUI::EstimateSize(szAvailable); +} + +void CRichEditUI::SetPos(RECT rc) +{ + CControlUI::SetPos(rc); + rc = m_rcItem; + + rc.left += m_rcInset.left; + rc.top += m_rcInset.top; + rc.right -= m_rcInset.right; + rc.bottom -= m_rcInset.bottom; + bool bVScrollBarVisiable = false; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) { + bVScrollBarVisiable = true; + rc.right -= m_pVerticalScrollBar->GetFixedWidth(); + } + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) { + rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight(); + } + + if( m_pTwh ) { + RECT rcRich = rc; + rcRich.left += m_rcTextPadding.left; + rcRich.right -= m_rcTextPadding.right; + rcRich.top += m_rcTextPadding.top; + rcRich.bottom -= m_rcTextPadding.bottom; + m_pTwh->SetClientRect(&rcRich); + if( bVScrollBarVisiable && (!m_pVerticalScrollBar->IsVisible() || m_bVScrollBarFixing) ) { + LONG lWidth = rcRich.right - rcRich.left + m_pVerticalScrollBar->GetFixedWidth(); + LONG lHeight = 0; + SIZEL szExtent = { -1, -1 }; + m_pTwh->GetTextServices()->TxGetNaturalSize( + DVASPECT_CONTENT, + GetManager()->GetPaintDC(), + NULL, + NULL, + TXTNS_FITTOCONTENT, + &szExtent, + &lWidth, + &lHeight); + if( lHeight > rcRich.bottom - rcRich.top ) { + m_pVerticalScrollBar->SetVisible(true); + m_pVerticalScrollBar->SetScrollPos(0); + m_bVScrollBarFixing = true; + } + else { + if( m_bVScrollBarFixing ) { + m_pVerticalScrollBar->SetVisible(false); + m_bVScrollBarFixing = false; + } + } + } + } + + if( m_pVerticalScrollBar != NULL && m_pVerticalScrollBar->IsVisible() ) { + RECT rcScrollBarPos = { rc.right, rc.top, rc.right + m_pVerticalScrollBar->GetFixedWidth(), rc.bottom}; + m_pVerticalScrollBar->SetPos(rcScrollBarPos); + } + if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() ) { + RECT rcScrollBarPos = { rc.left, rc.bottom, rc.right, rc.bottom + m_pHorizontalScrollBar->GetFixedHeight()}; + m_pHorizontalScrollBar->SetPos(rcScrollBarPos); + } + + SIZE szAvailable = { rc.right - rc.left, rc.bottom - rc.top }; + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) + szAvailable.cx += m_pHorizontalScrollBar->GetScrollRange(); + + int nAdjustables = 0; + int cxFixed = 0; + int nEstimateNum = 0; + for( int it1 = 0; it1 < m_items.GetSize(); it1++ ) { + CControlUI* pControl = static_cast(m_items[it1]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) continue; + SIZE sz = pControl->EstimateSize(szAvailable); + if( sz.cx == 0 ) { + nAdjustables++; + } + else { + if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); + if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); + } + cxFixed += sz.cx + pControl->GetPadding().left + pControl->GetPadding().right; + nEstimateNum++; + } + cxFixed += (nEstimateNum - 1) * m_iChildPadding; + + int cxExpand = 0; + int cxNeeded = 0; + if( nAdjustables > 0 ) cxExpand = MAX(0, (szAvailable.cx - cxFixed) / nAdjustables); + // Position the elements + SIZE szRemaining = szAvailable; + int iPosX = rc.left; + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) { + iPosX -= m_pHorizontalScrollBar->GetScrollPos(); + } + int iAdjustable = 0; + int cxFixedRemaining = cxFixed; + for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) { + CControlUI* pControl = static_cast(m_items[it2]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) { + SetFloatPos(it2); + continue; + } + RECT rcPadding = pControl->GetPadding(); + szRemaining.cx -= rcPadding.left; + SIZE sz = pControl->EstimateSize(szRemaining); + if( sz.cx == 0 ) { + iAdjustable++; + sz.cx = cxExpand; + + if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); + if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); + } + else { + if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); + if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); + + } + + sz.cy = pControl->GetFixedHeight(); + if( sz.cy == 0 ) sz.cy = rc.bottom - rc.top - rcPadding.top - rcPadding.bottom; + if( sz.cy < 0 ) sz.cy = 0; + if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight(); + if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight(); + + RECT rcCtrl = { iPosX + rcPadding.left, rc.top + rcPadding.top, iPosX + sz.cx + rcPadding.left , rc.top + rcPadding.top + sz.cy}; + pControl->SetPos(rcCtrl); + iPosX += sz.cx + m_iChildPadding + rcPadding.left + rcPadding.right; + cxNeeded += sz.cx + rcPadding.left + rcPadding.right; + szRemaining.cx -= sz.cx + m_iChildPadding + rcPadding.right; + } + cxNeeded += (nEstimateNum - 1) * m_iChildPadding; + //reddrain + if( m_pHorizontalScrollBar != NULL ) { + if( cxNeeded > rc.right - rc.left ) { + if( m_pHorizontalScrollBar->IsVisible() ) { + m_pHorizontalScrollBar->SetScrollRange(cxNeeded - (rc.right - rc.left)); + } + else { + m_pHorizontalScrollBar->SetVisible(true); + m_pHorizontalScrollBar->SetScrollRange(cxNeeded - (rc.right - rc.left)); + m_pHorizontalScrollBar->SetScrollPos(0); + rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight(); + } + } + else { + if( m_pHorizontalScrollBar->IsVisible() ) { + m_pHorizontalScrollBar->SetVisible(false); + m_pHorizontalScrollBar->SetScrollRange(0); + m_pHorizontalScrollBar->SetScrollPos(0); + rc.bottom += m_pHorizontalScrollBar->GetFixedHeight(); + } + } + } +} + +void CRichEditUI::DoPaint(HDC hDC, const RECT& rcPaint) +{ + RECT rcTemp = { 0 }; + if( !::IntersectRect(&rcTemp, &rcPaint, &m_rcItem) ) return; + + CRenderClip clip; + CRenderClip::GenerateClip(hDC, rcTemp, clip); + CControlUI::DoPaint(hDC, rcPaint); + + if( m_pTwh ) { + RECT rc; + m_pTwh->GetControlRect(&rc); + // Remember wparam is actually the hdc and lparam is the update + // rect because this message has been preprocessed by the window. + m_pTwh->GetTextServices()->TxDraw( + DVASPECT_CONTENT, // Draw Aspect + /*-1*/0, // Lindex + NULL, // Info for drawing optimazation + NULL, // target device information + hDC, // Draw device HDC + NULL, // Target device HDC + (RECTL*)&rc, // Bounding client rectangle + NULL, // Clipping rectangle for metafiles + (RECT*)&rcPaint, // Update rectangle + NULL, // Call back function + NULL, // Call back parameter + 0); // What view of the object + if( m_bVScrollBarFixing ) { + LONG lWidth = rc.right - rc.left + m_pVerticalScrollBar->GetFixedWidth(); + LONG lHeight = 0; + SIZEL szExtent = { -1, -1 }; + m_pTwh->GetTextServices()->TxGetNaturalSize( + DVASPECT_CONTENT, + GetManager()->GetPaintDC(), + NULL, + NULL, + TXTNS_FITTOCONTENT, + &szExtent, + &lWidth, + &lHeight); + if( lHeight <= rc.bottom - rc.top ) { + NeedUpdate(); + } + } + } + + if( m_items.GetSize() > 0 ) { + RECT rc = m_rcItem; + rc.left += m_rcInset.left; + rc.top += m_rcInset.top; + rc.right -= m_rcInset.right; + rc.bottom -= m_rcInset.bottom; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth(); + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight(); + + if( !::IntersectRect(&rcTemp, &rcPaint, &rc) ) { + for( int it = 0; it < m_items.GetSize(); it++ ) { + CControlUI* pControl = static_cast(m_items[it]); + if( !pControl->IsVisible() ) continue; + if( !::IntersectRect(&rcTemp, &rcPaint, &pControl->GetPos()) ) continue; + if( pControl ->IsFloat() ) { + if( !::IntersectRect(&rcTemp, &m_rcItem, &pControl->GetPos()) ) continue; + pControl->DoPaint(hDC, rcPaint); + } + } + } + else { + CRenderClip childClip; + CRenderClip::GenerateClip(hDC, rcTemp, childClip); + for( int it = 0; it < m_items.GetSize(); it++ ) { + CControlUI* pControl = static_cast(m_items[it]); + if( !pControl->IsVisible() ) continue; + if( !::IntersectRect(&rcTemp, &rcPaint, &pControl->GetPos()) ) continue; + if( pControl ->IsFloat() ) { + if( !::IntersectRect(&rcTemp, &m_rcItem, &pControl->GetPos()) ) continue; + CRenderClip::UseOldClipBegin(hDC, childClip); + pControl->DoPaint(hDC, rcPaint); + CRenderClip::UseOldClipEnd(hDC, childClip); + } + else { + if( !::IntersectRect(&rcTemp, &rc, &pControl->GetPos()) ) continue; + pControl->DoPaint(hDC, rcPaint); + } + } + } + } + + if( m_pVerticalScrollBar != NULL && m_pVerticalScrollBar->IsVisible() ) { + if( ::IntersectRect(&rcTemp, &rcPaint, &m_pVerticalScrollBar->GetPos()) ) { + m_pVerticalScrollBar->DoPaint(hDC, rcPaint); + } + } + + if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() ) { + if( ::IntersectRect(&rcTemp, &rcPaint, &m_pHorizontalScrollBar->GetPos()) ) { + m_pHorizontalScrollBar->DoPaint(hDC, rcPaint); + } + } +} + +void CRichEditUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) +{ + if( _tcscmp(pstrName, _T("vscrollbar")) == 0 ) { + if( _tcscmp(pstrValue, _T("true")) == 0 ) m_lTwhStyle |= ES_DISABLENOSCROLL | WS_VSCROLL; + } + else if( _tcscmp(pstrName, _T("autovscroll")) == 0 ) { + if( _tcscmp(pstrValue, _T("true")) == 0 ) m_lTwhStyle |= ES_AUTOVSCROLL; + } + else if( _tcscmp(pstrName, _T("hscrollbar")) == 0 ) { + if( _tcscmp(pstrValue, _T("true")) == 0 ) m_lTwhStyle |= ES_DISABLENOSCROLL | WS_HSCROLL; + } + else if( _tcscmp(pstrName, _T("autohscroll")) == 0 ) { + if( _tcscmp(pstrValue, _T("true")) == 0 ) m_lTwhStyle |= ES_AUTOHSCROLL; + } + else if( _tcscmp(pstrName, _T("wanttab")) == 0 ) { + SetWantTab(_tcscmp(pstrValue, _T("true")) == 0); + } + else if( _tcscmp(pstrName, _T("wantreturn")) == 0 ) { + SetWantReturn(_tcscmp(pstrValue, _T("true")) == 0); + } + else if( _tcscmp(pstrName, _T("wantctrlreturn")) == 0 ) { + SetWantCtrlReturn(_tcscmp(pstrValue, _T("true")) == 0); + } + else if( _tcscmp(pstrName, _T("rich")) == 0 ) { + SetRich(_tcscmp(pstrValue, _T("true")) == 0); + } + else if( _tcscmp(pstrName, _T("multiline")) == 0 ) { + if( _tcscmp(pstrValue, _T("false")) == 0 ) m_lTwhStyle &= ~ES_MULTILINE; + } + else if( _tcscmp(pstrName, _T("readonly")) == 0 ) { + if( _tcscmp(pstrValue, _T("true")) == 0 ) { m_lTwhStyle |= ES_READONLY; m_bReadOnly = true; } + } + else if( _tcscmp(pstrName, _T("password")) == 0 ) { + if( _tcscmp(pstrValue, _T("true")) == 0 ) m_lTwhStyle |= ES_PASSWORD; + } + else if( _tcscmp(pstrName, _T("align")) == 0 ) { + if( _tcsstr(pstrValue, _T("left")) != NULL ) { + m_lTwhStyle &= ~(ES_CENTER | ES_RIGHT); + m_lTwhStyle |= ES_LEFT; + } + if( _tcsstr(pstrValue, _T("center")) != NULL ) { + m_lTwhStyle &= ~(ES_LEFT | ES_RIGHT); + m_lTwhStyle |= ES_CENTER; + } + if( _tcsstr(pstrValue, _T("right")) != NULL ) { + m_lTwhStyle &= ~(ES_LEFT | ES_CENTER); + m_lTwhStyle |= ES_RIGHT; + } + } + else if( _tcscmp(pstrName, _T("font")) == 0 ) SetFont(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("textcolor")) == 0 ) { + while( *pstrValue > _T('\0') && *pstrValue <= _T(' ') ) pstrValue = ::CharNext(pstrValue); + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetTextColor(clrColor); + } + else if( _tcscmp(pstrName, _T("maxchar")) == 0 ) SetLimitText(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("normalimage")) == 0 ) SetNormalImage(pstrValue); + else if( _tcscmp(pstrName, _T("hotimage")) == 0 ) SetHotImage(pstrValue); + else if( _tcscmp(pstrName, _T("focusedimage")) == 0 ) SetFocusedImage(pstrValue); + else if( _tcscmp(pstrName, _T("disabledimage")) == 0 ) SetDisabledImage(pstrValue); + else if( _tcscmp(pstrName, _T("textpadding")) == 0 ) { + RECT rcTextPadding = { 0 }; + LPTSTR pstr = NULL; + rcTextPadding.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + rcTextPadding.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcTextPadding.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcTextPadding.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetTextPadding(rcTextPadding); + } + else if( _tcscmp(pstrName, _T("tipvalue")) == 0 ) SetTipValue(pstrValue); + else if( _tcscmp(pstrName, _T("tipvaluecolor")) == 0 ) SetTipValueColor(pstrValue); + else CContainerUI::SetAttribute(pstrName, pstrValue); +} + +LRESULT CRichEditUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled) +{ + if( !IsVisible() || !IsEnabled() ) return 0; + if( !IsMouseEnabled() && uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST ) return 0; + if( uMsg == WM_MOUSEWHEEL && (LOWORD(wParam) & MK_CONTROL) == 0 ) return 0; + + if (uMsg == WM_IME_COMPOSITION) + { + // ΢뷨λ쳣 + HIMC hIMC = ImmGetContext(GetManager()->GetPaintWindow()); + if (hIMC) + { + // Set composition window position near caret position + POINT point; + GetCaretPos(&point); + + COMPOSITIONFORM Composition; + Composition.dwStyle = CFS_POINT; + Composition.ptCurrentPos.x = point.x; + Composition.ptCurrentPos.y = point.y; + ImmSetCompositionWindow(hIMC, &Composition); + + ImmReleaseContext(GetManager()->GetPaintWindow(),hIMC); + } + + return 0; + } + + bool bWasHandled = true; + if( (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) || uMsg == WM_SETCURSOR ) { + if( !m_pTwh->IsCaptured() ) { + switch (uMsg) { + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + { + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + CControlUI* pHover = GetManager()->FindControl(pt); + if(pHover != this) { + bWasHandled = false; + return 0; + } + } + break; + } + } + // Mouse message only go when captured or inside rect + DWORD dwHitResult = m_pTwh->IsCaptured() ? HITRESULT_HIT : HITRESULT_OUTSIDE; + if( dwHitResult == HITRESULT_OUTSIDE ) { + RECT rc; + m_pTwh->GetControlRect(&rc); + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + if( uMsg == WM_MOUSEWHEEL ) ::ScreenToClient(GetManager()->GetPaintWindow(), &pt); + if( ::PtInRect(&rc, pt) && !GetManager()->IsCaptured() ) dwHitResult = HITRESULT_HIT; + } + if( dwHitResult != HITRESULT_HIT ) return 0; + if( uMsg == WM_SETCURSOR ) bWasHandled = false; + else if( uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK || uMsg == WM_RBUTTONDOWN ) { + SetFocus(); + } + } +#ifdef _UNICODE + else if( uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST ) { +#else + else if( (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST) || uMsg == WM_CHAR || uMsg == WM_IME_CHAR ) { +#endif + if( !IsFocused() ) return 0; + } + else if( uMsg == WM_CONTEXTMENU ) { + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + ::ScreenToClient(GetManager()->GetPaintWindow(), &pt); + CControlUI* pHover = GetManager()->FindControl(pt); + if(pHover != this) { + bWasHandled = false; + return 0; + } + } + else + { + switch( uMsg ) { + case WM_HELP: + bWasHandled = false; + break; + default: + return 0; + } + } + + if(WM_CHAR == uMsg) + { +#ifndef _UNICODE + // check if we are waiting for 2 consecutive WM_CHAR messages + if ( IsAccumulateDBCMode() ) + { + if ( (GetKeyState(VK_KANA) & 0x1) ) + { + // turn off accumulate mode + SetAccumulateDBCMode ( false ); + m_chLeadByte = 0; + } + else + { + if ( !m_chLeadByte ) + { + // This is the first WM_CHAR message, + // accumulate it if this is a LeadByte. Otherwise, fall thru to + // regular WM_CHAR processing. + if ( IsDBCSLeadByte ( (WORD)wParam ) ) + { + // save the Lead Byte and don't process this message + m_chLeadByte = (WORD)wParam << 8 ; + + //TCHAR a = (WORD)wParam << 8 ; + return 0; + } + } + else + { + // This is the second WM_CHAR message, + // combine the current byte with previous byte. + // This DBC will be handled as WM_IME_CHAR. + wParam |= m_chLeadByte; + uMsg = WM_IME_CHAR; + + // setup to accumulate more WM_CHAR + m_chLeadByte = 0; + } + } + } +#endif + } + + LRESULT lResult = 0; + HRESULT Hr = TxSendMessage(uMsg, wParam, lParam, &lResult); + if( Hr == S_OK ) bHandled = bWasHandled; + else if( (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST) || uMsg == WM_CHAR || uMsg == WM_IME_CHAR ) + bHandled = bWasHandled; + else if( uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST ) { + if( m_pTwh->IsCaptured() ) bHandled = bWasHandled; + } + return lResult; +} + +void CRichEditUI::SetAccumulateDBCMode( bool bDBCMode ) +{ + m_fAccumulateDBC = bDBCMode; +} + +bool CRichEditUI::IsAccumulateDBCMode() +{ + return m_fAccumulateDBC; +} + + + +} // namespace DuiLib diff --git a/DuiLib/Control/UIRichEdit.h b/DuiLib/Control/UIRichEdit.h new file mode 100644 index 00000000..5ac653bf --- /dev/null +++ b/DuiLib/Control/UIRichEdit.h @@ -0,0 +1,179 @@ +#ifndef __UIRICHEDIT_H__ +#define __UIRICHEDIT_H__ + +#pragma once +#include +#pragma comment(lib,"imm32.lib") + +namespace DuiLib { + +class CTxtWinHost; + +class UILIB_API CRichEditUI : public CContainerUI, public IMessageFilterUI +{ +public: + CRichEditUI(); + ~CRichEditUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + UINT GetControlFlags() const; + + bool IsWantTab(); + void SetWantTab(bool bWantTab = true); + bool IsWantReturn(); + void SetWantReturn(bool bWantReturn = true); + bool IsWantCtrlReturn(); + void SetWantCtrlReturn(bool bWantCtrlReturn = true); + bool IsRich(); + void SetRich(bool bRich = true); + bool IsReadOnly(); + void SetReadOnly(bool bReadOnly = true); + bool GetWordWrap(); + void SetWordWrap(bool bWordWrap = true); + int GetFont(); + void SetFont(int index); + void SetFont(LPCTSTR pStrFontName, int nSize, bool bBold, bool bUnderline, bool bItalic); + void SetEnabled(bool bEnabled); + LONG GetWinStyle(); + void SetWinStyle(LONG lStyle); + DWORD GetTextColor(); + void SetTextColor(DWORD dwTextColor); + int GetLimitText(); + void SetLimitText(int iChars); + long GetTextLength(DWORD dwFlags = GTL_DEFAULT) const; + CDuiString GetText() const; + void SetText(LPCTSTR pstrText); + bool GetModify() const; + void SetModify(bool bModified = true) const; + void GetSel(CHARRANGE &cr) const; + void GetSel(long& nStartChar, long& nEndChar) const; + int SetSel(CHARRANGE &cr); + int SetSel(long nStartChar, long nEndChar); + void ReplaceSel(LPCTSTR lpszNewText, bool bCanUndo); + void ReplaceSelW(LPCWSTR lpszNewText, bool bCanUndo = false); + CDuiString GetSelText() const; + int SetSelAll(); + int SetSelNone(); + WORD GetSelectionType() const; + bool GetZoom(int& nNum, int& nDen) const; + bool SetZoom(int nNum, int nDen); + bool SetZoomOff(); + bool GetAutoURLDetect() const; + bool SetAutoURLDetect(bool bAutoDetect = true); + DWORD GetEventMask() const; + DWORD SetEventMask(DWORD dwEventMask); + CDuiString GetTextRange(long nStartChar, long nEndChar) const; + void HideSelection(bool bHide = true, bool bChangeStyle = false); + void ScrollCaret(); + int InsertText(long nInsertAfterChar, LPCTSTR lpstrText, bool bCanUndo = false); + int AppendText(LPCTSTR lpstrText, bool bCanUndo = false); + DWORD GetDefaultCharFormat(CHARFORMAT2 &cf) const; + bool SetDefaultCharFormat(CHARFORMAT2 &cf); + DWORD GetSelectionCharFormat(CHARFORMAT2 &cf) const; + bool SetSelectionCharFormat(CHARFORMAT2 &cf); + bool SetWordCharFormat(CHARFORMAT2 &cf); + DWORD GetParaFormat(PARAFORMAT2 &pf) const; + bool SetParaFormat(PARAFORMAT2 &pf); + bool Redo(); + bool Undo(); + void Clear(); + void Copy(); + void Cut(); + void Paste(); + int GetLineCount() const; + CDuiString GetLine(int nIndex, int nMaxLength) const; + int LineIndex(int nLine = -1) const; + int LineLength(int nLine = -1) const; + bool LineScroll(int nLines, int nChars = 0); + CDuiPoint GetCharPos(long lChar) const; + long LineFromChar(long nIndex) const; + CDuiPoint PosFromChar(UINT nChar) const; + int CharFromPos(CDuiPoint pt) const; + void EmptyUndoBuffer(); + UINT SetUndoLimit(UINT nLimit); + long StreamIn(int nFormat, EDITSTREAM &es); + long StreamOut(int nFormat, EDITSTREAM &es); + void SetAccumulateDBCMode(bool bDBCMode); + bool IsAccumulateDBCMode(); + + void DoInit(); + // ע⣺TxSendMessageSendMessageģTxSendMessageûmultibyteunicodeԶתĹܣ + // richedit2.0ڲunicodeʵֵģmultibyteУԼunicodemultibyteת + bool SetDropAcceptFile(bool bAccept); + virtual HRESULT TxSendMessage(UINT msg, WPARAM wparam, LPARAM lparam, LRESULT *plresult) const; + IDropTarget* GetTxDropTarget(); + virtual bool OnTxViewChanged(); + virtual void OnTxNotify(DWORD iNotify, void *pv); + + void SetScrollPos(SIZE szPos); + void LineUp(); + void LineDown(); + void PageUp(); + void PageDown(); + void HomeUp(); + void EndDown(); + void LineLeft(); + void LineRight(); + void PageLeft(); + void PageRight(); + void HomeLeft(); + void EndRight(); + + SIZE EstimateSize(SIZE szAvailable); + void SetPos(RECT rc); + void DoEvent(TEventUI& event); + void DoPaint(HDC hDC, const RECT& rcPaint); + + LPCTSTR GetNormalImage(); + void SetNormalImage(LPCTSTR pStrImage); + LPCTSTR GetHotImage(); + void SetHotImage(LPCTSTR pStrImage); + LPCTSTR GetFocusedImage(); + void SetFocusedImage(LPCTSTR pStrImage); + LPCTSTR GetDisabledImage(); + void SetDisabledImage(LPCTSTR pStrImage); + void PaintStatusImage(HDC hDC); + RECT GetTextPadding() const; + void SetTextPadding(RECT rc); + + void SetTipValue(LPCTSTR pStrTipValue); + LPCTSTR GetTipValue(); + void SetTipValueColor(LPCTSTR pStrColor); + DWORD GetTipValueColor(); + + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled); + +protected: + CTxtWinHost* m_pTwh; + bool m_bVScrollBarFixing; + bool m_bWantTab; + bool m_bWantReturn; + bool m_bWantCtrlReturn; + bool m_bRich; + bool m_bReadOnly; + bool m_bWordWrap; + DWORD m_dwTextColor; + int m_iFont; + int m_iLimitText; + LONG m_lTwhStyle; + bool m_bInited; + bool m_fAccumulateDBC ; // TRUE - need to cumulate ytes from 2 WM_CHAR msgs + // we are in this mode when we receive VK_PROCESSKEY + UINT m_chLeadByte; // use when we are in _fAccumulateDBC mode + + UINT m_uButtonState; + CDuiString m_sNormalImage; + CDuiString m_sHotImage; + CDuiString m_sFocusedImage; + CDuiString m_sDisabledImage; + RECT m_rcTextPadding; + CDuiString m_sTipValue; + DWORD m_dwTipValueColor; +}; + +} // namespace DuiLib + +#endif // __UIRICHEDIT_H__ diff --git a/DuiLib/Control/UIScrollBar.cpp b/DuiLib/Control/UIScrollBar.cpp new file mode 100644 index 00000000..912e36c2 --- /dev/null +++ b/DuiLib/Control/UIScrollBar.cpp @@ -0,0 +1,987 @@ +#include "stdafx.h" +#include "UIScrollBar.h" + +namespace DuiLib +{ + CScrollBarUI::CScrollBarUI() : m_bHorizontal(false), m_nRange(100), m_nScrollPos(0), m_nLineSize(8), + m_pOwner(NULL), m_nLastScrollPos(0), m_nLastScrollOffset(0), m_nScrollRepeatDelay(0), m_uButton1State(0), \ + m_uButton2State(0), m_uThumbState(0), m_bShowButton1(true), m_bShowButton2(true) + { + m_cxyFixed.cx = DEFAULT_SCROLLBAR_SIZE; + ptLastMouse.x = ptLastMouse.y = 0; + ::ZeroMemory(&m_rcThumb, sizeof(m_rcThumb)); + ::ZeroMemory(&m_rcButton1, sizeof(m_rcButton1)); + ::ZeroMemory(&m_rcButton2, sizeof(m_rcButton2)); + } + + LPCTSTR CScrollBarUI::GetClass() const + { + return _T("ScrollBarUI"); + } + + LPVOID CScrollBarUI::GetInterface(LPCTSTR pstrName) + { + if( _tcscmp(pstrName, DUI_CTR_SCROLLBAR) == 0 ) return static_cast(this); + return CControlUI::GetInterface(pstrName); + } + + CContainerUI* CScrollBarUI::GetOwner() const + { + return m_pOwner; + } + + void CScrollBarUI::SetOwner(CContainerUI* pOwner) + { + m_pOwner = pOwner; + } + + void CScrollBarUI::SetVisible(bool bVisible) + { + if( m_bVisible == bVisible ) return; + + bool v = IsVisible(); + m_bVisible = bVisible; + if( m_bFocused ) m_bFocused = false; + + } + + void CScrollBarUI::SetEnabled(bool bEnable) + { + CControlUI::SetEnabled(bEnable); + if( !IsEnabled() ) { + m_uButton1State = 0; + m_uButton2State = 0; + m_uThumbState = 0; + } + } + + void CScrollBarUI::SetFocus() + { + if( m_pOwner != NULL ) m_pOwner->SetFocus(); + else CControlUI::SetFocus(); + } + + bool CScrollBarUI::IsHorizontal() + { + return m_bHorizontal; + } + + void CScrollBarUI::SetHorizontal(bool bHorizontal) + { + if( m_bHorizontal == bHorizontal ) return; + + m_bHorizontal = bHorizontal; + if( m_bHorizontal ) { + if( m_cxyFixed.cy == 0 ) { + m_cxyFixed.cx = 0; + m_cxyFixed.cy = DEFAULT_SCROLLBAR_SIZE; + } + } + else { + if( m_cxyFixed.cx == 0 ) { + m_cxyFixed.cx = DEFAULT_SCROLLBAR_SIZE; + m_cxyFixed.cy = 0; + } + } + + if( m_pOwner != NULL ) m_pOwner->NeedUpdate(); else NeedParentUpdate(); + } + + int CScrollBarUI::GetScrollRange() const + { + return m_nRange; + } + + void CScrollBarUI::SetScrollRange(int nRange) + { + if( m_nRange == nRange ) return; + + m_nRange = nRange; + if( m_nRange < 0 ) m_nRange = 0; + if( m_nScrollPos > m_nRange ) m_nScrollPos = m_nRange; + SetPos(m_rcItem); + } + + int CScrollBarUI::GetScrollPos() const + { + return m_nScrollPos; + } + + void CScrollBarUI::SetScrollPos(int nPos) + { + if( m_nScrollPos == nPos ) return; + + m_nScrollPos = nPos; + if( m_nScrollPos < 0 ) m_nScrollPos = 0; + if( m_nScrollPos > m_nRange ) m_nScrollPos = m_nRange; + SetPos(m_rcItem); + } + + int CScrollBarUI::GetLineSize() const + { + return m_nLineSize; + } + + void CScrollBarUI::SetLineSize(int nSize) + { + m_nLineSize = nSize; + } + + bool CScrollBarUI::GetShowButton1() + { + return m_bShowButton1; + } + + void CScrollBarUI::SetShowButton1(bool bShow) + { + m_bShowButton1 = bShow; + SetPos(m_rcItem); + } + + LPCTSTR CScrollBarUI::GetButton1NormalImage() + { + return m_sButton1NormalImage; + } + + void CScrollBarUI::SetButton1NormalImage(LPCTSTR pStrImage) + { + m_sButton1NormalImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetButton1HotImage() + { + return m_sButton1HotImage; + } + + void CScrollBarUI::SetButton1HotImage(LPCTSTR pStrImage) + { + m_sButton1HotImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetButton1PushedImage() + { + return m_sButton1PushedImage; + } + + void CScrollBarUI::SetButton1PushedImage(LPCTSTR pStrImage) + { + m_sButton1PushedImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetButton1DisabledImage() + { + return m_sButton1DisabledImage; + } + + void CScrollBarUI::SetButton1DisabledImage(LPCTSTR pStrImage) + { + m_sButton1DisabledImage = pStrImage; + Invalidate(); + } + + bool CScrollBarUI::GetShowButton2() + { + return m_bShowButton2; + } + + void CScrollBarUI::SetShowButton2(bool bShow) + { + m_bShowButton2 = bShow; + SetPos(m_rcItem); + } + + LPCTSTR CScrollBarUI::GetButton2NormalImage() + { + return m_sButton2NormalImage; + } + + void CScrollBarUI::SetButton2NormalImage(LPCTSTR pStrImage) + { + m_sButton2NormalImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetButton2HotImage() + { + return m_sButton2HotImage; + } + + void CScrollBarUI::SetButton2HotImage(LPCTSTR pStrImage) + { + m_sButton2HotImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetButton2PushedImage() + { + return m_sButton2PushedImage; + } + + void CScrollBarUI::SetButton2PushedImage(LPCTSTR pStrImage) + { + m_sButton2PushedImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetButton2DisabledImage() + { + return m_sButton2DisabledImage; + } + + void CScrollBarUI::SetButton2DisabledImage(LPCTSTR pStrImage) + { + m_sButton2DisabledImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetThumbNormalImage() + { + return m_sThumbNormalImage; + } + + void CScrollBarUI::SetThumbNormalImage(LPCTSTR pStrImage) + { + m_sThumbNormalImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetThumbHotImage() + { + return m_sThumbHotImage; + } + + void CScrollBarUI::SetThumbHotImage(LPCTSTR pStrImage) + { + m_sThumbHotImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetThumbPushedImage() + { + return m_sThumbPushedImage; + } + + void CScrollBarUI::SetThumbPushedImage(LPCTSTR pStrImage) + { + m_sThumbPushedImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetThumbDisabledImage() + { + return m_sThumbDisabledImage; + } + + void CScrollBarUI::SetThumbDisabledImage(LPCTSTR pStrImage) + { + m_sThumbDisabledImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetRailNormalImage() + { + return m_sRailNormalImage; + } + + void CScrollBarUI::SetRailNormalImage(LPCTSTR pStrImage) + { + m_sRailNormalImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetRailHotImage() + { + return m_sRailHotImage; + } + + void CScrollBarUI::SetRailHotImage(LPCTSTR pStrImage) + { + m_sRailHotImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetRailPushedImage() + { + return m_sRailPushedImage; + } + + void CScrollBarUI::SetRailPushedImage(LPCTSTR pStrImage) + { + m_sRailPushedImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetRailDisabledImage() + { + return m_sRailDisabledImage; + } + + void CScrollBarUI::SetRailDisabledImage(LPCTSTR pStrImage) + { + m_sRailDisabledImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetBkNormalImage() + { + return m_sBkNormalImage; + } + + void CScrollBarUI::SetBkNormalImage(LPCTSTR pStrImage) + { + m_sBkNormalImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetBkHotImage() + { + return m_sBkHotImage; + } + + void CScrollBarUI::SetBkHotImage(LPCTSTR pStrImage) + { + m_sBkHotImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetBkPushedImage() + { + return m_sBkPushedImage; + } + + void CScrollBarUI::SetBkPushedImage(LPCTSTR pStrImage) + { + m_sBkPushedImage = pStrImage; + Invalidate(); + } + + LPCTSTR CScrollBarUI::GetBkDisabledImage() + { + return m_sBkDisabledImage; + } + + void CScrollBarUI::SetBkDisabledImage(LPCTSTR pStrImage) + { + m_sBkDisabledImage = pStrImage; + Invalidate(); + } + + void CScrollBarUI::SetPos(RECT rc) + { + CControlUI::SetPos(rc); + rc = m_rcItem; + + if( m_bHorizontal ) { + int cx = rc.right - rc.left; + if( m_bShowButton1 ) cx -= m_cxyFixed.cy; + if( m_bShowButton2 ) cx -= m_cxyFixed.cy; + if( cx > m_cxyFixed.cy ) { + m_rcButton1.left = rc.left; + m_rcButton1.top = rc.top; + if( m_bShowButton1 ) { + m_rcButton1.right = rc.left + m_cxyFixed.cy; + m_rcButton1.bottom = rc.top + m_cxyFixed.cy; + } + else { + m_rcButton1.right = m_rcButton1.left; + m_rcButton1.bottom = m_rcButton1.top; + } + + m_rcButton2.top = rc.top; + m_rcButton2.right = rc.right; + if( m_bShowButton2 ) { + m_rcButton2.left = rc.right - m_cxyFixed.cy; + m_rcButton2.bottom = rc.top + m_cxyFixed.cy; + } + else { + m_rcButton2.left = m_rcButton2.right; + m_rcButton2.bottom = m_rcButton2.top; + } + + m_rcThumb.top = rc.top; + m_rcThumb.bottom = rc.top + m_cxyFixed.cy; + if( m_nRange > 0 ) { + int cxThumb = cx * (rc.right - rc.left) / (m_nRange + rc.right - rc.left); + if( cxThumb < m_cxyFixed.cy ) cxThumb = m_cxyFixed.cy; + + m_rcThumb.left = m_nScrollPos * (cx - cxThumb) / m_nRange + m_rcButton1.right; + m_rcThumb.right = m_rcThumb.left + cxThumb; + if( m_rcThumb.right > m_rcButton2.left ) { + m_rcThumb.left = m_rcButton2.left - cxThumb; + m_rcThumb.right = m_rcButton2.left; + } + } + else { + m_rcThumb.left = m_rcButton1.right; + m_rcThumb.right = m_rcButton2.left; + } + } + else { + int cxButton = (rc.right - rc.left) / 2; + if( cxButton > m_cxyFixed.cy ) cxButton = m_cxyFixed.cy; + m_rcButton1.left = rc.left; + m_rcButton1.top = rc.top; + if( m_bShowButton1 ) { + m_rcButton1.right = rc.left + cxButton; + m_rcButton1.bottom = rc.top + m_cxyFixed.cy; + } + else { + m_rcButton1.right = m_rcButton1.left; + m_rcButton1.bottom = m_rcButton1.top; + } + + m_rcButton2.top = rc.top; + m_rcButton2.right = rc.right; + if( m_bShowButton2 ) { + m_rcButton2.left = rc.right - cxButton; + m_rcButton2.bottom = rc.top + m_cxyFixed.cy; + } + else { + m_rcButton2.left = m_rcButton2.right; + m_rcButton2.bottom = m_rcButton2.top; + } + + ::ZeroMemory(&m_rcThumb, sizeof(m_rcThumb)); + } + } + else { + int cy = rc.bottom - rc.top; + if( m_bShowButton1 ) cy -= m_cxyFixed.cx; + if( m_bShowButton2 ) cy -= m_cxyFixed.cx; + if( cy > m_cxyFixed.cx ) { + m_rcButton1.left = rc.left; + m_rcButton1.top = rc.top; + if( m_bShowButton1 ) { + m_rcButton1.right = rc.left + m_cxyFixed.cx; + m_rcButton1.bottom = rc.top + m_cxyFixed.cx; + } + else { + m_rcButton1.right = m_rcButton1.left; + m_rcButton1.bottom = m_rcButton1.top; + } + + m_rcButton2.left = rc.left; + m_rcButton2.bottom = rc.bottom; + if( m_bShowButton2 ) { + m_rcButton2.top = rc.bottom - m_cxyFixed.cx; + m_rcButton2.right = rc.left + m_cxyFixed.cx; + } + else { + m_rcButton2.top = m_rcButton2.bottom; + m_rcButton2.right = m_rcButton2.left; + } + + m_rcThumb.left = rc.left; + m_rcThumb.right = rc.left + m_cxyFixed.cx; + if( m_nRange > 0 ) { + int cyThumb = cy * (rc.bottom - rc.top) / (m_nRange + rc.bottom - rc.top); + if( cyThumb < m_cxyFixed.cx ) cyThumb = m_cxyFixed.cx; + + m_rcThumb.top = m_nScrollPos * (cy - cyThumb) / m_nRange + m_rcButton1.bottom; + m_rcThumb.bottom = m_rcThumb.top + cyThumb; + if( m_rcThumb.bottom > m_rcButton2.top ) { + m_rcThumb.top = m_rcButton2.top - cyThumb; + m_rcThumb.bottom = m_rcButton2.top; + } + } + else { + m_rcThumb.top = m_rcButton1.bottom; + m_rcThumb.bottom = m_rcButton2.top; + } + } + else { + int cyButton = (rc.bottom - rc.top) / 2; + if( cyButton > m_cxyFixed.cx ) cyButton = m_cxyFixed.cx; + m_rcButton1.left = rc.left; + m_rcButton1.top = rc.top; + if( m_bShowButton1 ) { + m_rcButton1.right = rc.left + m_cxyFixed.cx; + m_rcButton1.bottom = rc.top + cyButton; + } + else { + m_rcButton1.right = m_rcButton1.left; + m_rcButton1.bottom = m_rcButton1.top; + } + + m_rcButton2.left = rc.left; + m_rcButton2.bottom = rc.bottom; + if( m_bShowButton2 ) { + m_rcButton2.top = rc.bottom - cyButton; + m_rcButton2.right = rc.left + m_cxyFixed.cx; + } + else { + m_rcButton2.top = m_rcButton2.bottom; + m_rcButton2.right = m_rcButton2.left; + } + + ::ZeroMemory(&m_rcThumb, sizeof(m_rcThumb)); + } + } + } + + void CScrollBarUI::DoEvent(TEventUI& event) + { + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pOwner != NULL ) m_pOwner->DoEvent(event); + else CControlUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_SETFOCUS ) + { + return; + } + if( event.Type == UIEVENT_KILLFOCUS ) + { + return; + } + if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK ) + { + if( !IsEnabled() ) return; + + m_nLastScrollOffset = 0; + m_nScrollRepeatDelay = 0; + m_pManager->SetTimer(this, DEFAULT_TIMERID, 50U); + + if( ::PtInRect(&m_rcButton1, event.ptMouse) ) { + m_uButton1State |= UISTATE_PUSHED; + if( !m_bHorizontal ) { + if( m_pOwner != NULL ) m_pOwner->LineUp(); + else SetScrollPos(m_nScrollPos - m_nLineSize); + } + else { + if( m_pOwner != NULL ) m_pOwner->LineLeft(); + else SetScrollPos(m_nScrollPos - m_nLineSize); + } + } + else if( ::PtInRect(&m_rcButton2, event.ptMouse) ) { + m_uButton2State |= UISTATE_PUSHED; + if( !m_bHorizontal ) { + if( m_pOwner != NULL ) m_pOwner->LineDown(); + else SetScrollPos(m_nScrollPos + m_nLineSize); + } + else { + if( m_pOwner != NULL ) m_pOwner->LineRight(); + else SetScrollPos(m_nScrollPos + m_nLineSize); + } + } + else if( ::PtInRect(&m_rcThumb, event.ptMouse) ) { + m_uThumbState |= UISTATE_CAPTURED | UISTATE_PUSHED; + ptLastMouse = event.ptMouse; + m_nLastScrollPos = m_nScrollPos; + } + else { + if( !m_bHorizontal ) { + if( event.ptMouse.y < m_rcThumb.top ) { + if( m_pOwner != NULL ) m_pOwner->PageUp(); + else SetScrollPos(m_nScrollPos + m_rcItem.top - m_rcItem.bottom); + } + else if ( event.ptMouse.y > m_rcThumb.bottom ){ + if( m_pOwner != NULL ) m_pOwner->PageDown(); + else SetScrollPos(m_nScrollPos - m_rcItem.top + m_rcItem.bottom); + } + } + else { + if( event.ptMouse.x < m_rcThumb.left ) { + if( m_pOwner != NULL ) m_pOwner->PageLeft(); + else SetScrollPos(m_nScrollPos + m_rcItem.left - m_rcItem.right); + } + else if ( event.ptMouse.x > m_rcThumb.right ){ + if( m_pOwner != NULL ) m_pOwner->PageRight(); + else SetScrollPos(m_nScrollPos - m_rcItem.left + m_rcItem.right); + } + } + } + if( m_pManager != NULL && m_pOwner == NULL ) m_pManager->SendNotify(this, DUI_MSGTYPE_SCROLL); + return; + } + if( event.Type == UIEVENT_BUTTONUP ) + { + m_nScrollRepeatDelay = 0; + m_nLastScrollOffset = 0; + m_pManager->KillTimer(this, DEFAULT_TIMERID); + + if( (m_uThumbState & UISTATE_CAPTURED) != 0 ) { + m_uThumbState &= ~( UISTATE_CAPTURED | UISTATE_PUSHED ); + Invalidate(); + } + else if( (m_uButton1State & UISTATE_PUSHED) != 0 ) { + m_uButton1State &= ~UISTATE_PUSHED; + Invalidate(); + } + else if( (m_uButton2State & UISTATE_PUSHED) != 0 ) { + m_uButton2State &= ~UISTATE_PUSHED; + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_MOUSEMOVE ) + { + if( (m_uThumbState & UISTATE_CAPTURED) != 0 ) { + if( !m_bHorizontal ) { + + int vRange = m_rcItem.bottom - m_rcItem.top - m_rcThumb.bottom + m_rcThumb.top - 2 * m_cxyFixed.cx; + + if (vRange != 0) + m_nLastScrollOffset = (event.ptMouse.y - ptLastMouse.y) * m_nRange / vRange; + + } + else { + + int hRange = m_rcItem.right - m_rcItem.left - m_rcThumb.right + m_rcThumb.left - 2 * m_cxyFixed.cy; + + if (hRange != 0) + m_nLastScrollOffset = (event.ptMouse.x - ptLastMouse.x) * m_nRange / hRange; + } + } + else { + if( (m_uThumbState & UISTATE_HOT) != 0 ) { + if( !::PtInRect(&m_rcThumb, event.ptMouse) ) { + m_uThumbState &= ~UISTATE_HOT; + Invalidate(); + } + } + else { + if( !IsEnabled() ) return; + if( ::PtInRect(&m_rcThumb, event.ptMouse) ) { + m_uThumbState |= UISTATE_HOT; + Invalidate(); + } + } + } + return; + } + if( event.Type == UIEVENT_CONTEXTMENU ) + { + return; + } + if( event.Type == UIEVENT_TIMER && event.wParam == DEFAULT_TIMERID ) + { + ++m_nScrollRepeatDelay; + if( (m_uThumbState & UISTATE_CAPTURED) != 0 ) { + if( !m_bHorizontal ) { + if( m_pOwner != NULL ) m_pOwner->SetScrollPos(CDuiSize(m_pOwner->GetScrollPos().cx, \ + m_nLastScrollPos + m_nLastScrollOffset)); + else SetScrollPos(m_nLastScrollPos + m_nLastScrollOffset); + } + else { + if( m_pOwner != NULL ) m_pOwner->SetScrollPos(CDuiSize(m_nLastScrollPos + m_nLastScrollOffset, \ + m_pOwner->GetScrollPos().cy)); + else SetScrollPos(m_nLastScrollPos + m_nLastScrollOffset); + } + Invalidate(); + } + else if( (m_uButton1State & UISTATE_PUSHED) != 0 ) { + if( m_nScrollRepeatDelay <= 5 ) return; + if( !m_bHorizontal ) { + if( m_pOwner != NULL ) m_pOwner->LineUp(); + else SetScrollPos(m_nScrollPos - m_nLineSize); + } + else { + if( m_pOwner != NULL ) m_pOwner->LineLeft(); + else SetScrollPos(m_nScrollPos - m_nLineSize); + } + } + else if( (m_uButton2State & UISTATE_PUSHED) != 0 ) { + if( m_nScrollRepeatDelay <= 5 ) return; + if( !m_bHorizontal ) { + if( m_pOwner != NULL ) m_pOwner->LineDown(); + else SetScrollPos(m_nScrollPos + m_nLineSize); + } + else { + if( m_pOwner != NULL ) m_pOwner->LineRight(); + else SetScrollPos(m_nScrollPos + m_nLineSize); + } + } + else { + if( m_nScrollRepeatDelay <= 5 ) return; + POINT pt = { 0 }; + ::GetCursorPos(&pt); + ::ScreenToClient(m_pManager->GetPaintWindow(), &pt); + if( !m_bHorizontal ) { + if( pt.y < m_rcThumb.top ) { + if( m_pOwner != NULL ) m_pOwner->PageUp(); + else SetScrollPos(m_nScrollPos + m_rcItem.top - m_rcItem.bottom); + } + else if ( pt.y > m_rcThumb.bottom ){ + if( m_pOwner != NULL ) m_pOwner->PageDown(); + else SetScrollPos(m_nScrollPos - m_rcItem.top + m_rcItem.bottom); + } + } + else { + if( pt.x < m_rcThumb.left ) { + if( m_pOwner != NULL ) m_pOwner->PageLeft(); + else SetScrollPos(m_nScrollPos + m_rcItem.left - m_rcItem.right); + } + else if ( pt.x > m_rcThumb.right ){ + if( m_pOwner != NULL ) m_pOwner->PageRight(); + else SetScrollPos(m_nScrollPos - m_rcItem.left + m_rcItem.right); + } + } + } + if( m_pManager != NULL && m_pOwner == NULL ) m_pManager->SendNotify(this, DUI_MSGTYPE_SCROLL); + return; + } + if( event.Type == UIEVENT_MOUSEENTER ) + { + if( IsEnabled() ) { + m_uButton1State |= UISTATE_HOT; + m_uButton2State |= UISTATE_HOT; + if( ::PtInRect(&m_rcThumb, event.ptMouse) ) m_uThumbState |= UISTATE_HOT; + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_MOUSELEAVE ) + { + if( IsEnabled() ) { + m_uButton1State &= ~UISTATE_HOT; + m_uButton2State &= ~UISTATE_HOT; + m_uThumbState &= ~UISTATE_HOT; + Invalidate(); + } + return; + } + + if( m_pOwner != NULL ) m_pOwner->DoEvent(event); else CControlUI::DoEvent(event); + } + + void CScrollBarUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if( _tcscmp(pstrName, _T("button1normalimage")) == 0 ) SetButton1NormalImage(pstrValue); + else if( _tcscmp(pstrName, _T("button1hotimage")) == 0 ) SetButton1HotImage(pstrValue); + else if( _tcscmp(pstrName, _T("button1pushedimage")) == 0 ) SetButton1PushedImage(pstrValue); + else if( _tcscmp(pstrName, _T("button1disabledimage")) == 0 ) SetButton1DisabledImage(pstrValue); + else if( _tcscmp(pstrName, _T("button2normalimage")) == 0 ) SetButton2NormalImage(pstrValue); + else if( _tcscmp(pstrName, _T("button2hotimage")) == 0 ) SetButton2HotImage(pstrValue); + else if( _tcscmp(pstrName, _T("button2pushedimage")) == 0 ) SetButton2PushedImage(pstrValue); + else if( _tcscmp(pstrName, _T("button2disabledimage")) == 0 ) SetButton2DisabledImage(pstrValue); + else if( _tcscmp(pstrName, _T("thumbnormalimage")) == 0 ) SetThumbNormalImage(pstrValue); + else if( _tcscmp(pstrName, _T("thumbhotimage")) == 0 ) SetThumbHotImage(pstrValue); + else if( _tcscmp(pstrName, _T("thumbpushedimage")) == 0 ) SetThumbPushedImage(pstrValue); + else if( _tcscmp(pstrName, _T("thumbdisabledimage")) == 0 ) SetThumbDisabledImage(pstrValue); + else if( _tcscmp(pstrName, _T("railnormalimage")) == 0 ) SetRailNormalImage(pstrValue); + else if( _tcscmp(pstrName, _T("railhotimage")) == 0 ) SetRailHotImage(pstrValue); + else if( _tcscmp(pstrName, _T("railpushedimage")) == 0 ) SetRailPushedImage(pstrValue); + else if( _tcscmp(pstrName, _T("raildisabledimage")) == 0 ) SetRailDisabledImage(pstrValue); + else if( _tcscmp(pstrName, _T("bknormalimage")) == 0 ) SetBkNormalImage(pstrValue); + else if( _tcscmp(pstrName, _T("bkhotimage")) == 0 ) SetBkHotImage(pstrValue); + else if( _tcscmp(pstrName, _T("bkpushedimage")) == 0 ) SetBkPushedImage(pstrValue); + else if( _tcscmp(pstrName, _T("bkdisabledimage")) == 0 ) SetBkDisabledImage(pstrValue); + else if( _tcscmp(pstrName, _T("hor")) == 0 ) SetHorizontal(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("linesize")) == 0 ) SetLineSize(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("range")) == 0 ) SetScrollRange(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("value")) == 0 ) SetScrollPos(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("showbutton1")) == 0 ) SetShowButton1(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("showbutton2")) == 0 ) SetShowButton2(_tcscmp(pstrValue, _T("true")) == 0); + else CControlUI::SetAttribute(pstrName, pstrValue); + } + + void CScrollBarUI::DoPaint(HDC hDC, const RECT& rcPaint) + { + if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return; + PaintBk(hDC); + PaintButton1(hDC); + PaintButton2(hDC); + PaintThumb(hDC); + PaintRail(hDC); + } + + void CScrollBarUI::PaintBk(HDC hDC) + { + if( !IsEnabled() ) m_uThumbState |= UISTATE_DISABLED; + else m_uThumbState &= ~ UISTATE_DISABLED; + + if( (m_uThumbState & UISTATE_DISABLED) != 0 ) { + if( !m_sBkDisabledImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sBkDisabledImage) ) m_sBkDisabledImage.Empty(); + else return; + } + } + else if( (m_uThumbState & UISTATE_PUSHED) != 0 ) { + if( !m_sBkPushedImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sBkPushedImage) ) m_sBkPushedImage.Empty(); + else return; + } + } + else if( (m_uThumbState & UISTATE_HOT) != 0 ) { + if( !m_sBkHotImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sBkHotImage) ) m_sBkHotImage.Empty(); + else return; + } + } + + if( !m_sBkNormalImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sBkNormalImage) ) m_sBkNormalImage.Empty(); + else return; + } + } + + void CScrollBarUI::PaintButton1(HDC hDC) + { + if( !m_bShowButton1 ) return; + + if( !IsEnabled() ) m_uButton1State |= UISTATE_DISABLED; + else m_uButton1State &= ~ UISTATE_DISABLED; + + m_sImageModify.Empty(); + m_sImageModify.SmallFormat(_T("dest='%d,%d,%d,%d'"), m_rcButton1.left - m_rcItem.left, \ + m_rcButton1.top - m_rcItem.top, m_rcButton1.right - m_rcItem.left, m_rcButton1.bottom - m_rcItem.top); + + if( (m_uButton1State & UISTATE_DISABLED) != 0 ) { + if( !m_sButton1DisabledImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sButton1DisabledImage, (LPCTSTR)m_sImageModify) ) m_sButton1DisabledImage.Empty(); + else return; + } + } + else if( (m_uButton1State & UISTATE_PUSHED) != 0 ) { + if( !m_sButton1PushedImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sButton1PushedImage, (LPCTSTR)m_sImageModify) ) m_sButton1PushedImage.Empty(); + else return; + } + } + else if( (m_uButton1State & UISTATE_HOT) != 0 ) { + if( !m_sButton1HotImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sButton1HotImage, (LPCTSTR)m_sImageModify) ) m_sButton1HotImage.Empty(); + else return; + } + } + + if( !m_sButton1NormalImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sButton1NormalImage, (LPCTSTR)m_sImageModify) ) m_sButton1NormalImage.Empty(); + else return; + } + + DWORD dwBorderColor = 0xFF85E4FF; + int nBorderSize = 2; + CRenderEngine::DrawRect(hDC, m_rcButton1, nBorderSize, dwBorderColor); + } + + void CScrollBarUI::PaintButton2(HDC hDC) + { + if( !m_bShowButton2 ) return; + + if( !IsEnabled() ) m_uButton2State |= UISTATE_DISABLED; + else m_uButton2State &= ~ UISTATE_DISABLED; + + m_sImageModify.Empty(); + m_sImageModify.SmallFormat(_T("dest='%d,%d,%d,%d'"), m_rcButton2.left - m_rcItem.left, \ + m_rcButton2.top - m_rcItem.top, m_rcButton2.right - m_rcItem.left, m_rcButton2.bottom - m_rcItem.top); + + if( (m_uButton2State & UISTATE_DISABLED) != 0 ) { + if( !m_sButton2DisabledImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sButton2DisabledImage, (LPCTSTR)m_sImageModify) ) m_sButton2DisabledImage.Empty(); + else return; + } + } + else if( (m_uButton2State & UISTATE_PUSHED) != 0 ) { + if( !m_sButton2PushedImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sButton2PushedImage, (LPCTSTR)m_sImageModify) ) m_sButton2PushedImage.Empty(); + else return; + } + } + else if( (m_uButton2State & UISTATE_HOT) != 0 ) { + if( !m_sButton2HotImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sButton2HotImage, (LPCTSTR)m_sImageModify) ) m_sButton2HotImage.Empty(); + else return; + } + } + + if( !m_sButton2NormalImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sButton2NormalImage, (LPCTSTR)m_sImageModify) ) m_sButton2NormalImage.Empty(); + else return; + } + + DWORD dwBorderColor = 0xFF85E4FF; + int nBorderSize = 2; + CRenderEngine::DrawRect(hDC, m_rcButton2, nBorderSize, dwBorderColor); + } + + void CScrollBarUI::PaintThumb(HDC hDC) + { + if( m_rcThumb.left == 0 && m_rcThumb.top == 0 && m_rcThumb.right == 0 && m_rcThumb.bottom == 0 ) return; + if( !IsEnabled() ) m_uThumbState |= UISTATE_DISABLED; + else m_uThumbState &= ~ UISTATE_DISABLED; + + m_sImageModify.Empty(); + m_sImageModify.SmallFormat(_T("dest='%d,%d,%d,%d'"), m_rcThumb.left - m_rcItem.left, \ + m_rcThumb.top - m_rcItem.top, m_rcThumb.right - m_rcItem.left, m_rcThumb.bottom - m_rcItem.top); + + if( (m_uThumbState & UISTATE_DISABLED) != 0 ) { + if( !m_sThumbDisabledImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sThumbDisabledImage, (LPCTSTR)m_sImageModify) ) m_sThumbDisabledImage.Empty(); + else return; + } + } + else if( (m_uThumbState & UISTATE_PUSHED) != 0 ) { + if( !m_sThumbPushedImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sThumbPushedImage, (LPCTSTR)m_sImageModify) ) m_sThumbPushedImage.Empty(); + else return; + } + } + else if( (m_uThumbState & UISTATE_HOT) != 0 ) { + if( !m_sThumbHotImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sThumbHotImage, (LPCTSTR)m_sImageModify) ) m_sThumbHotImage.Empty(); + else return; + } + } + + if( !m_sThumbNormalImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sThumbNormalImage, (LPCTSTR)m_sImageModify) ) m_sThumbNormalImage.Empty(); + else return; + } + + DWORD dwBorderColor = 0xFF85E4FF; + int nBorderSize = 2; + CRenderEngine::DrawRect(hDC, m_rcThumb, nBorderSize, dwBorderColor); + } + + void CScrollBarUI::PaintRail(HDC hDC) + { + if( m_rcThumb.left == 0 && m_rcThumb.top == 0 && m_rcThumb.right == 0 && m_rcThumb.bottom == 0 ) return; + if( !IsEnabled() ) m_uThumbState |= UISTATE_DISABLED; + else m_uThumbState &= ~ UISTATE_DISABLED; + + m_sImageModify.Empty(); + if( !m_bHorizontal ) { + m_sImageModify.SmallFormat(_T("dest='%d,%d,%d,%d'"), m_rcThumb.left - m_rcItem.left, \ + (m_rcThumb.top + m_rcThumb.bottom) / 2 - m_rcItem.top - m_cxyFixed.cx / 2, \ + m_rcThumb.right - m_rcItem.left, \ + (m_rcThumb.top + m_rcThumb.bottom) / 2 - m_rcItem.top + m_cxyFixed.cx - m_cxyFixed.cx / 2); + } + else { + m_sImageModify.SmallFormat(_T("dest='%d,%d,%d,%d'"), \ + (m_rcThumb.left + m_rcThumb.right) / 2 - m_rcItem.left - m_cxyFixed.cy / 2, \ + m_rcThumb.top - m_rcItem.top, \ + (m_rcThumb.left + m_rcThumb.right) / 2 - m_rcItem.left + m_cxyFixed.cy - m_cxyFixed.cy / 2, \ + m_rcThumb.bottom - m_rcItem.top); + } + + if( (m_uThumbState & UISTATE_DISABLED) != 0 ) { + if( !m_sRailDisabledImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sRailDisabledImage, (LPCTSTR)m_sImageModify) ) m_sRailDisabledImage.Empty(); + else return; + } + } + else if( (m_uThumbState & UISTATE_PUSHED) != 0 ) { + if( !m_sRailPushedImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sRailPushedImage, (LPCTSTR)m_sImageModify) ) m_sRailPushedImage.Empty(); + else return; + } + } + else if( (m_uThumbState & UISTATE_HOT) != 0 ) { + if( !m_sRailHotImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sRailHotImage, (LPCTSTR)m_sImageModify) ) m_sRailHotImage.Empty(); + else return; + } + } + + if( !m_sRailNormalImage.IsEmpty() ) { + if( !DrawImage(hDC, (LPCTSTR)m_sRailNormalImage, (LPCTSTR)m_sImageModify) ) m_sRailNormalImage.Empty(); + else return; + } + } +} diff --git a/DuiLib/Control/UIScrollBar.h b/DuiLib/Control/UIScrollBar.h new file mode 100644 index 00000000..8d8ae500 --- /dev/null +++ b/DuiLib/Control/UIScrollBar.h @@ -0,0 +1,148 @@ +#ifndef __UISCROLLBAR_H__ +#define __UISCROLLBAR_H__ + +#pragma once + +namespace DuiLib +{ + class UILIB_API CScrollBarUI : public CControlUI + { + public: + CScrollBarUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + + CContainerUI* GetOwner() const; + void SetOwner(CContainerUI* pOwner); + + void SetVisible(bool bVisible = true); + void SetEnabled(bool bEnable = true); + void SetFocus(); + + bool IsHorizontal(); + void SetHorizontal(bool bHorizontal = true); + int GetScrollRange() const; + void SetScrollRange(int nRange); + int GetScrollPos() const; + void SetScrollPos(int nPos); + int GetLineSize() const; + void SetLineSize(int nSize); + + bool GetShowButton1(); + void SetShowButton1(bool bShow); + LPCTSTR GetButton1NormalImage(); + void SetButton1NormalImage(LPCTSTR pStrImage); + LPCTSTR GetButton1HotImage(); + void SetButton1HotImage(LPCTSTR pStrImage); + LPCTSTR GetButton1PushedImage(); + void SetButton1PushedImage(LPCTSTR pStrImage); + LPCTSTR GetButton1DisabledImage(); + void SetButton1DisabledImage(LPCTSTR pStrImage); + + bool GetShowButton2(); + void SetShowButton2(bool bShow); + LPCTSTR GetButton2NormalImage(); + void SetButton2NormalImage(LPCTSTR pStrImage); + LPCTSTR GetButton2HotImage(); + void SetButton2HotImage(LPCTSTR pStrImage); + LPCTSTR GetButton2PushedImage(); + void SetButton2PushedImage(LPCTSTR pStrImage); + LPCTSTR GetButton2DisabledImage(); + void SetButton2DisabledImage(LPCTSTR pStrImage); + + LPCTSTR GetThumbNormalImage(); + void SetThumbNormalImage(LPCTSTR pStrImage); + LPCTSTR GetThumbHotImage(); + void SetThumbHotImage(LPCTSTR pStrImage); + LPCTSTR GetThumbPushedImage(); + void SetThumbPushedImage(LPCTSTR pStrImage); + LPCTSTR GetThumbDisabledImage(); + void SetThumbDisabledImage(LPCTSTR pStrImage); + + LPCTSTR GetRailNormalImage(); + void SetRailNormalImage(LPCTSTR pStrImage); + LPCTSTR GetRailHotImage(); + void SetRailHotImage(LPCTSTR pStrImage); + LPCTSTR GetRailPushedImage(); + void SetRailPushedImage(LPCTSTR pStrImage); + LPCTSTR GetRailDisabledImage(); + void SetRailDisabledImage(LPCTSTR pStrImage); + + LPCTSTR GetBkNormalImage(); + void SetBkNormalImage(LPCTSTR pStrImage); + LPCTSTR GetBkHotImage(); + void SetBkHotImage(LPCTSTR pStrImage); + LPCTSTR GetBkPushedImage(); + void SetBkPushedImage(LPCTSTR pStrImage); + LPCTSTR GetBkDisabledImage(); + void SetBkDisabledImage(LPCTSTR pStrImage); + + void SetPos(RECT rc); + void DoEvent(TEventUI& event); + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + void DoPaint(HDC hDC, const RECT& rcPaint); + + void PaintBk(HDC hDC); + void PaintButton1(HDC hDC); + void PaintButton2(HDC hDC); + void PaintThumb(HDC hDC); + void PaintRail(HDC hDC); + + protected: + + enum + { + DEFAULT_SCROLLBAR_SIZE = 16, + DEFAULT_TIMERID = 10, + }; + + bool m_bHorizontal; + int m_nRange; + int m_nScrollPos; + int m_nLineSize; + CContainerUI* m_pOwner; + POINT ptLastMouse; + int m_nLastScrollPos; + int m_nLastScrollOffset; + int m_nScrollRepeatDelay; + + CDuiString m_sBkNormalImage; + CDuiString m_sBkHotImage; + CDuiString m_sBkPushedImage; + CDuiString m_sBkDisabledImage; + + bool m_bShowButton1; + RECT m_rcButton1; + UINT m_uButton1State; + CDuiString m_sButton1NormalImage; + CDuiString m_sButton1HotImage; + CDuiString m_sButton1PushedImage; + CDuiString m_sButton1DisabledImage; + + bool m_bShowButton2; + RECT m_rcButton2; + UINT m_uButton2State; + CDuiString m_sButton2NormalImage; + CDuiString m_sButton2HotImage; + CDuiString m_sButton2PushedImage; + CDuiString m_sButton2DisabledImage; + + RECT m_rcThumb; + UINT m_uThumbState; + CDuiString m_sThumbNormalImage; + CDuiString m_sThumbHotImage; + CDuiString m_sThumbPushedImage; + CDuiString m_sThumbDisabledImage; + + CDuiString m_sRailNormalImage; + CDuiString m_sRailHotImage; + CDuiString m_sRailPushedImage; + CDuiString m_sRailDisabledImage; + + CDuiString m_sImageModify; + }; +} + +#endif // __UISCROLLBAR_H__ \ No newline at end of file diff --git a/DuiLib/Control/UISlider.cpp b/DuiLib/Control/UISlider.cpp new file mode 100644 index 00000000..38f7fdea --- /dev/null +++ b/DuiLib/Control/UISlider.cpp @@ -0,0 +1,295 @@ +#include "StdAfx.h" +#include "UISlider.h" + +namespace DuiLib +{ + CSliderUI::CSliderUI() : m_uButtonState(0), m_nStep(1),m_bSendMove(false) + { + m_uTextStyle = DT_SINGLELINE | DT_CENTER; + m_szThumb.cx = m_szThumb.cy = 10; + } + + LPCTSTR CSliderUI::GetClass() const + { + return _T("SliderUI"); + } + + UINT CSliderUI::GetControlFlags() const + { + if( IsEnabled() ) return UIFLAG_SETCURSOR; + else return 0; + } + + LPVOID CSliderUI::GetInterface(LPCTSTR pstrName) + { + if( _tcscmp(pstrName, DUI_CTR_SLIDER) == 0 ) return static_cast(this); + return CProgressUI::GetInterface(pstrName); + } + + void CSliderUI::SetEnabled(bool bEnable) + { + CControlUI::SetEnabled(bEnable); + if( !IsEnabled() ) { + m_uButtonState = 0; + } + } + + int CSliderUI::GetChangeStep() + { + return m_nStep; + } + + void CSliderUI::SetChangeStep(int step) + { + m_nStep = step; + } + + void CSliderUI::SetThumbSize(SIZE szXY) + { + m_szThumb = szXY; + } + + RECT CSliderUI::GetThumbRect() const + { + if( m_bHorizontal ) { + int left = m_rcItem.left + (m_rcItem.right - m_rcItem.left - m_szThumb.cx) * (m_nValue - m_nMin) / (m_nMax - m_nMin); + int top = (m_rcItem.bottom + m_rcItem.top - m_szThumb.cy) / 2; + return CDuiRect(left, top, left + m_szThumb.cx, top + m_szThumb.cy); + } + else { + int left = (m_rcItem.right + m_rcItem.left - m_szThumb.cx) / 2; + int top = m_rcItem.bottom - m_szThumb.cy - (m_rcItem.bottom - m_rcItem.top - m_szThumb.cy) * (m_nValue - m_nMin) / (m_nMax - m_nMin); + return CDuiRect(left, top, left + m_szThumb.cx, top + m_szThumb.cy); + } + } + + LPCTSTR CSliderUI::GetThumbImage() const + { + return m_sThumbImage; + } + + void CSliderUI::SetThumbImage(LPCTSTR pStrImage) + { + m_sThumbImage = pStrImage; + Invalidate(); + } + + LPCTSTR CSliderUI::GetThumbHotImage() const + { + return m_sThumbHotImage; + } + + void CSliderUI::SetThumbHotImage(LPCTSTR pStrImage) + { + m_sThumbHotImage = pStrImage; + Invalidate(); + } + + LPCTSTR CSliderUI::GetThumbPushedImage() const + { + return m_sThumbPushedImage; + } + + void CSliderUI::SetThumbPushedImage(LPCTSTR pStrImage) + { + m_sThumbPushedImage = pStrImage; + Invalidate(); + } + + void CSliderUI::SetValue(int nValue) + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) + return; + CProgressUI::SetValue(nValue); + } + void CSliderUI::DoEvent(TEventUI& event) + { + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pParent != NULL ) m_pParent->DoEvent(event); + else CProgressUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK ) + { + if( IsEnabled() ) { + m_uButtonState |= UISTATE_CAPTURED; + + int nValue; + + if( m_bHorizontal ) { + if( event.ptMouse.x >= m_rcItem.right - m_szThumb.cx / 2 ) nValue = m_nMax; + else if( event.ptMouse.x <= m_rcItem.left + m_szThumb.cx / 2 ) nValue = m_nMin; + else nValue = m_nMin + (m_nMax - m_nMin) * (event.ptMouse.x - m_rcItem.left - m_szThumb.cx / 2 ) / (m_rcItem.right - m_rcItem.left - m_szThumb.cx); + } + else { + if( event.ptMouse.y >= m_rcItem.bottom - m_szThumb.cy / 2 ) nValue = m_nMin; + else if( event.ptMouse.y <= m_rcItem.top + m_szThumb.cy / 2 ) nValue = m_nMax; + else nValue = m_nMin + (m_nMax - m_nMin) * (m_rcItem.bottom - event.ptMouse.y - m_szThumb.cy / 2 ) / (m_rcItem.bottom - m_rcItem.top - m_szThumb.cy); + } + if(m_nValue !=nValue && nValue>=m_nMin && nValue<=m_nMax) + { + m_nValue =nValue; + Invalidate(); + } + } + return; + } + + if( event.Type == UIEVENT_BUTTONUP ) + { + int nValue; + if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { + m_uButtonState &= ~UISTATE_CAPTURED; + } + if( m_bHorizontal ) { + if( event.ptMouse.x >= m_rcItem.right - m_szThumb.cx / 2 ) nValue = m_nMax; + else if( event.ptMouse.x <= m_rcItem.left + m_szThumb.cx / 2 ) nValue = m_nMin; + else nValue = m_nMin + (m_nMax - m_nMin) * (event.ptMouse.x - m_rcItem.left - m_szThumb.cx / 2 ) / (m_rcItem.right - m_rcItem.left - m_szThumb.cx); + } + else { + if( event.ptMouse.y >= m_rcItem.bottom - m_szThumb.cy / 2 ) nValue = m_nMin; + else if( event.ptMouse.y <= m_rcItem.top + m_szThumb.cy / 2 ) nValue = m_nMax; + else nValue = m_nMin + (m_nMax - m_nMin) * (m_rcItem.bottom - event.ptMouse.y - m_szThumb.cy / 2 ) / (m_rcItem.bottom - m_rcItem.top - m_szThumb.cy); + } + if(nValue>=m_nMin && nValue<=m_nMax) + { + m_nValue =nValue; + m_pManager->SendNotify(this, DUI_MSGTYPE_VALUECHANGED); + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_CONTEXTMENU ) + { + return; + } + if( event.Type == UIEVENT_SCROLLWHEEL ) + { + switch( LOWORD(event.wParam) ) { + case SB_LINEUP: + SetValue(GetValue() + GetChangeStep()); + m_pManager->SendNotify(this, DUI_MSGTYPE_VALUECHANGED); + return; + case SB_LINEDOWN: + SetValue(GetValue() - GetChangeStep()); + m_pManager->SendNotify(this, DUI_MSGTYPE_VALUECHANGED); + return; + } + } + if( event.Type == UIEVENT_MOUSEMOVE ) + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { + if( m_bHorizontal ) { + if( event.ptMouse.x >= m_rcItem.right - m_szThumb.cx / 2 ) m_nValue = m_nMax; + else if( event.ptMouse.x <= m_rcItem.left + m_szThumb.cx / 2 ) m_nValue = m_nMin; + else m_nValue = m_nMin + (m_nMax - m_nMin) * (event.ptMouse.x - m_rcItem.left - m_szThumb.cx / 2 ) / (m_rcItem.right - m_rcItem.left - m_szThumb.cx); + } + else { + if( event.ptMouse.y >= m_rcItem.bottom - m_szThumb.cy / 2 ) m_nValue = m_nMin; + else if( event.ptMouse.y <= m_rcItem.top + m_szThumb.cy / 2 ) m_nValue = m_nMax; + else m_nValue = m_nMin + (m_nMax - m_nMin) * (m_rcItem.bottom - event.ptMouse.y - m_szThumb.cy / 2 ) / (m_rcItem.bottom - m_rcItem.top - m_szThumb.cy); + } + if (m_bSendMove) + m_pManager->SendNotify(this, DUI_MSGTYPE_VALUECHANGED_MOVE); + Invalidate(); + } + + // Generate the appropriate mouse messages + POINT pt = event.ptMouse; + RECT rcThumb = GetThumbRect(); + if( IsEnabled() && ::PtInRect(&rcThumb, event.ptMouse) ) { + + m_uButtonState |= UISTATE_HOT; + Invalidate(); + }else + { + m_uButtonState &= ~UISTATE_HOT; + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_SETCURSOR ) + { + RECT rcThumb = GetThumbRect(); + if( IsEnabled()) { + ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND))); + return; + } + } + + if( event.Type == UIEVENT_MOUSELEAVE ) + { + if( IsEnabled() ) { + m_uButtonState &= ~UISTATE_HOT; + Invalidate(); + } + return; + } + CControlUI::DoEvent(event); + } + + void CSliderUI::SetCanSendMove(bool bCanSend) + { + m_bSendMove = bCanSend; + } + bool CSliderUI::GetCanSendMove() const + { + return m_bSendMove; + } + + void CSliderUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if( _tcscmp(pstrName, _T("thumbimage")) == 0 ) SetThumbImage(pstrValue); + else if( _tcscmp(pstrName, _T("thumbhotimage")) == 0 ) SetThumbHotImage(pstrValue); + else if( _tcscmp(pstrName, _T("thumbpushedimage")) == 0 ) SetThumbPushedImage(pstrValue); + else if( _tcscmp(pstrName, _T("thumbsize")) == 0 ) { + SIZE szXY = {0}; + LPTSTR pstr = NULL; + szXY.cx = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + szXY.cy = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetThumbSize(szXY); + } + else if( _tcscmp(pstrName, _T("step")) == 0 ) { + SetChangeStep(_ttoi(pstrValue)); + } + else if( _tcscmp(pstrName, _T("sendmove")) == 0 ) { + SetCanSendMove(_tcscmp(pstrValue, _T("true")) == 0); + } + else CProgressUI::SetAttribute(pstrName, pstrValue); + } + + void CSliderUI::PaintStatusImage(HDC hDC) + { + CProgressUI::PaintStatusImage(hDC); + + RECT rcThumb = GetThumbRect(); + rcThumb.left -= m_rcItem.left; + rcThumb.top -= m_rcItem.top; + rcThumb.right -= m_rcItem.left; + rcThumb.bottom -= m_rcItem.top; + if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { + if( !m_sThumbPushedImage.IsEmpty() ) { + m_sImageModify.Empty(); + m_sImageModify.SmallFormat(_T("dest='%d,%d,%d,%d'"), rcThumb.left, rcThumb.top, rcThumb.right, rcThumb.bottom); + if( !DrawImage(hDC, (LPCTSTR)m_sThumbPushedImage, (LPCTSTR)m_sImageModify) ) m_sThumbPushedImage.Empty(); + else return; + } + } + else if( (m_uButtonState & UISTATE_HOT) != 0 ) { + if( !m_sThumbHotImage.IsEmpty() ) { + m_sImageModify.Empty(); + m_sImageModify.SmallFormat(_T("dest='%d,%d,%d,%d'"), rcThumb.left, rcThumb.top, rcThumb.right, rcThumb.bottom); + if( !DrawImage(hDC, (LPCTSTR)m_sThumbHotImage, (LPCTSTR)m_sImageModify) ) m_sThumbHotImage.Empty(); + else return; + } + } + + if( !m_sThumbImage.IsEmpty() ) { + m_sImageModify.Empty(); + m_sImageModify.SmallFormat(_T("dest='%d,%d,%d,%d'"), rcThumb.left, rcThumb.top, rcThumb.right, rcThumb.bottom); + if( !DrawImage(hDC, (LPCTSTR)m_sThumbImage, (LPCTSTR)m_sImageModify) ) m_sThumbImage.Empty(); + else return; + } + } +} diff --git a/DuiLib/Control/UISlider.h b/DuiLib/Control/UISlider.h new file mode 100644 index 00000000..79c2db74 --- /dev/null +++ b/DuiLib/Control/UISlider.h @@ -0,0 +1,51 @@ +#ifndef __UISLIDER_H__ +#define __UISLIDER_H__ + +#pragma once + +namespace DuiLib +{ + class UILIB_API CSliderUI : public CProgressUI + { + public: + CSliderUI(); + + LPCTSTR GetClass() const; + UINT GetControlFlags() const; + LPVOID GetInterface(LPCTSTR pstrName); + + void SetEnabled(bool bEnable = true); + + int GetChangeStep(); + void SetChangeStep(int step); + void SetThumbSize(SIZE szXY); + RECT GetThumbRect() const; + LPCTSTR GetThumbImage() const; + void SetThumbImage(LPCTSTR pStrImage); + LPCTSTR GetThumbHotImage() const; + void SetThumbHotImage(LPCTSTR pStrImage); + LPCTSTR GetThumbPushedImage() const; + void SetThumbPushedImage(LPCTSTR pStrImage); + + void DoEvent(TEventUI& event); + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + void PaintStatusImage(HDC hDC); + + void SetValue(int nValue); + void SetCanSendMove(bool bCanSend); + bool GetCanSendMove() const; + protected: + SIZE m_szThumb; + UINT m_uButtonState; + int m_nStep; + + CDuiString m_sThumbImage; + CDuiString m_sThumbHotImage; + CDuiString m_sThumbPushedImage; + + CDuiString m_sImageModify; + bool m_bSendMove; + }; +} + +#endif // __UISLIDER_H__ \ No newline at end of file diff --git a/DuiLib/Control/UIText.cpp b/DuiLib/Control/UIText.cpp new file mode 100644 index 00000000..4316f173 --- /dev/null +++ b/DuiLib/Control/UIText.cpp @@ -0,0 +1,166 @@ +#include "StdAfx.h" +#include "UIText.h" + +namespace DuiLib +{ + CTextUI::CTextUI() : m_nLinks(0), m_nHoverLink(-1) + { + m_uTextStyle = DT_WORDBREAK; + m_rcTextPadding.left = 2; + m_rcTextPadding.right = 2; + ::ZeroMemory(m_rcLinks, sizeof(m_rcLinks)); + } + + CTextUI::~CTextUI() + { + } + + LPCTSTR CTextUI::GetClass() const + { + return _T("TextUI"); + } + + LPVOID CTextUI::GetInterface(LPCTSTR pstrName) + { + if( _tcscmp(pstrName, DUI_CTR_TEXT) == 0 ) return static_cast(this); + return CLabelUI::GetInterface(pstrName); + } + + UINT CTextUI::GetControlFlags() const + { + if( IsEnabled() && m_nLinks > 0 ) return UIFLAG_SETCURSOR; + else return 0; + } + + CDuiString* CTextUI::GetLinkContent(int iIndex) + { + if( iIndex >= 0 && iIndex < m_nLinks ) return &m_sLinks[iIndex]; + return NULL; + } + + void CTextUI::DoEvent(TEventUI& event) + { + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pParent != NULL ) m_pParent->DoEvent(event); + else CLabelUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_SETCURSOR ) { + for( int i = 0; i < m_nLinks; i++ ) { + if( ::PtInRect(&m_rcLinks[i], event.ptMouse) ) { + ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND))); + return; + } + } + } + if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK && IsEnabled() ) { + for( int i = 0; i < m_nLinks; i++ ) { + if( ::PtInRect(&m_rcLinks[i], event.ptMouse) ) { + Invalidate(); + return; + } + } + } + if( event.Type == UIEVENT_BUTTONUP && IsEnabled() ) { + for( int i = 0; i < m_nLinks; i++ ) { + if( ::PtInRect(&m_rcLinks[i], event.ptMouse) ) { + m_pManager->SendNotify(this, DUI_MSGTYPE_LINK, i); + return; + } + } + } + if( event.Type == UIEVENT_CONTEXTMENU ) + { + return; + } + // When you move over a link + if( m_nLinks > 0 && event.Type == UIEVENT_MOUSEMOVE && IsEnabled() ) { + int nHoverLink = -1; + for( int i = 0; i < m_nLinks; i++ ) { + if( ::PtInRect(&m_rcLinks[i], event.ptMouse) ) { + nHoverLink = i; + break; + } + } + + if(m_nHoverLink != nHoverLink) { + m_nHoverLink = nHoverLink; + Invalidate(); + return; + } + } + if( event.Type == UIEVENT_MOUSELEAVE ) { + if( m_nLinks > 0 && IsEnabled() ) { + if(m_nHoverLink != -1) { + m_nHoverLink = -1; + Invalidate(); + return; + } + } + } + + CLabelUI::DoEvent(event); + } + + SIZE CTextUI::EstimateSize(SIZE szAvailable) + { + + RECT rcText = { 0, 0, m_bAutoCalcWidth ? 9999 : m_cxyFixed.cx, 9999 }; + rcText.left += m_rcTextPadding.left; + rcText.right -= m_rcTextPadding.right; + + if( m_bShowHtml ) { + int nLinks = 0; + CRenderEngine::DrawHtmlText(m_pManager->GetPaintDC(), m_pManager, rcText, m_sText, m_dwTextColor, NULL, NULL, nLinks, DT_CALCRECT | m_uTextStyle); + } + else { + CRenderEngine::DrawText(m_pManager->GetPaintDC(), m_pManager, rcText, m_sText, m_dwTextColor, m_iFont, DT_CALCRECT | m_uTextStyle); + } + SIZE cXY = {rcText.right - rcText.left + m_rcTextPadding.left + m_rcTextPadding.right, + rcText.bottom - rcText.top + m_rcTextPadding.top + m_rcTextPadding.bottom}; + + if (m_bAutoCalcWidth) + { + m_cxyFixed.cx = cXY.cx; + } + + return CControlUI::EstimateSize(szAvailable); + } + + void CTextUI::PaintText(HDC hDC) + { + if( m_sText.IsEmpty() ) { + m_nLinks = 0; + return; + } + + if( m_dwTextColor == 0 ) m_dwTextColor = m_pManager->GetDefaultFontColor(); + if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor(); + + if( m_sText.IsEmpty() ) return; + + m_nLinks = lengthof(m_rcLinks); + RECT rc = m_rcItem; + rc.left += m_rcTextPadding.left; + rc.right -= m_rcTextPadding.right; + rc.top += m_rcTextPadding.top; + rc.bottom -= m_rcTextPadding.bottom; + if( IsEnabled() ) { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + m_rcLinks, m_sLinks, m_nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \ + m_iFont, m_uTextStyle); + } + else { + if( m_bShowHtml ) + CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + m_rcLinks, m_sLinks, m_nLinks, m_uTextStyle); + else + CRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \ + m_iFont, m_uTextStyle); + } + } +} diff --git a/DuiLib/Control/UIText.h b/DuiLib/Control/UIText.h new file mode 100644 index 00000000..d5c62cb2 --- /dev/null +++ b/DuiLib/Control/UIText.h @@ -0,0 +1,35 @@ +#ifndef __UITEXT_H__ +#define __UITEXT_H__ + +#pragma once + +namespace DuiLib +{ + class UILIB_API CTextUI : public CLabelUI + { + public: + CTextUI(); + ~CTextUI(); + + LPCTSTR GetClass() const; + UINT GetControlFlags() const; + LPVOID GetInterface(LPCTSTR pstrName); + + CDuiString* GetLinkContent(int iIndex); + + void DoEvent(TEventUI& event); + SIZE EstimateSize(SIZE szAvailable); + + void PaintText(HDC hDC); + + protected: + enum { MAX_LINK = 8 }; + int m_nLinks; + RECT m_rcLinks[MAX_LINK]; + CDuiString m_sLinks[MAX_LINK]; + int m_nHoverLink; + }; + +} // namespace DuiLib + +#endif //__UITEXT_H__ \ No newline at end of file diff --git a/DuiLib/Control/UITreeView.cpp b/DuiLib/Control/UITreeView.cpp new file mode 100644 index 00000000..63f3b019 --- /dev/null +++ b/DuiLib/Control/UITreeView.cpp @@ -0,0 +1,1024 @@ +#include "StdAfx.h" +#include "UITreeView.h" + +#pragma warning( disable: 4251 ) +namespace DuiLib +{ + CTreeNodeUI::CTreeNodeUI( CTreeNodeUI* _ParentNode /*= NULL*/ ) + { + m_dwItemTextColor = 0x00000000; + m_dwItemHotTextColor = 0; + m_dwSelItemTextColor = 0; + m_dwSelItemHotTextColor = 0; + + pTreeView = NULL; + m_bIsVisable = false; + m_bIsCheckBox = false; + pParentTreeNode = NULL; + + pHoriz = new CHorizontalLayoutUI(); + pFolderButton = new COptionUI(); + pDottedLine = new CLabelUI(); + pCheckBox = new COptionUI(); + pItemButton = new COptionUI(); + + this->SetFixedHeight(18); + pFolderButton->SetFixedWidth(GetFixedHeight()); + pDottedLine->SetFixedWidth(2); + pCheckBox->SetFixedWidth(GetFixedHeight()); + pItemButton->SetAttribute(_T("align"),_T("left")); + + pDottedLine->SetVisible(false); + pCheckBox->SetVisible(false); + pItemButton->SetMouseEnabled(false); + + if(_ParentNode) + { + if (_tcsicmp(_ParentNode->GetClass(), _T("TreeNodeUI")) != 0) + return; + + pDottedLine->SetVisible(_ParentNode->IsVisible()); + pDottedLine->SetFixedWidth(_ParentNode->GetDottedLine()->GetFixedWidth()+16); + this->SetParentNode(_ParentNode); + } + + pHoriz->Add(pDottedLine); + pHoriz->Add(pFolderButton); + pHoriz->Add(pCheckBox); + pHoriz->Add(pItemButton); + Add(pHoriz); + } + + CTreeNodeUI::~CTreeNodeUI( void ) + { + + } + + LPCTSTR CTreeNodeUI::GetClass() const + { + return _T("TreeNodeUI"); + } + + LPVOID CTreeNodeUI::GetInterface( LPCTSTR pstrName ) + { + if( _tcscmp(pstrName, _T("TreeNode")) == 0 ) + return static_cast(this); + return CListContainerElementUI::GetInterface(pstrName); + } + + void CTreeNodeUI::DoEvent( TEventUI& event ) + { + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pOwner != NULL ) m_pOwner->DoEvent(event); + else CContainerUI::DoEvent(event); + return; + } + + CListContainerElementUI::DoEvent(event); + + if( event.Type == UIEVENT_DBLCLICK ) + { + if( IsEnabled() ) { + m_pManager->SendNotify(this, _T("itemdbclick")); + Invalidate(); + } + return; + } + if( event.Type == UIEVENT_MOUSEENTER ) + { + if( IsEnabled()) { + if(m_bSelected && GetSelItemHotTextColor()) + pItemButton->SetTextColor(GetSelItemHotTextColor()); + else + pItemButton->SetTextColor(GetItemHotTextColor()); + } + else + pItemButton->SetTextColor(pItemButton->GetDisabledTextColor()); + + return; + } + if( event.Type == UIEVENT_MOUSELEAVE ) + { + if( IsEnabled()) { + if(m_bSelected && GetSelItemTextColor()) + pItemButton->SetTextColor(GetSelItemTextColor()); + else if(!m_bSelected) + pItemButton->SetTextColor(GetItemTextColor()); + } + else + pItemButton->SetTextColor(pItemButton->GetDisabledTextColor()); + + return; + } + } + + void CTreeNodeUI::Invalidate() + { + if( !IsVisible() ) + return; + + if( GetParent() ) { + CContainerUI* pParentContainer = static_cast(GetParent()->GetInterface(_T("Container"))); + if( pParentContainer ) { + RECT rc = pParentContainer->GetPos(); + RECT rcInset = pParentContainer->GetInset(); + rc.left += rcInset.left; + rc.top += rcInset.top; + rc.right -= rcInset.right; + rc.bottom -= rcInset.bottom; + CScrollBarUI* pVerticalScrollBar = pParentContainer->GetVerticalScrollBar(); + if( pVerticalScrollBar && pVerticalScrollBar->IsVisible() ) rc.right -= pVerticalScrollBar->GetFixedWidth(); + CScrollBarUI* pHorizontalScrollBar = pParentContainer->GetHorizontalScrollBar(); + if( pHorizontalScrollBar && pHorizontalScrollBar->IsVisible() ) rc.bottom -= pHorizontalScrollBar->GetFixedHeight(); + + RECT invalidateRc = m_rcItem; + if( !::IntersectRect(&invalidateRc, &m_rcItem, &rc) ) + return; + + CControlUI* pParent = GetParent(); + RECT rcTemp; + RECT rcParent; + while( pParent = pParent->GetParent() ) + { + rcTemp = invalidateRc; + rcParent = pParent->GetPos(); + if( !::IntersectRect(&invalidateRc, &rcTemp, &rcParent) ) + return; + } + + if( m_pManager != NULL ) m_pManager->Invalidate(invalidateRc); + } + else { + CContainerUI::Invalidate(); + } + } + else { + CContainerUI::Invalidate(); + } + } + + bool CTreeNodeUI::Select( bool bSelect /*= true*/ ) + { + bool nRet = CListContainerElementUI::Select(bSelect); + if(m_bSelected) + pItemButton->SetTextColor(GetSelItemTextColor()); + else + pItemButton->SetTextColor(GetItemTextColor()); + + return nRet; + } + + + void CTreeNodeUI::SetVisibleTag( bool _IsVisible ) + { + m_bIsVisable = _IsVisible; + } + + bool CTreeNodeUI::GetVisibleTag() + { + return m_bIsVisable; + } + + void CTreeNodeUI::SetItemText( LPCTSTR pstrValue ) + { + pItemButton->SetText(pstrValue); + } + + CDuiString CTreeNodeUI::GetItemText() + { + return pItemButton->GetText(); + } + + void CTreeNodeUI::CheckBoxSelected( bool _Selected ) + { + pCheckBox->Selected(_Selected); + } + + bool CTreeNodeUI::IsCheckBoxSelected() const + { + return pCheckBox->IsSelected(); + } + + bool CTreeNodeUI::IsHasChild() const + { + return !mTreeNodes.IsEmpty(); + } + + bool CTreeNodeUI::Add( CControlUI* _pTreeNodeUI ) + { + if (_tcsicmp(_pTreeNodeUI->GetClass(), _T("TreeNodeUI")) == 0) + return AddChildNode((CTreeNodeUI*)_pTreeNodeUI); + + return CListContainerElementUI::Add(_pTreeNodeUI); + } + + //************************************ + // : AddAt + // : bool + // Ϣ: CControlUI * pControl + // Ϣ: int iIndex òԵǰڵµֵбͼ + // ˵: TreeNodeָλòӽڵ(edit by joe 2014/7/28) + //************************************ + bool CTreeNodeUI::AddAt( CControlUI* pControl, int iIndex ) + { + if (!pControl) + return false; + + if(_tcsicmp(pControl->GetClass(), _T("TreeNodeUI")) != 0) + return false; + + //filter invalidate index + int iDestIndex = iIndex; + if (iDestIndex < 0) + { + iDestIndex = 0; + } + else if (iDestIndex > GetCountChild()) + { + iDestIndex = GetCountChild(); + } + if(iIndex != iDestIndex) iIndex = iDestIndex; + + CTreeNodeUI* pIndexNode = static_cast(mTreeNodes.GetAt(iIndex)); + + pControl = CalLocation((CTreeNodeUI*)pControl); + + bool bRet = false; + int iTreeIndex = -1; + if (pTreeView) + { + //Get TreeView insert index + if (pIndexNode) + { + iTreeIndex = pIndexNode->GetTreeIndex(); + bRet = pTreeView->AddAt((CTreeNodeUI*)pControl, iTreeIndex) >= 0; + if (bRet) + { + mTreeNodes.InsertAt(iIndex, pControl); + } + } + else + { + CTreeNodeUI *pChildNode = NULL; + //insert child node position index(new node insert to tail, default add tail) + int iChIndex = -1; + //insert child node tree-view position index(new node insert to tail) + int iChTreeIndex = -1; + //search tree index reverse + for (int i = GetCountChild(); i > 0; i++) + { + pChildNode = GetChildNode(i - 1); + iChTreeIndex = pChildNode->GetTreeIndex(); + if (iChTreeIndex >= GetTreeIndex() && iChTreeIndex <= GetTreeIndex() + GetCountChild() ) + { + //new child node position + iChIndex = i; + //child node tree position + iTreeIndex = iChTreeIndex + 1; + break; + } + } + //child not find tree index directly insert to parent tail + if (iTreeIndex <= GetTreeIndex()) + { + iTreeIndex = GetTreeIndex() + 1; + } + //insert TreeNode to TreeView + bRet = pTreeView->AddAt((CTreeNodeUI*)pControl, iTreeIndex) >= 0; + //insert TreeNode to parent TreeNode + if (bRet) + { + if (iChIndex > 0) + bRet = mTreeNodes.InsertAt(iChIndex, pControl); + else + bRet = mTreeNodes.Add(pControl); + } + } + } + else + { + //parent TreeNode not bind TreeView just insert to parent TreeNode + bRet = mTreeNodes.InsertAt(iIndex, pControl); + } + + if(bRet) + { + pControl->SetVisible(GetFolderButton()->IsSelected()); + } + + return bRet; + } + + + bool CTreeNodeUI::AddChildNode( CTreeNodeUI* _pTreeNodeUI ) + { + if (!_pTreeNodeUI) + return false; + + if (_tcsicmp(_pTreeNodeUI->GetClass(), _T("TreeNodeUI")) != 0) + return false; + + + _pTreeNodeUI = CalLocation(_pTreeNodeUI); + + bool nRet = true; + + if(pTreeView){ + CTreeNodeUI* pNode = static_cast(mTreeNodes.GetAt(mTreeNodes.GetSize()-1)); + if(!pNode || !pNode->GetLastNode()) + nRet = pTreeView->AddAt(_pTreeNodeUI,GetTreeIndex()+1) >= 0; + else nRet = pTreeView->AddAt(_pTreeNodeUI,pNode->GetLastNode()->GetTreeIndex()+1) >= 0; + } + + if(nRet) + { + _pTreeNodeUI->SetVisible(GetFolderButton()->IsSelected()); + mTreeNodes.Add(_pTreeNodeUI); + } + + return nRet; + } + + bool CTreeNodeUI::Remove( CControlUI* pControl ) + { + return RemoveAt((CTreeNodeUI*)pControl); + } + + bool CTreeNodeUI::RemoveAt( CTreeNodeUI* _pTreeNodeUI ) + { + int nIndex = mTreeNodes.Find(_pTreeNodeUI); + CTreeNodeUI* pNode = static_cast(mTreeNodes.GetAt(nIndex)); + if(pNode && pNode == _pTreeNodeUI) + { + while(pNode->IsHasChild()) + pNode->RemoveAt(static_cast(pNode->mTreeNodes.GetAt(0))); + + mTreeNodes.Remove(nIndex); + + if(pTreeView) + pTreeView->Remove(_pTreeNodeUI); + + return true; + } + return false; + } + + void CTreeNodeUI::SetParentNode( CTreeNodeUI* _pParentTreeNode ) + { + pParentTreeNode = _pParentTreeNode; + } + + CTreeNodeUI* CTreeNodeUI::GetParentNode() + { + return pParentTreeNode; + } + + long CTreeNodeUI::GetCountChild() + { + return mTreeNodes.GetSize(); + } + + void CTreeNodeUI::SetTreeView( CTreeViewUI* _CTreeViewUI ) + { + pTreeView = _CTreeViewUI; + } + + CTreeViewUI* CTreeNodeUI::GetTreeView() + { + return pTreeView; + } + + void CTreeNodeUI::SetAttribute( LPCTSTR pstrName, LPCTSTR pstrValue ) + { + if(_tcscmp(pstrName, _T("text")) == 0 ) + pItemButton->SetText(pstrValue); + else if(_tcscmp(pstrName, _T("horizattr")) == 0 ) + pHoriz->ApplyAttributeList(pstrValue); + else if(_tcscmp(pstrName, _T("dotlineattr")) == 0 ) + pDottedLine->ApplyAttributeList(pstrValue); + else if(_tcscmp(pstrName, _T("folderattr")) == 0 ) + pFolderButton->ApplyAttributeList(pstrValue); + else if(_tcscmp(pstrName, _T("checkboxattr")) == 0 ) + pCheckBox->ApplyAttributeList(pstrValue); + else if(_tcscmp(pstrName, _T("itemattr")) == 0 ) + pItemButton->ApplyAttributeList(pstrValue); + else if(_tcscmp(pstrName, _T("itemtextcolor")) == 0 ){ + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetItemTextColor(clrColor); + } + else if(_tcscmp(pstrName, _T("itemhottextcolor")) == 0 ){ + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetItemHotTextColor(clrColor); + } + else if(_tcscmp(pstrName, _T("selitemtextcolor")) == 0 ){ + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetSelItemTextColor(clrColor); + } + else if(_tcscmp(pstrName, _T("selitemhottextcolor")) == 0 ){ + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetSelItemHotTextColor(clrColor); + } + else if(_tcscmp(pstrName,_T("defaultexpand")) == 0) + GetFolderButton()->Selected(_tcscmp(pstrValue,_T("true")) == 0); + else CListContainerElementUI::SetAttribute(pstrName,pstrValue); + } + + void CTreeNodeUI::IsAllChildChecked() + { + bool bIsAllChildChecked = true; + bool bIsAllChildUncheck = true; + int nCount = GetCountChild(); + if(nCount > 0) + { + for(int nIndex = 0;nIndex < nCount;nIndex++) + { + CTreeNodeUI* pItem = GetChildNode(nIndex); + if(!pItem->GetCheckBox()->IsSelected()) + { + bIsAllChildChecked = false; + } + else + { + bIsAllChildUncheck = false; + } + } + if (bIsAllChildChecked && !GetCheckBox()->IsSelected()) + { + GetCheckBox()->Selected(true); + return; + } + else if (bIsAllChildUncheck && GetCheckBox()->IsSelected()) + { + GetCheckBox()->Selected(false); + return; + } + + } + + } + + CStdPtrArray CTreeNodeUI::GetTreeNodes() + { + return mTreeNodes; + } + + CTreeNodeUI* CTreeNodeUI::GetChildNode( int _nIndex ) + { + return static_cast(mTreeNodes.GetAt(_nIndex)); + } + + void CTreeNodeUI::SetVisibleFolderBtn( bool _IsVisibled ) + { + pFolderButton->SetVisible(_IsVisibled); + } + + bool CTreeNodeUI::GetVisibleFolderBtn() + { + return pFolderButton->IsVisible(); + } + + void CTreeNodeUI::SetVisibleCheckBtn( bool _IsVisibled ) + { + pCheckBox->SetVisible(_IsVisibled); + } + + bool CTreeNodeUI::GetVisibleCheckBtn() + { + return pCheckBox->IsVisible(); + } + + int CTreeNodeUI::GetTreeIndex() + { + if(!pTreeView) + return -1; + + for(int nIndex = 0;nIndex < pTreeView->GetCount();nIndex++){ + if(this == pTreeView->GetItemAt(nIndex)) + return nIndex; + } + + return -1; + } + + int CTreeNodeUI::GetNodeIndex() + { + if(!GetParentNode() && !pTreeView) + return -1; + + if(!GetParentNode() && pTreeView) + return GetTreeIndex(); + + return GetParentNode()->GetTreeNodes().Find(this); + } + + CTreeNodeUI* CTreeNodeUI::GetLastNode( ) + { + if(!IsHasChild()) + return this; + + CTreeNodeUI* nRetNode = NULL; + + for(int nIndex = 0;nIndex < GetTreeNodes().GetSize();nIndex++){ + CTreeNodeUI* pNode = static_cast(GetTreeNodes().GetAt(nIndex)); + if(!pNode) + continue; + + CDuiString aa = pNode->GetItemText(); + + if(pNode->IsHasChild()) + nRetNode = pNode->GetLastNode(); + else + nRetNode = pNode; + } + + return nRetNode; + } + + CTreeNodeUI* CTreeNodeUI::CalLocation( CTreeNodeUI* _pTreeNodeUI ) + { + _pTreeNodeUI->GetDottedLine()->SetVisible(true); + _pTreeNodeUI->GetDottedLine()->SetFixedWidth(pDottedLine->GetFixedWidth()+16); + _pTreeNodeUI->SetParentNode(this); + _pTreeNodeUI->GetItemButton()->SetGroup(pItemButton->GetGroup()); + _pTreeNodeUI->SetTreeView(pTreeView); + + return _pTreeNodeUI; + } + + void CTreeNodeUI::SetItemTextColor( DWORD _dwItemTextColor ) + { + m_dwItemTextColor = _dwItemTextColor; + pItemButton->SetTextColor(m_dwItemTextColor); + } + + DWORD CTreeNodeUI::GetItemTextColor() const + { + return m_dwItemTextColor; + } + + void CTreeNodeUI::SetItemHotTextColor( DWORD _dwItemHotTextColor ) + { + m_dwItemHotTextColor = _dwItemHotTextColor; + Invalidate(); + } + + DWORD CTreeNodeUI::GetItemHotTextColor() const + { + return m_dwItemHotTextColor; + } + + void CTreeNodeUI::SetSelItemTextColor( DWORD _dwSelItemTextColor ) + { + m_dwSelItemTextColor = _dwSelItemTextColor; + Invalidate(); + } + + DWORD CTreeNodeUI::GetSelItemTextColor() const + { + return m_dwSelItemTextColor; + } + + void CTreeNodeUI::SetSelItemHotTextColor( DWORD _dwSelHotItemTextColor ) + { + m_dwSelItemHotTextColor = _dwSelHotItemTextColor; + Invalidate(); + } + + DWORD CTreeNodeUI::GetSelItemHotTextColor() const + { + return m_dwSelItemHotTextColor; + } + + /*****************************************************************************/ + /*****************************************************************************/ + /*****************************************************************************/ + + CTreeViewUI::CTreeViewUI( void ) : m_bVisibleFolderBtn(true),m_bVisibleCheckBtn(false),m_uItemMinWidth(0) + { + this->GetHeader()->SetVisible(false); + } + + CTreeViewUI::~CTreeViewUI( void ) + { + + } + + LPCTSTR CTreeViewUI::GetClass() const + { + return _T("TreeViewUI"); + } + + LPVOID CTreeViewUI::GetInterface( LPCTSTR pstrName ) + { + if( _tcscmp(pstrName, _T("TreeView")) == 0 ) return static_cast(this); + return CListUI::GetInterface(pstrName); + } + + bool CTreeViewUI::Add( CTreeNodeUI* pControl ) + { + if (!pControl) + return false; + + if (_tcsicmp(pControl->GetClass(), _T("TreeNodeUI")) != 0) + return false; + + pControl->OnNotify += MakeDelegate(this,&CTreeViewUI::OnDBClickItem); + pControl->GetFolderButton()->OnNotify += MakeDelegate(this,&CTreeViewUI::OnFolderChanged); + pControl->GetCheckBox()->OnNotify += MakeDelegate(this,&CTreeViewUI::OnCheckBoxChanged); + + pControl->SetVisibleCheckBtn(m_bVisibleCheckBtn); + pControl->SetVisibleFolderBtn(false); + if(m_uItemMinWidth > 0) + pControl->SetMinWidth(m_uItemMinWidth); + + CListUI::Add(pControl); + + if(pControl->GetCountChild() > 0) + { + pControl->SetVisibleFolderBtn(m_bVisibleFolderBtn); + int nCount = pControl->GetCountChild(); + for(int nIndex = 0;nIndex < nCount;nIndex++) + { + CTreeNodeUI* pNode = pControl->GetChildNode(nIndex); + if(pNode) + Add(pNode); + } + } + + + pControl->SetTreeView(this); + + return true; + } + + //************************************ + // : AddAt + // : long + // Ϣ: CTreeNodeUI * pControl + // Ϣ: int iIndex + // ˵: ÷ὫĽڵλĽڵΪǸڵ㣬ʹAddAt(CTreeNodeUI* pControl,CTreeNodeUI* _IndexNode) + //************************************ + long CTreeViewUI::AddAt( CTreeNodeUI* pControl, int iIndex ) + { + if (!pControl) + return -1; + + if (_tcsicmp(pControl->GetClass(), _T("TreeNodeUI")) != 0) + return -1; + + //filter invalidate index + int iDestIndex = iIndex; + if (iDestIndex < 0) + { + iDestIndex = 0; + } + else if (iDestIndex > GetCount()) + { + iDestIndex = GetCount(); + } + if(iIndex != iDestIndex) iIndex = iDestIndex; + + //CTreeNodeUI* pParent = static_cast(GetItemAt(iIndex)); + //if(!pParent) + // return -1; + + pControl->OnNotify += MakeDelegate(this,&CTreeViewUI::OnDBClickItem); + pControl->GetFolderButton()->OnNotify += MakeDelegate(this,&CTreeViewUI::OnFolderChanged); + pControl->GetCheckBox()->OnNotify += MakeDelegate(this,&CTreeViewUI::OnCheckBoxChanged); + + pControl->SetVisibleCheckBtn(m_bVisibleCheckBtn); + pControl->SetVisibleFolderBtn(m_bVisibleFolderBtn); + if(m_uItemMinWidth > 0) + pControl->SetMinWidth(m_uItemMinWidth); + + CListUI::AddAt(pControl,iIndex); + + if(pControl->GetCountChild() > 0) + { + int nCount = pControl->GetCountChild(); + for(int nIndex = 0;nIndex < nCount;nIndex++) + { + CTreeNodeUI* pNode = pControl->GetChildNode(nIndex); + if(pNode) + return AddAt(pNode,iIndex+1); + } + } + else + { + return iIndex+1; + } + + return -1; + } + + + bool CTreeViewUI::AddAt( CTreeNodeUI* pControl,CTreeNodeUI* _IndexNode ) + { + if(!_IndexNode && !pControl) + return false; + + int nItemIndex = -1; + + for(int nIndex = 0;nIndex < GetCount();nIndex++){ + if(_IndexNode == GetItemAt(nIndex)){ + nItemIndex = nIndex; + break; + } + } + + if(nItemIndex == -1) + return false; + + return AddAt(pControl,nItemIndex) >= 0; + } + + //************************************ + // : Remove + // : bool + // Ϣ: CTreeNodeUI * pControl + // ˵: pControl Լµнڵ㽫һƳ + //************************************ + bool CTreeViewUI::Remove( CTreeNodeUI* pControl ) + { + if(pControl->GetCountChild() > 0) + { + int nCount = pControl->GetCountChild(); + for(int nIndex = 0;nIndex < nCount;nIndex++) + { + CTreeNodeUI* pNode = pControl->GetChildNode(nIndex); + if(pNode){ + pControl->Remove(pNode); + } + } + } + CListUI::Remove(pControl); + return true; + } + + //************************************ + // : RemoveAt + // : bool + // Ϣ: int iIndex + // ˵: iIndex Լµнڵ㽫һƳ + //************************************ + bool CTreeViewUI::RemoveAt( int iIndex ) + { + CTreeNodeUI* pItem = (CTreeNodeUI*)GetItemAt(iIndex); + if(pItem->GetCountChild()) + Remove(pItem); + return true; + } + + void CTreeViewUI::RemoveAll() + { + CListUI::RemoveAll(); + } + + void CTreeViewUI::Notify( TNotifyUI& msg ) + { + + } + + bool CTreeViewUI::OnCheckBoxChanged( void* param ) + { + TNotifyUI* pMsg = (TNotifyUI*)param; + if(pMsg->sType == _T("selectchanged")) + { + COptionUI* pCheckBox = (COptionUI*)pMsg->pSender; + CTreeNodeUI* pItem = (CTreeNodeUI*)pCheckBox->GetParent()->GetParent(); + SetItemCheckBox(pCheckBox->IsSelected(), pItem); + + if(pItem->GetParentNode() != NULL) + pItem->GetParentNode()->IsAllChildChecked(); + + return true; + } + return true; + } + + bool CTreeViewUI::OnFolderChanged( void* param ) + { + TNotifyUI* pMsg = (TNotifyUI*)param; + if(pMsg->sType == _T("selectchanged")) + { + COptionUI* pFolder = (COptionUI*)pMsg->pSender; + CTreeNodeUI* pItem = (CTreeNodeUI*)pFolder->GetParent()->GetParent(); + pItem->SetVisibleTag(pFolder->IsSelected()); + SetItemExpand(pFolder->IsSelected(), pItem); + return true; + } + return true; + } + + bool CTreeViewUI::OnDBClickItem( void* param ) + { + TNotifyUI* pMsg = (TNotifyUI*)param; + if(pMsg->sType == _T("itemdbclick")) + { + CTreeNodeUI* pItem = static_cast(pMsg->pSender); + COptionUI* pFolder = pItem->GetFolderButton(); + pFolder->Selected(!pFolder->IsSelected()); + return true; + } + return false; + } + + bool CTreeViewUI::SetItemCheckBox( bool _Selected,CTreeNodeUI* _TreeNode /*= NULL*/ ) + { + if(_TreeNode) + { + if(_TreeNode->GetCountChild() > 0) + { + int nCount = _TreeNode->GetCountChild(); + for(int nIndex = 0;nIndex < nCount;nIndex++) + { + CTreeNodeUI* pItem = _TreeNode->GetChildNode(nIndex); + pItem->GetCheckBox()->Selected(_Selected); + if(pItem->GetCountChild()) + SetItemCheckBox(_Selected,pItem); + } + } + return true; + } + else + { + int nIndex = 0; + int nCount = GetCount(); + while(nIndex < nCount) + { + CTreeNodeUI* pItem = (CTreeNodeUI*)GetItemAt(nIndex); + pItem->GetCheckBox()->Selected(_Selected); + if(pItem->GetCountChild()) + SetItemCheckBox(_Selected,pItem); + + nIndex++; + } + return true; + } + return false; + } + + void CTreeViewUI::SetItemExpand( bool _Expanded,CTreeNodeUI* _TreeNode /*= NULL*/ ) + { + if(_TreeNode) + { + if(_TreeNode->GetCountChild() > 0) + { + int nCount = _TreeNode->GetCountChild(); + for(int nIndex = 0;nIndex < nCount;nIndex++) + { + CTreeNodeUI* pItem = _TreeNode->GetChildNode(nIndex); + pItem->SetVisible(_Expanded); + + if(pItem->GetCountChild() && !pItem->GetFolderButton()->IsSelected()) + SetItemExpand(_Expanded,pItem); + } + } + } + else + { + int nIndex = 0; + int nCount = GetCount(); + while(nIndex < nCount) + { + CTreeNodeUI* pItem = (CTreeNodeUI*)GetItemAt(nIndex); + + pItem->SetVisible(_Expanded); + + if(pItem->GetCountChild() && !pItem->GetFolderButton()->IsSelected()) + SetItemExpand(_Expanded,pItem); + + nIndex++; + } + } + } + + void CTreeViewUI::SetVisibleFolderBtn( bool _IsVisibled ) + { + m_bVisibleFolderBtn = _IsVisibled; + int nCount = this->GetCount(); + for(int nIndex = 0;nIndex < nCount;nIndex++) + { + CTreeNodeUI* pItem = static_cast(this->GetItemAt(nIndex)); + pItem->GetFolderButton()->SetVisible(m_bVisibleFolderBtn); + } + } + + bool CTreeViewUI::GetVisibleFolderBtn() + { + return m_bVisibleFolderBtn; + } + + void CTreeViewUI::SetVisibleCheckBtn( bool _IsVisibled ) + { + m_bVisibleCheckBtn = _IsVisibled; + int nCount = this->GetCount(); + for(int nIndex = 0;nIndex < nCount;nIndex++) + { + CTreeNodeUI* pItem = static_cast(this->GetItemAt(nIndex)); + pItem->GetCheckBox()->SetVisible(m_bVisibleCheckBtn); + } + } + + bool CTreeViewUI::GetVisibleCheckBtn() + { + return m_bVisibleCheckBtn; + } + + void CTreeViewUI::SetItemMinWidth( UINT _ItemMinWidth ) + { + m_uItemMinWidth = _ItemMinWidth; + + for(int nIndex = 0;nIndex < GetCount();nIndex++){ + CTreeNodeUI* pTreeNode = static_cast(GetItemAt(nIndex)); + if(pTreeNode) + pTreeNode->SetMinWidth(GetItemMinWidth()); + } + Invalidate(); + } + + UINT CTreeViewUI::GetItemMinWidth() + { + return m_uItemMinWidth; + } + + void CTreeViewUI::SetItemTextColor( DWORD _dwItemTextColor ) + { + for(int nIndex = 0;nIndex < GetCount();nIndex++){ + CTreeNodeUI* pTreeNode = static_cast(GetItemAt(nIndex)); + if(pTreeNode) + pTreeNode->SetItemTextColor(_dwItemTextColor); + } + } + + void CTreeViewUI::SetItemHotTextColor( DWORD _dwItemHotTextColor ) + { + for(int nIndex = 0;nIndex < GetCount();nIndex++){ + CTreeNodeUI* pTreeNode = static_cast(GetItemAt(nIndex)); + if(pTreeNode) + pTreeNode->SetItemHotTextColor(_dwItemHotTextColor); + } + } + + void CTreeViewUI::SetSelItemTextColor( DWORD _dwSelItemTextColor ) + { + for(int nIndex = 0;nIndex < GetCount();nIndex++){ + CTreeNodeUI* pTreeNode = static_cast(GetItemAt(nIndex)); + if(pTreeNode) + pTreeNode->SetSelItemTextColor(_dwSelItemTextColor); + } + } + + void CTreeViewUI::SetSelItemHotTextColor( DWORD _dwSelHotItemTextColor ) + { + for(int nIndex = 0;nIndex < GetCount();nIndex++){ + CTreeNodeUI* pTreeNode = static_cast(GetItemAt(nIndex)); + if(pTreeNode) + pTreeNode->SetSelItemHotTextColor(_dwSelHotItemTextColor); + } + } + + void CTreeViewUI::SetAttribute( LPCTSTR pstrName, LPCTSTR pstrValue ) + { + if(_tcscmp(pstrName,_T("visiblefolderbtn")) == 0) + SetVisibleFolderBtn(_tcscmp(pstrValue,_T("true")) == 0); + else if(_tcscmp(pstrName,_T("visiblecheckbtn")) == 0) + SetVisibleCheckBtn(_tcscmp(pstrValue,_T("true")) == 0); + else if(_tcscmp(pstrName,_T("itemminwidth")) == 0) + SetItemMinWidth(_ttoi(pstrValue)); + else if(_tcscmp(pstrName, _T("itemtextcolor")) == 0 ){ + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetItemTextColor(clrColor); + } + else if(_tcscmp(pstrName, _T("itemhottextcolor")) == 0 ){ + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetItemHotTextColor(clrColor); + } + else if(_tcscmp(pstrName, _T("selitemtextcolor")) == 0 ){ + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetSelItemTextColor(clrColor); + } + else if(_tcscmp(pstrName, _T("selitemhottextcolor")) == 0 ){ + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetSelItemHotTextColor(clrColor); + } + else CListUI::SetAttribute(pstrName,pstrValue); + } + +} \ No newline at end of file diff --git a/DuiLib/Control/UITreeView.h b/DuiLib/Control/UITreeView.h new file mode 100644 index 00000000..7b55e965 --- /dev/null +++ b/DuiLib/Control/UITreeView.h @@ -0,0 +1,139 @@ +#ifndef UITreeView_h__ +#define UITreeView_h__ + + +#pragma once + +namespace DuiLib +{ + class CTreeViewUI; + class COptionUI; + class CLabelUI; + class COptionUI; + + class UILIB_API CTreeNodeUI : public CListContainerElementUI + { + public: + CTreeNodeUI(CTreeNodeUI* _ParentNode = NULL); + ~CTreeNodeUI(void); + + public: + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + void DoEvent(TEventUI& event); + void Invalidate(); + bool Select(bool bSelect = true); + + bool Add(CControlUI* _pTreeNodeUI); + bool AddAt(CControlUI* pControl, int iIndex); + bool Remove(CControlUI* pControl); + + void SetVisibleTag(bool _IsVisible); + bool GetVisibleTag(); + void SetItemText(LPCTSTR pstrValue); + CDuiString GetItemText(); + void CheckBoxSelected(bool _Selected); + bool IsCheckBoxSelected() const; + bool IsHasChild() const; + long GetTreeLevel() const; + bool AddChildNode(CTreeNodeUI* _pTreeNodeUI); + bool RemoveAt(CTreeNodeUI* _pTreeNodeUI); + void SetParentNode(CTreeNodeUI* _pParentTreeNode); + CTreeNodeUI* GetParentNode(); + long GetCountChild(); + void SetTreeView(CTreeViewUI* _CTreeViewUI); + CTreeViewUI* GetTreeView(); + CTreeNodeUI* GetChildNode(int _nIndex); + void SetVisibleFolderBtn(bool _IsVisibled); + bool GetVisibleFolderBtn(); + void SetVisibleCheckBtn(bool _IsVisibled); + bool GetVisibleCheckBtn(); + void SetItemTextColor(DWORD _dwItemTextColor); + DWORD GetItemTextColor() const; + void SetItemHotTextColor(DWORD _dwItemHotTextColor); + DWORD GetItemHotTextColor() const; + void SetSelItemTextColor(DWORD _dwSelItemTextColor); + DWORD GetSelItemTextColor() const; + void SetSelItemHotTextColor(DWORD _dwSelHotItemTextColor); + DWORD GetSelItemHotTextColor() const; + + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + void IsAllChildChecked(); + CStdPtrArray GetTreeNodes(); + + int GetTreeIndex(); + int GetNodeIndex(); + + private: + CTreeNodeUI* GetLastNode(); + CTreeNodeUI* CalLocation(CTreeNodeUI* _pTreeNodeUI); + public: + CHorizontalLayoutUI* GetTreeNodeHoriznotal() const {return pHoriz;}; + COptionUI* GetFolderButton() const { return pFolderButton; }; + CLabelUI* GetDottedLine() const {return pDottedLine;}; + COptionUI* GetCheckBox() const { return pCheckBox; }; + COptionUI* GetItemButton() const {return pItemButton;}; + + private: + long m_iTreeLavel; + bool m_bIsVisable; + bool m_bIsCheckBox; + DWORD m_dwItemTextColor; + DWORD m_dwItemHotTextColor; + DWORD m_dwSelItemTextColor; + DWORD m_dwSelItemHotTextColor; + + CTreeViewUI* pTreeView; + CHorizontalLayoutUI* pHoriz; + COptionUI* pFolderButton; + CLabelUI* pDottedLine; + COptionUI* pCheckBox; + COptionUI* pItemButton; + CTreeNodeUI* pParentTreeNode; + + CStdPtrArray mTreeNodes; + }; + + class UILIB_API CTreeViewUI : public CListUI,public INotifyUI + { + public: + CTreeViewUI(void); + ~CTreeViewUI(void); + + public: + virtual LPCTSTR GetClass() const; + virtual LPVOID GetInterface(LPCTSTR pstrName); + virtual bool Add(CTreeNodeUI* pControl ); + virtual long AddAt(CTreeNodeUI* pControl, int iIndex ); + virtual bool AddAt(CTreeNodeUI* pControl,CTreeNodeUI* _IndexNode); + virtual bool Remove(CTreeNodeUI* pControl); + virtual bool RemoveAt(int iIndex); + virtual void RemoveAll(); + virtual bool OnCheckBoxChanged(void* param); + virtual bool OnFolderChanged(void* param); + virtual bool OnDBClickItem(void* param); + virtual bool SetItemCheckBox(bool _Selected,CTreeNodeUI* _TreeNode = NULL); + virtual void SetItemExpand(bool _Expanded,CTreeNodeUI* _TreeNode = NULL); + virtual void Notify(TNotifyUI& msg); + virtual void SetVisibleFolderBtn(bool _IsVisibled); + virtual bool GetVisibleFolderBtn(); + virtual void SetVisibleCheckBtn(bool _IsVisibled); + virtual bool GetVisibleCheckBtn(); + virtual void SetItemMinWidth(UINT _ItemMinWidth); + virtual UINT GetItemMinWidth(); + virtual void SetItemTextColor(DWORD _dwItemTextColor); + virtual void SetItemHotTextColor(DWORD _dwItemHotTextColor); + virtual void SetSelItemTextColor(DWORD _dwSelItemTextColor); + virtual void SetSelItemHotTextColor(DWORD _dwSelHotItemTextColor); + + virtual void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + private: + UINT m_uItemMinWidth; + bool m_bVisibleFolderBtn; + bool m_bVisibleCheckBtn; + }; +} + + +#endif // UITreeView_h__ diff --git a/DuiLib/Control/UIWebBrowser.cpp b/DuiLib/Control/UIWebBrowser.cpp new file mode 100644 index 00000000..d50c9188 --- /dev/null +++ b/DuiLib/Control/UIWebBrowser.cpp @@ -0,0 +1,715 @@ +#include "StdAfx.h" +#include "UIWebBrowser.h" +#include +#include +#include "../Utils/downloadmgr.h" +#include + +DuiLib::CWebBrowserUI::CWebBrowserUI() +: m_pWebBrowser2(NULL) +, _pHtmlWnd2(NULL) +, m_pWebBrowserEventHandler(NULL) +, m_bAutoNavi(false) +, m_dwRef(0) +, m_dwCookie(0) +{ + m_clsid=CLSID_WebBrowser; + m_sHomePage.Empty(); +} + +bool DuiLib::CWebBrowserUI::DoCreateControl() +{ + if (!CActiveXUI::DoCreateControl()) + return false; + GetManager()->AddTranslateAccelerator(this); + GetControl(IID_IWebBrowser2,(LPVOID*)&m_pWebBrowser2); + if ( m_bAutoNavi && !m_sHomePage.IsEmpty()) + { + this->Navigate2(m_sHomePage); + } + RegisterEventHandler(TRUE); + return true; +} + +void DuiLib::CWebBrowserUI::ReleaseControl() +{ + m_bCreated=false; + GetManager()->RemoveTranslateAccelerator(this); + RegisterEventHandler(FALSE); +} + +DuiLib::CWebBrowserUI::~CWebBrowserUI() +{ + ReleaseControl(); +} + +STDMETHODIMP DuiLib::CWebBrowserUI::GetTypeInfoCount( UINT *iTInfo ) +{ + *iTInfo = 0; + return E_NOTIMPL; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::GetTypeInfo( UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo ) +{ + return E_NOTIMPL; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::GetIDsOfNames( REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid,DISPID *rgDispId ) +{ + return E_NOTIMPL; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::Invoke( DISPID dispIdMember, REFIID riid, LCID lcid,WORD wFlags, DISPPARAMS* pDispParams,VARIANT* pVarResult, EXCEPINFO* pExcepInfo,UINT* puArgErr ) +{ + if ((riid != IID_NULL)) + return E_INVALIDARG; + + switch(dispIdMember) + { + case DISPID_BEFORENAVIGATE2: + BeforeNavigate2( + pDispParams->rgvarg[6].pdispVal, + pDispParams->rgvarg[5].pvarVal, + pDispParams->rgvarg[4].pvarVal, + pDispParams->rgvarg[3].pvarVal, + pDispParams->rgvarg[2].pvarVal, + pDispParams->rgvarg[1].pvarVal, + pDispParams->rgvarg[0].pboolVal); + break; + case DISPID_COMMANDSTATECHANGE: + CommandStateChange( + pDispParams->rgvarg[1].lVal, + pDispParams->rgvarg[0].boolVal); + break; + case DISPID_NAVIGATECOMPLETE2: + NavigateComplete2( + pDispParams->rgvarg[1].pdispVal, + pDispParams->rgvarg[0].pvarVal); + break; + case DISPID_NAVIGATEERROR: + NavigateError( + pDispParams->rgvarg[4].pdispVal, + pDispParams->rgvarg[3].pvarVal, + pDispParams->rgvarg[2].pvarVal, + pDispParams->rgvarg[1].pvarVal, + pDispParams->rgvarg[0].pboolVal); + break; + case DISPID_STATUSTEXTCHANGE: + break; + // case DISPID_NEWWINDOW2: + // break; + case DISPID_NEWWINDOW3: + NewWindow3( + pDispParams->rgvarg[4].ppdispVal, + pDispParams->rgvarg[3].pboolVal, + pDispParams->rgvarg[2].uintVal, + pDispParams->rgvarg[1].bstrVal, + pDispParams->rgvarg[0].bstrVal); + break; +// case DISPID_PROPERTYCHANGE: +// if (pDispParams->cArgs>0 && pDispParams->rgvarg[0].vt == VT_BSTR) { +// TRACE(_T("PropertyChange(%s)\n"), pDispParams->rgvarg[0].bstrVal); +// } +// break; + default: + return DISP_E_MEMBERNOTFOUND; + } + return S_OK; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::QueryInterface( REFIID riid, LPVOID *ppvObject ) +{ + *ppvObject = NULL; + + if( riid == IID_IDocHostUIHandler) + *ppvObject = static_cast(this); + else if( riid == IID_IDispatch) + *ppvObject = static_cast(this); + else if( riid == IID_IServiceProvider) + *ppvObject = static_cast(this); + else if (riid == IID_IOleCommandTarget) + *ppvObject = static_cast(this); + + if( *ppvObject != NULL ) + AddRef(); + return *ppvObject == NULL ? E_NOINTERFACE : S_OK; +} + +STDMETHODIMP_(ULONG) DuiLib::CWebBrowserUI::AddRef() +{ + InterlockedIncrement(&m_dwRef); + return m_dwRef; +} + +STDMETHODIMP_(ULONG) DuiLib::CWebBrowserUI::Release() +{ + ULONG ulRefCount = InterlockedDecrement(&m_dwRef); + return ulRefCount; +} + +void DuiLib::CWebBrowserUI::Navigate2( LPCTSTR lpszUrl ) +{ + if (lpszUrl == NULL) + return; + + if (m_pWebBrowser2) + { + CDuiVariant url; + url.vt=VT_BSTR; + url.bstrVal=T2BSTR(lpszUrl); + HRESULT hr = m_pWebBrowser2->Navigate2(&url, NULL, NULL, NULL, NULL); + } +} + +void DuiLib::CWebBrowserUI::Refresh() +{ + if (m_pWebBrowser2) + { + m_pWebBrowser2->Refresh(); + } +} +void DuiLib::CWebBrowserUI::GoBack() +{ + if (m_pWebBrowser2) + { + m_pWebBrowser2->GoBack(); + } +} +void DuiLib::CWebBrowserUI::GoForward() +{ + if (m_pWebBrowser2) + { + m_pWebBrowser2->GoForward(); + } +} +/// DWebBrowserEvents2 +void DuiLib::CWebBrowserUI::BeforeNavigate2( IDispatch *pDisp,VARIANT *&url,VARIANT *&Flags,VARIANT *&TargetFrameName,VARIANT *&PostData,VARIANT *&Headers,VARIANT_BOOL *&Cancel ) +{ + if (m_pWebBrowserEventHandler) + { + m_pWebBrowserEventHandler->BeforeNavigate2(pDisp,url,Flags,TargetFrameName,PostData,Headers,Cancel); + } +} + +void DuiLib::CWebBrowserUI::NavigateError( IDispatch *pDisp,VARIANT * &url,VARIANT *&TargetFrameName,VARIANT *&StatusCode,VARIANT_BOOL *&Cancel ) +{ + if (m_pWebBrowserEventHandler) + { + m_pWebBrowserEventHandler->NavigateError(pDisp,url,TargetFrameName,StatusCode,Cancel); + } +} + +void DuiLib::CWebBrowserUI::NavigateComplete2( IDispatch *pDisp,VARIANT *&url ) +{ + CComPtr spDoc; + m_pWebBrowser2->get_Document(&spDoc); + + if (spDoc) + { + CComQIPtr spCustomDoc(spDoc); + if (spCustomDoc) + spCustomDoc->SetUIHandler(this); + } + + if (m_pWebBrowserEventHandler) + { + m_pWebBrowserEventHandler->NavigateComplete2(pDisp,url); + } +} + +void DuiLib::CWebBrowserUI::ProgressChange( LONG nProgress, LONG nProgressMax ) +{ + if (m_pWebBrowserEventHandler) + { + m_pWebBrowserEventHandler->ProgressChange(nProgress,nProgressMax); + } +} + +void DuiLib::CWebBrowserUI::NewWindow3( IDispatch **pDisp, VARIANT_BOOL *&Cancel, DWORD dwFlags, BSTR bstrUrlContext, BSTR bstrUrl ) +{ + if (m_pWebBrowserEventHandler) + { + m_pWebBrowserEventHandler->NewWindow3(pDisp,Cancel,dwFlags,bstrUrlContext,bstrUrl); + } +} +void DuiLib::CWebBrowserUI::CommandStateChange(long Command,VARIANT_BOOL Enable) +{ + if (m_pWebBrowserEventHandler) + { + m_pWebBrowserEventHandler->CommandStateChange(Command,Enable); + } +} + +// IDownloadManager +STDMETHODIMP DuiLib::CWebBrowserUI::Download( /* [in] */ IMoniker *pmk, /* [in] */ IBindCtx *pbc, /* [in] */ DWORD dwBindVerb, /* [in] */ LONG grfBINDF, /* [in] */ BINDINFO *pBindInfo, /* [in] */ LPCOLESTR pszHeaders, /* [in] */ LPCOLESTR pszRedir, /* [in] */ UINT uiCP ) +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->Download(pmk,pbc,dwBindVerb,grfBINDF,pBindInfo,pszHeaders,pszRedir,uiCP); + } + return S_OK; +} + +// IDocHostUIHandler +STDMETHODIMP DuiLib::CWebBrowserUI::ShowContextMenu( DWORD dwID, POINT* pptPosition, IUnknown* pCommandTarget, IDispatch* pDispatchObjectHit ) +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->ShowContextMenu(dwID,pptPosition,pCommandTarget,pDispatchObjectHit); + } + return S_FALSE; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::GetHostInfo( DOCHOSTUIINFO* pInfo ) +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->GetHostInfo(pInfo); + } + return E_NOTIMPL; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::ShowUI( DWORD dwID, IOleInPlaceActiveObject* pActiveObject, IOleCommandTarget* pCommandTarget, IOleInPlaceFrame* pFrame, IOleInPlaceUIWindow* pDoc ) +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->ShowUI(dwID,pActiveObject,pCommandTarget,pFrame,pDoc); + } + return S_OK; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::HideUI() +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->HideUI(); + } + return S_OK; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::UpdateUI() +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->UpdateUI(); + } + return S_OK; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::EnableModeless( BOOL fEnable ) +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->EnableModeless(fEnable); + } + return E_NOTIMPL; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::OnDocWindowActivate( BOOL fActivate ) +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->OnDocWindowActivate(fActivate); + } + return E_NOTIMPL; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::OnFrameWindowActivate( BOOL fActivate ) +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->OnFrameWindowActivate(fActivate); + } + return E_NOTIMPL; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::ResizeBorder( LPCRECT prcBorder, IOleInPlaceUIWindow* pUIWindow, BOOL fFrameWindow ) +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->ResizeBorder(prcBorder,pUIWindow,fFrameWindow); + } + return E_NOTIMPL; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::TranslateAccelerator( LPMSG lpMsg, const GUID* pguidCmdGroup, DWORD nCmdID ) +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->TranslateAccelerator(lpMsg,pguidCmdGroup,nCmdID); + } + return S_FALSE; +} + +LRESULT DuiLib::CWebBrowserUI::TranslateAccelerator( MSG *pMsg ) +{ + if(pMsg->message < WM_KEYFIRST || pMsg->message > WM_KEYLAST) + return S_FALSE; + + if( m_pWebBrowser2 == NULL ) + return E_NOTIMPL; + + // ǰWebڲǽ,ټ + BOOL bIsChild = FALSE; + HWND hTempWnd = NULL; + HWND hWndFocus = ::GetFocus(); + + hTempWnd = hWndFocus; + while(hTempWnd != NULL) + { + if(hTempWnd == m_hwndHost) + { + bIsChild = TRUE; + break; + } + hTempWnd = ::GetParent(hTempWnd); + } + if(!bIsChild) + return S_FALSE; + + IOleInPlaceActiveObject *pObj; + if (FAILED(m_pWebBrowser2->QueryInterface(IID_IOleInPlaceActiveObject, (LPVOID *)&pObj))) + return S_FALSE; + + HRESULT hResult = pObj->TranslateAccelerator(pMsg); + pObj->Release(); + return hResult; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::GetOptionKeyPath( LPOLESTR* pchKey, DWORD dwReserved ) +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->GetOptionKeyPath(pchKey,dwReserved); + } + return E_NOTIMPL; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::GetDropTarget( IDropTarget* pDropTarget, IDropTarget** ppDropTarget ) +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->GetDropTarget(pDropTarget,ppDropTarget); + } + return S_FALSE; // ʹϵͳק +} + +STDMETHODIMP DuiLib::CWebBrowserUI::GetExternal( IDispatch** ppDispatch ) +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->GetExternal(ppDispatch); + } + return S_FALSE; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::TranslateUrl( DWORD dwTranslate, OLECHAR* pchURLIn, OLECHAR** ppchURLOut ) +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->TranslateUrl(dwTranslate,pchURLIn,ppchURLOut); + } + else + { + *ppchURLOut = 0; + return E_NOTIMPL; + } +} + +STDMETHODIMP DuiLib::CWebBrowserUI::FilterDataObject( IDataObject* pDO, IDataObject** ppDORet ) +{ + if (m_pWebBrowserEventHandler) + { + return m_pWebBrowserEventHandler->FilterDataObject(pDO,ppDORet); + } + else + { + *ppDORet = 0; + return E_NOTIMPL; + } +} + +void DuiLib::CWebBrowserUI::SetWebBrowserEventHandler( CWebBrowserEventHandler* pEventHandler ) +{ + if ( pEventHandler!=NULL && m_pWebBrowserEventHandler!=pEventHandler) + m_pWebBrowserEventHandler=pEventHandler; +} + +void DuiLib::CWebBrowserUI::Refresh2( int Level ) +{ + CDuiVariant vLevel; + vLevel.vt=VT_I4; + vLevel.intVal=Level; + m_pWebBrowser2->Refresh2(&vLevel); +} + +void DuiLib::CWebBrowserUI::SetAttribute( LPCTSTR pstrName, LPCTSTR pstrValue ) +{ + if (_tcscmp(pstrName, _T("homepage")) == 0) + { + m_sHomePage = pstrValue; + } + else if (_tcscmp(pstrName, _T("autonavi"))==0) + { + m_bAutoNavi = (_tcscmp(pstrValue, _T("true")) == 0); + } + else + CActiveXUI::SetAttribute(pstrName, pstrValue); +} + +void DuiLib::CWebBrowserUI::NavigateHomePage() +{ + if (!m_sHomePage.IsEmpty()) + this->NavigateUrl(m_sHomePage); +} + +void DuiLib::CWebBrowserUI::NavigateUrl( LPCTSTR lpszUrl ) +{ + if (m_pWebBrowser2 && lpszUrl) + { + m_pWebBrowser2->Navigate((BSTR)SysAllocString(T2BSTR(lpszUrl)),NULL,NULL,NULL,NULL); + } +} + +LPCTSTR DuiLib::CWebBrowserUI::GetClass() const +{ + return _T("WebBrowserUI"); +} + +LPVOID DuiLib::CWebBrowserUI::GetInterface( LPCTSTR pstrName ) +{ + if( _tcscmp(pstrName, DUI_CTR_WEBBROWSER) == 0 ) return static_cast(this); + return CActiveXUI::GetInterface(pstrName); +} + +void DuiLib::CWebBrowserUI::SetHomePage( LPCTSTR lpszUrl ) +{ + m_sHomePage.Format(_T("%s"),lpszUrl); +} + +LPCTSTR DuiLib::CWebBrowserUI::GetHomePage() +{ + return m_sHomePage; +} + +void DuiLib::CWebBrowserUI::SetAutoNavigation( bool bAuto /*= TRUE*/ ) +{ + if (m_bAutoNavi==bAuto) return; + + m_bAutoNavi=bAuto; +} + +bool DuiLib::CWebBrowserUI::IsAutoNavigation() +{ + return m_bAutoNavi; +} + +STDMETHODIMP DuiLib::CWebBrowserUI::QueryService( REFGUID guidService, REFIID riid, void** ppvObject ) +{ + HRESULT hr = E_NOINTERFACE; + *ppvObject = NULL; + + if ( guidService == SID_SDownloadManager && riid == IID_IDownloadManager) + { + *ppvObject = this; + return S_OK; + } + + return hr; +} + +HRESULT DuiLib::CWebBrowserUI::RegisterEventHandler( BOOL inAdvise ) +{ + CComPtr pWebBrowser; + CComPtr pCPC; + CComPtr pCP; + HRESULT hr = GetControl(IID_IWebBrowser2, (void**)&pWebBrowser); + if (FAILED(hr)) + return hr; + hr=pWebBrowser->QueryInterface(IID_IConnectionPointContainer,(void **)&pCPC); + if (FAILED(hr)) + return hr; + hr=pCPC->FindConnectionPoint(DIID_DWebBrowserEvents2,&pCP); + if (FAILED(hr)) + return hr; + + if (inAdvise) + { + hr = pCP->Advise((IDispatch*)this, &m_dwCookie); + } + else + { + hr = pCP->Unadvise(m_dwCookie); + } + return hr; +} + +DISPID DuiLib::CWebBrowserUI::FindId( IDispatch *pObj, LPOLESTR pName ) +{ + DISPID id = 0; + if(FAILED(pObj->GetIDsOfNames(IID_NULL,&pName,1,LOCALE_SYSTEM_DEFAULT,&id))) id = -1; + return id; +} + +HRESULT DuiLib::CWebBrowserUI::InvokeMethod( IDispatch *pObj, LPOLESTR pMehtod, VARIANT *pVarResult, VARIANT *ps, int cArgs ) +{ + DISPID dispid = FindId(pObj, pMehtod); + if(dispid == -1) return E_FAIL; + + DISPPARAMS dispparams; + dispparams.cArgs = cArgs; + dispparams.rgvarg = ps; + dispparams.cNamedArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + + return pObj->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &dispparams, pVarResult, NULL, NULL); +} + +HRESULT DuiLib::CWebBrowserUI::GetProperty( IDispatch *pObj, LPOLESTR pName, VARIANT *pValue ) +{ + DISPID dispid = FindId(pObj, pName); + if(dispid == -1) return E_FAIL; + + DISPPARAMS ps; + ps.cArgs = 0; + ps.rgvarg = NULL; + ps.cNamedArgs = 0; + ps.rgdispidNamedArgs = NULL; + + return pObj->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &ps, pValue, NULL, NULL); +} + +HRESULT DuiLib::CWebBrowserUI::SetProperty( IDispatch *pObj, LPOLESTR pName, VARIANT *pValue ) +{ + DISPID dispid = FindId(pObj, pName); + if(dispid == -1) return E_FAIL; + + DISPPARAMS ps; + ps.cArgs = 1; + ps.rgvarg = pValue; + ps.cNamedArgs = 0; + ps.rgdispidNamedArgs = NULL; + + return pObj->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &ps, NULL, NULL, NULL); +} + +IDispatch* DuiLib::CWebBrowserUI::GetHtmlWindow() +{ + IDispatch* pDp = NULL; + HRESULT hr; + if (m_pWebBrowser2) + hr=m_pWebBrowser2->get_Document(&pDp); + + if (FAILED(hr)) + return NULL; + + CComQIPtr pHtmlDoc2 = pDp; + + if (pHtmlDoc2 == NULL) + return NULL; + + hr=pHtmlDoc2->get_parentWindow(&_pHtmlWnd2); + + if (FAILED(hr)) + return NULL; + + IDispatch *pHtmlWindown = NULL; + hr=_pHtmlWnd2->QueryInterface(IID_IDispatch, (void**)&pHtmlWindown); + if (FAILED(hr)) + return NULL; + + return pHtmlWindown; +} + +IWebBrowser2* DuiLib::CWebBrowserUI::GetWebBrowser2( void ) +{ + return m_pWebBrowser2; +} + +HRESULT STDMETHODCALLTYPE DuiLib::CWebBrowserUI::QueryStatus( __RPC__in_opt const GUID *pguidCmdGroup, ULONG cCmds, __RPC__inout_ecount_full(cCmds ) OLECMD prgCmds[ ], __RPC__inout_opt OLECMDTEXT *pCmdText ) +{ + HRESULT hr = OLECMDERR_E_NOTSUPPORTED; + return hr; +} + +HRESULT STDMETHODCALLTYPE DuiLib::CWebBrowserUI::Exec( __RPC__in_opt const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, __RPC__in_opt VARIANT *pvaIn, __RPC__inout_opt VARIANT *pvaOut ) +{ + HRESULT hr = S_OK; + + if (pguidCmdGroup && IsEqualGUID(*pguidCmdGroup, CGID_DocHostCommandHandler)) + { + + switch (nCmdID) + { + + case OLECMDID_SHOWSCRIPTERROR: + { + IHTMLDocument2* pDoc = NULL; + IHTMLWindow2* pWindow = NULL; + IHTMLEventObj* pEventObj = NULL; + BSTR rgwszNames[5] = + { + SysAllocString(L"errorLine"), + SysAllocString(L"errorCharacter"), + SysAllocString(L"errorCode"), + SysAllocString(L"errorMessage"), + SysAllocString(L"errorUrl") + }; + DISPID rgDispIDs[5]; + VARIANT rgvaEventInfo[5]; + DISPPARAMS params; + BOOL fContinueRunningScripts = true; + int i; + + params.cArgs = 0; + params.cNamedArgs = 0; + + // Get the document that is currently being viewed. + hr = pvaIn->punkVal->QueryInterface(IID_IHTMLDocument2, (void **) &pDoc); + // Get document.parentWindow. + hr = pDoc->get_parentWindow(&pWindow); + pDoc->Release(); + // Get the window.event object. + hr = pWindow->get_event(&pEventObj); + // Get the error info from the window.event object. + for (i = 0; i < 5; i++) + { + // Get the property's dispID. + hr = pEventObj->GetIDsOfNames(IID_NULL, &rgwszNames[i], 1, + LOCALE_SYSTEM_DEFAULT, &rgDispIDs[i]); + // Get the value of the property. + hr = pEventObj->Invoke(rgDispIDs[i], IID_NULL, + LOCALE_SYSTEM_DEFAULT, + DISPATCH_PROPERTYGET, ¶ms, &rgvaEventInfo[i], + NULL, NULL); + SysFreeString(rgwszNames[i]); + } + + // At this point, you would normally alert the user with + // the information about the error, which is now contained + // in rgvaEventInfo[]. Or, you could just exit silently. + + (*pvaOut).vt = VT_BOOL; + if (fContinueRunningScripts) + { + // Continue running scripts on the page. + (*pvaOut).boolVal = VARIANT_TRUE; + } + else + { + // Stop running scripts on the page. + (*pvaOut).boolVal = VARIANT_FALSE; + } + break; + } + default: + hr = OLECMDERR_E_NOTSUPPORTED; + break; + } + } + else + { + hr = OLECMDERR_E_UNKNOWNGROUP; + } + return (hr); +} diff --git a/DuiLib/Control/UIWebBrowser.h b/DuiLib/Control/UIWebBrowser.h new file mode 100644 index 00000000..64038779 --- /dev/null +++ b/DuiLib/Control/UIWebBrowser.h @@ -0,0 +1,122 @@ +#ifndef __UIWEBBROWSER_H__ +#define __UIWEBBROWSER_H__ + +#pragma once + +#include +#include "Utils/WebBrowserEventHandler.h" +#include + +namespace DuiLib +{ + class UILIB_API CWebBrowserUI + : public CActiveXUI + , public IDocHostUIHandler + , public IServiceProvider + , public IOleCommandTarget + , public IDispatch + , public ITranslateAccelerator + { + public: + /// 캯 + CWebBrowserUI(); + virtual ~CWebBrowserUI(); + + void SetHomePage(LPCTSTR lpszUrl); + LPCTSTR GetHomePage(); + + void SetAutoNavigation(bool bAuto = TRUE); + bool IsAutoNavigation(); + + void SetWebBrowserEventHandler(CWebBrowserEventHandler* pEventHandler); + void Navigate2(LPCTSTR lpszUrl); + void Refresh(); + void Refresh2(int Level); + void GoBack(); + void GoForward(); + void NavigateHomePage(); + void NavigateUrl(LPCTSTR lpszUrl); + virtual bool DoCreateControl(); + IWebBrowser2* GetWebBrowser2(void); + IDispatch* GetHtmlWindow(); + static DISPID FindId(IDispatch *pObj, LPOLESTR pName); + static HRESULT InvokeMethod(IDispatch *pObj, LPOLESTR pMehtod, VARIANT *pVarResult, VARIANT *ps, int cArgs); + static HRESULT GetProperty(IDispatch *pObj, LPOLESTR pName, VARIANT *pValue); + static HRESULT SetProperty(IDispatch *pObj, LPOLESTR pName, VARIANT *pValue); + + protected: + IWebBrowser2* m_pWebBrowser2; //ָ + IHTMLWindow2* _pHtmlWnd2; + LONG m_dwRef; + DWORD m_dwCookie; + virtual void ReleaseControl(); + HRESULT RegisterEventHandler(BOOL inAdvise); + virtual void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + CDuiString m_sHomePage; // Ĭҳ + bool m_bAutoNavi; // ǷʱĬҳ + CWebBrowserEventHandler* m_pWebBrowserEventHandler; //¼ + + // DWebBrowserEvents2 + void BeforeNavigate2( IDispatch *pDisp,VARIANT *&url,VARIANT *&Flags,VARIANT *&TargetFrameName,VARIANT *&PostData,VARIANT *&Headers,VARIANT_BOOL *&Cancel ); + void NavigateError(IDispatch *pDisp,VARIANT * &url,VARIANT *&TargetFrameName,VARIANT *&StatusCode,VARIANT_BOOL *&Cancel); + void NavigateComplete2(IDispatch *pDisp,VARIANT *&url); + void ProgressChange(LONG nProgress, LONG nProgressMax); + void NewWindow3(IDispatch **pDisp, VARIANT_BOOL *&Cancel, DWORD dwFlags, BSTR bstrUrlContext, BSTR bstrUrl); + void CommandStateChange(long Command,VARIANT_BOOL Enable); + + public: + virtual LPCTSTR GetClass() const; + virtual LPVOID GetInterface( LPCTSTR pstrName ); + + // IUnknown + STDMETHOD_(ULONG,AddRef)(); + STDMETHOD_(ULONG,Release)(); + STDMETHOD(QueryInterface)(REFIID riid, LPVOID *ppvObject); + + // IDispatch + virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount( __RPC__out UINT *pctinfo ); + virtual HRESULT STDMETHODCALLTYPE GetTypeInfo( UINT iTInfo, LCID lcid, __RPC__deref_out_opt ITypeInfo **ppTInfo ); + virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames( __RPC__in REFIID riid, __RPC__in_ecount_full(cNames ) LPOLESTR *rgszNames, UINT cNames, LCID lcid, __RPC__out_ecount_full(cNames) DISPID *rgDispId); + virtual HRESULT STDMETHODCALLTYPE Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr ); + + // IDocHostUIHandler + STDMETHOD(ShowContextMenu)(DWORD dwID, POINT* pptPosition, IUnknown* pCommandTarget, IDispatch* pDispatchObjectHit); + STDMETHOD(GetHostInfo)(DOCHOSTUIINFO* pInfo); + STDMETHOD(ShowUI)(DWORD dwID, IOleInPlaceActiveObject* pActiveObject, IOleCommandTarget* pCommandTarget, IOleInPlaceFrame* pFrame, IOleInPlaceUIWindow* pDoc); + STDMETHOD(HideUI)(); + STDMETHOD(UpdateUI)(); + STDMETHOD(EnableModeless)(BOOL fEnable); + STDMETHOD(OnDocWindowActivate)(BOOL fActivate); + STDMETHOD(OnFrameWindowActivate)(BOOL fActivate); + STDMETHOD(ResizeBorder)(LPCRECT prcBorder, IOleInPlaceUIWindow* pUIWindow, BOOL fFrameWindow); + STDMETHOD(TranslateAccelerator)(LPMSG lpMsg, const GUID* pguidCmdGroup, DWORD nCmdID); //Ϣ + STDMETHOD(GetOptionKeyPath)(LPOLESTR* pchKey, DWORD dwReserved); + STDMETHOD(GetDropTarget)(IDropTarget* pDropTarget, IDropTarget** ppDropTarget); + STDMETHOD(GetExternal)(IDispatch** ppDispatch); + STDMETHOD(TranslateUrl)(DWORD dwTranslate, OLECHAR* pchURLIn, OLECHAR** ppchURLOut); + STDMETHOD(FilterDataObject)(IDataObject* pDO, IDataObject** ppDORet); + + // IServiceProvider + STDMETHOD(QueryService)(REFGUID guidService, REFIID riid, void** ppvObject); + + // IOleCommandTarget + virtual HRESULT STDMETHODCALLTYPE QueryStatus( __RPC__in_opt const GUID *pguidCmdGroup, ULONG cCmds, __RPC__inout_ecount_full(cCmds ) OLECMD prgCmds[ ], __RPC__inout_opt OLECMDTEXT *pCmdText); + virtual HRESULT STDMETHODCALLTYPE Exec( __RPC__in_opt const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, __RPC__in_opt VARIANT *pvaIn, __RPC__inout_opt VARIANT *pvaOut ); + + // IDownloadManager + STDMETHOD(Download)( + /* [in] */ IMoniker *pmk, + /* [in] */ IBindCtx *pbc, + /* [in] */ DWORD dwBindVerb, + /* [in] */ LONG grfBINDF, + /* [in] */ BINDINFO *pBindInfo, + /* [in] */ LPCOLESTR pszHeaders, + /* [in] */ LPCOLESTR pszRedir, + /* [in] */ UINT uiCP); + + // ITranslateAccelerator + // DuilibϢַWebBrowser + virtual LRESULT TranslateAccelerator( MSG *pMsg ); + }; +} // namespace DuiLib +#endif // __UIWEBBROWSER_H__ \ No newline at end of file diff --git a/DuiLib/Core/UIBase.cpp b/DuiLib/Core/UIBase.cpp new file mode 100644 index 00000000..9be6baeb --- /dev/null +++ b/DuiLib/Core/UIBase.cpp @@ -0,0 +1,503 @@ +#include "StdAfx.h" + +#ifdef _DEBUG +#include +#pragma comment(lib, "shlwapi.lib") +#endif + +namespace DuiLib { + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +void UILIB_API DUI__Trace(LPCTSTR pstrFormat, ...) +{ +#ifdef _DEBUG + CDuiString strMsg; + va_list Args; + + va_start(Args, pstrFormat); + strMsg.Format(pstrFormat, Args); + va_end(Args); + + strMsg += _T("\n"); + OutputDebugString(strMsg.GetData()); + +#endif +} + +LPCTSTR DUI__TraceMsg(UINT uMsg) +{ +#define MSGDEF(x) if(uMsg==x) return _T(#x) + MSGDEF(WM_SETCURSOR); + MSGDEF(WM_NCHITTEST); + MSGDEF(WM_NCPAINT); + MSGDEF(WM_PAINT); + MSGDEF(WM_ERASEBKGND); + MSGDEF(WM_NCMOUSEMOVE); + MSGDEF(WM_MOUSEMOVE); + MSGDEF(WM_MOUSELEAVE); + MSGDEF(WM_MOUSEHOVER); + MSGDEF(WM_NOTIFY); + MSGDEF(WM_COMMAND); + MSGDEF(WM_MEASUREITEM); + MSGDEF(WM_DRAWITEM); + MSGDEF(WM_LBUTTONDOWN); + MSGDEF(WM_LBUTTONUP); + MSGDEF(WM_LBUTTONDBLCLK); + MSGDEF(WM_RBUTTONDOWN); + MSGDEF(WM_RBUTTONUP); + MSGDEF(WM_RBUTTONDBLCLK); + MSGDEF(WM_SETFOCUS); + MSGDEF(WM_KILLFOCUS); + MSGDEF(WM_MOVE); + MSGDEF(WM_SIZE); + MSGDEF(WM_SIZING); + MSGDEF(WM_MOVING); + MSGDEF(WM_GETMINMAXINFO); + MSGDEF(WM_CAPTURECHANGED); + MSGDEF(WM_WINDOWPOSCHANGED); + MSGDEF(WM_WINDOWPOSCHANGING); + MSGDEF(WM_NCCALCSIZE); + MSGDEF(WM_NCCREATE); + MSGDEF(WM_NCDESTROY); + MSGDEF(WM_TIMER); + MSGDEF(WM_KEYDOWN); + MSGDEF(WM_KEYUP); + MSGDEF(WM_CHAR); + MSGDEF(WM_SYSKEYDOWN); + MSGDEF(WM_SYSKEYUP); + MSGDEF(WM_SYSCOMMAND); + MSGDEF(WM_SYSCHAR); + MSGDEF(WM_VSCROLL); + MSGDEF(WM_HSCROLL); + MSGDEF(WM_CHAR); + MSGDEF(WM_SHOWWINDOW); + MSGDEF(WM_PARENTNOTIFY); + MSGDEF(WM_CREATE); + MSGDEF(WM_NCACTIVATE); + MSGDEF(WM_ACTIVATE); + MSGDEF(WM_ACTIVATEAPP); + MSGDEF(WM_CLOSE); + MSGDEF(WM_DESTROY); + MSGDEF(WM_GETICON); + MSGDEF(WM_GETTEXT); + MSGDEF(WM_GETTEXTLENGTH); + static TCHAR szMsg[10]; + ::wsprintf(szMsg, _T("0x%04X"), uMsg); + return szMsg; +} + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +////////////////////////////////////////////////////////////////////////// +// +DUI_BASE_BEGIN_MESSAGE_MAP(CNotifyPump) +DUI_END_MESSAGE_MAP() + +static const DUI_MSGMAP_ENTRY* DuiFindMessageEntry(const DUI_MSGMAP_ENTRY* lpEntry,TNotifyUI& msg ) +{ + CDuiString sMsgType = msg.sType; + CDuiString sCtrlName = msg.pSender->GetName(); + const DUI_MSGMAP_ENTRY* pMsgTypeEntry = NULL; + while (lpEntry->nSig != DuiSig_end) + { + if(lpEntry->sMsgType==sMsgType) + { + if(!lpEntry->sCtrlName.IsEmpty()) + { + if(lpEntry->sCtrlName==sCtrlName) + { + return lpEntry; + } + } + else + { + pMsgTypeEntry = lpEntry; + } + } + lpEntry++; + } + return pMsgTypeEntry; +} + +bool CNotifyPump::AddVirtualWnd(CDuiString strName,CNotifyPump* pObject) +{ + if( m_VirtualWndMap.Find(strName) == NULL ) + { + m_VirtualWndMap.Insert(strName.GetData(),(LPVOID)pObject); + return true; + } + return false; +} + +bool CNotifyPump::RemoveVirtualWnd(CDuiString strName) +{ + if( m_VirtualWndMap.Find(strName) != NULL ) + { + m_VirtualWndMap.Remove(strName); + return true; + } + return false; +} + +bool CNotifyPump::LoopDispatch(TNotifyUI& msg) +{ + const DUI_MSGMAP_ENTRY* lpEntry = NULL; + const DUI_MSGMAP* pMessageMap = NULL; + +#ifndef UILIB_STATIC + for(pMessageMap = GetMessageMap(); pMessageMap!=NULL; pMessageMap = (*pMessageMap->pfnGetBaseMap)()) +#else + for(pMessageMap = GetMessageMap(); pMessageMap!=NULL; pMessageMap = pMessageMap->pBaseMap) +#endif + { +#ifndef UILIB_STATIC + ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)()); +#else + ASSERT(pMessageMap != pMessageMap->pBaseMap); +#endif + if ((lpEntry = DuiFindMessageEntry(pMessageMap->lpEntries,msg)) != NULL) + { + goto LDispatch; + } + } + return false; + +LDispatch: + union DuiMessageMapFunctions mmf; + mmf.pfn = lpEntry->pfn; + + bool bRet = false; + int nSig; + nSig = lpEntry->nSig; + switch (nSig) + { + default: + ASSERT(FALSE); + break; + case DuiSig_lwl: + (this->*mmf.pfn_Notify_lwl)(msg.wParam,msg.lParam); + bRet = true; + break; + case DuiSig_vn: + (this->*mmf.pfn_Notify_vn)(msg); + bRet = true; + break; + } + return bRet; +} + +void CNotifyPump::NotifyPump(TNotifyUI& msg) +{ + ///ⴰ + if( !msg.sVirtualWnd.IsEmpty() ){ + for( int i = 0; i< m_VirtualWndMap.GetSize(); i++ ) { + if( LPCTSTR key = m_VirtualWndMap.GetAt(i) ) { + if( _tcsicmp(key, msg.sVirtualWnd.GetData()) == 0 ){ + CNotifyPump* pObject = static_cast(m_VirtualWndMap.Find(key, false)); + if( pObject && pObject->LoopDispatch(msg) ) + return; + } + } + } + } + + /// + // + LoopDispatch( msg ); +} + +////////////////////////////////////////////////////////////////////////// +/// +CWindowWnd::CWindowWnd() : m_hWnd(NULL), m_OldWndProc(::DefWindowProc), m_bSubclassed(false) +{ +} + +HWND CWindowWnd::GetHWND() const +{ + return m_hWnd; +} + +UINT CWindowWnd::GetClassStyle() const +{ + return 0; +} + +LPCTSTR CWindowWnd::GetSuperClassName() const +{ + return NULL; +} + +CWindowWnd::operator HWND() const +{ + return m_hWnd; +} + +HWND CWindowWnd::CreateDuiWindow( HWND hwndParent, LPCTSTR pstrWindowName,DWORD dwStyle /*=0*/, DWORD dwExStyle /*=0*/ ) +{ + return Create(hwndParent,pstrWindowName,dwStyle,dwExStyle,0,0,0,0,NULL); +} + +HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, const RECT rc, HMENU hMenu) +{ + return Create(hwndParent, pstrName, dwStyle, dwExStyle, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hMenu); +} + +HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu) +{ + if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL; + if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL; + m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this); + ASSERT(m_hWnd!=NULL); + return m_hWnd; +} + +HWND CWindowWnd::Subclass(HWND hWnd) +{ + ASSERT(::IsWindow(hWnd)); + ASSERT(m_hWnd==NULL); + m_OldWndProc = SubclassWindow(hWnd, __WndProc); + if( m_OldWndProc == NULL ) return NULL; + m_bSubclassed = true; + m_hWnd = hWnd; + ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast(this)); + return m_hWnd; +} + +void CWindowWnd::Unsubclass() +{ + ASSERT(::IsWindow(m_hWnd)); + if( !::IsWindow(m_hWnd) ) return; + if( !m_bSubclassed ) return; + SubclassWindow(m_hWnd, m_OldWndProc); + m_OldWndProc = ::DefWindowProc; + m_bSubclassed = false; +} + +void CWindowWnd::ShowWindow(bool bShow /*= true*/, bool bTakeFocus /*= false*/) +{ + ASSERT(::IsWindow(m_hWnd)); + if( !::IsWindow(m_hWnd) ) return; + ::ShowWindow(m_hWnd, bShow ? (bTakeFocus ? SW_SHOWNORMAL : SW_SHOWNOACTIVATE) : SW_HIDE); +} + +UINT CWindowWnd::ShowModal() +{ + ASSERT(::IsWindow(m_hWnd)); + UINT nRet = 0; + HWND hWndParent = GetWindowOwner(m_hWnd); + ::ShowWindow(m_hWnd, SW_SHOWNORMAL); + ::EnableWindow(hWndParent, FALSE); + MSG msg = { 0 }; + while( ::IsWindow(m_hWnd) && ::GetMessage(&msg, NULL, 0, 0) ) { + if( msg.message == WM_CLOSE && msg.hwnd == m_hWnd ) { + nRet = msg.wParam; + ::EnableWindow(hWndParent, TRUE); + ::SetFocus(hWndParent); + } + if( !CPaintManagerUI::TranslateMessage(&msg) ) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + if( msg.message == WM_QUIT ) break; + } + ::EnableWindow(hWndParent, TRUE); + ::SetFocus(hWndParent); + if( msg.message == WM_QUIT ) ::PostQuitMessage(msg.wParam); + return nRet; +} + +void CWindowWnd::Close(UINT nRet) +{ + ASSERT(::IsWindow(m_hWnd)); + if( !::IsWindow(m_hWnd) ) return; + PostMessage(WM_CLOSE, (WPARAM)nRet, 0L); +} + +void CWindowWnd::CenterWindow() +{ + ASSERT(::IsWindow(m_hWnd)); + ASSERT((GetWindowStyle(m_hWnd)&WS_CHILD)==0); + RECT rcDlg = { 0 }; + ::GetWindowRect(m_hWnd, &rcDlg); + RECT rcArea = { 0 }; + RECT rcCenter = { 0 }; + HWND hWnd=*this; + HWND hWndParent = ::GetParent(m_hWnd); + HWND hWndCenter = ::GetWindowOwner(m_hWnd); + if (hWndCenter!=NULL) + hWnd=hWndCenter; + + // ʾģʽĻ + MONITORINFO oMonitor = {}; + oMonitor.cbSize = sizeof(oMonitor); + ::GetMonitorInfo(::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), &oMonitor); + rcArea = oMonitor.rcWork; + + if( hWndCenter == NULL ) + rcCenter = rcArea; + else + ::GetWindowRect(hWndCenter, &rcCenter); + + int DlgWidth = rcDlg.right - rcDlg.left; + int DlgHeight = rcDlg.bottom - rcDlg.top; + + // Find dialog's upper left based on rcCenter + int xLeft = (rcCenter.left + rcCenter.right) / 2 - DlgWidth / 2; + int yTop = (rcCenter.top + rcCenter.bottom) / 2 - DlgHeight / 2; + + // The dialog is outside the screen, move it inside + if( xLeft < rcArea.left ) xLeft = rcArea.left; + else if( xLeft + DlgWidth > rcArea.right ) xLeft = rcArea.right - DlgWidth; + if( yTop < rcArea.top ) yTop = rcArea.top; + else if( yTop + DlgHeight > rcArea.bottom ) yTop = rcArea.bottom - DlgHeight; + ::SetWindowPos(m_hWnd, NULL, xLeft, yTop, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); +} + +void CWindowWnd::SetIcon(UINT nRes) +{ + HICON hIcon = (HICON)::LoadImage(CPaintManagerUI::GetInstance(), MAKEINTRESOURCE(nRes), IMAGE_ICON, + (::GetSystemMetrics(SM_CXICON) + 15) & ~15, (::GetSystemMetrics(SM_CYICON) + 15) & ~15, // ֹDPIͼģ + LR_DEFAULTCOLOR); + ASSERT(hIcon); + ::SendMessage(m_hWnd, WM_SETICON, (WPARAM) TRUE, (LPARAM) hIcon); + + hIcon = (HICON)::LoadImage(CPaintManagerUI::GetInstance(), MAKEINTRESOURCE(nRes), IMAGE_ICON, + (::GetSystemMetrics(SM_CXICON) + 15) & ~15, (::GetSystemMetrics(SM_CYICON) + 15) & ~15, // ֹDPIͼģ + LR_DEFAULTCOLOR); + ASSERT(hIcon); + ::SendMessage(m_hWnd, WM_SETICON, (WPARAM) FALSE, (LPARAM) hIcon); +} + +bool CWindowWnd::RegisterWindowClass() +{ + WNDCLASS wc = { 0 }; + wc.style = GetClassStyle(); + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hIcon = NULL; + wc.lpfnWndProc = CWindowWnd::__WndProc; + wc.hInstance = CPaintManagerUI::GetInstance(); + wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = GetWindowClassName(); + ATOM ret = ::RegisterClass(&wc); + ASSERT(ret!=NULL || ::GetLastError()==ERROR_CLASS_ALREADY_EXISTS); + return ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS; +} + +bool CWindowWnd::RegisterSuperclass() +{ + // Get the class information from an existing + // window so we can subclass it later on... + WNDCLASSEX wc = { 0 }; + wc.cbSize = sizeof(WNDCLASSEX); + if( !::GetClassInfoEx(NULL, GetSuperClassName(), &wc) ) { + if( !::GetClassInfoEx(CPaintManagerUI::GetInstance(), GetSuperClassName(), &wc) ) { + ASSERT(!"Unable to locate window class"); + return NULL; + } + } + m_OldWndProc = wc.lpfnWndProc; + wc.lpfnWndProc = CWindowWnd::__ControlProc; + wc.hInstance = CPaintManagerUI::GetInstance(); + wc.lpszClassName = GetWindowClassName(); + ATOM ret = ::RegisterClassEx(&wc); + ASSERT(ret!=NULL || ::GetLastError()==ERROR_CLASS_ALREADY_EXISTS); + return ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS; +} + +LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + CWindowWnd* pThis = NULL; + if( uMsg == WM_NCCREATE ) { + LPCREATESTRUCT lpcs = reinterpret_cast(lParam); + pThis = static_cast(lpcs->lpCreateParams); + pThis->m_hWnd = hWnd; + ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast(pThis)); + } + else { + pThis = reinterpret_cast(::GetWindowLongPtr(hWnd, GWLP_USERDATA)); + if( uMsg == WM_NCDESTROY && pThis != NULL ) { + LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam); + ::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L); + if( pThis->m_bSubclassed ) pThis->Unsubclass(); + pThis->m_hWnd = NULL; + pThis->OnFinalMessage(hWnd); + return lRes; + } + } + if( pThis != NULL ) { + return pThis->HandleMessage(uMsg, wParam, lParam); + } + else { + return ::DefWindowProc(hWnd, uMsg, wParam, lParam); + } +} + +LRESULT CALLBACK CWindowWnd::__ControlProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + CWindowWnd* pThis = NULL; + if( uMsg == WM_NCCREATE ) { + LPCREATESTRUCT lpcs = reinterpret_cast(lParam); + pThis = static_cast(lpcs->lpCreateParams); + ::SetProp(hWnd, _T("WndX"), (HANDLE) pThis); + pThis->m_hWnd = hWnd; + } + else { + pThis = reinterpret_cast(::GetProp(hWnd, _T("WndX"))); + if( uMsg == WM_NCDESTROY && pThis != NULL ) { + LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam); + if( pThis->m_bSubclassed ) pThis->Unsubclass(); + ::SetProp(hWnd, _T("WndX"), NULL); + pThis->m_hWnd = NULL; + pThis->OnFinalMessage(hWnd); + return lRes; + } + } + if( pThis != NULL ) { + return pThis->HandleMessage(uMsg, wParam, lParam); + } + else { + return ::DefWindowProc(hWnd, uMsg, wParam, lParam); + } +} + +LRESULT CWindowWnd::SendMessage(UINT uMsg, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/) +{ + ASSERT(::IsWindow(m_hWnd)); + return ::SendMessage(m_hWnd, uMsg, wParam, lParam); +} + +LRESULT CWindowWnd::PostMessage(UINT uMsg, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/) +{ + ASSERT(::IsWindow(m_hWnd)); + return ::PostMessage(m_hWnd, uMsg, wParam, lParam); +} + +void CWindowWnd::ResizeClient(int cx /*= -1*/, int cy /*= -1*/) +{ + ASSERT(::IsWindow(m_hWnd)); + RECT rc = { 0 }; + if( !::GetClientRect(m_hWnd, &rc) ) return; + if( cx != -1 ) rc.right = cx; + if( cy != -1 ) rc.bottom = cy; + if( !::AdjustWindowRectEx(&rc, GetWindowStyle(m_hWnd), (!(GetWindowStyle(m_hWnd) & WS_CHILD) && (::GetMenu(m_hWnd) != NULL)), GetWindowExStyle(m_hWnd)) ) return; + ::SetWindowPos(m_hWnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); +} + +LRESULT CWindowWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return ::CallWindowProc(m_OldWndProc, m_hWnd, uMsg, wParam, lParam); +} + +void CWindowWnd::OnFinalMessage(HWND /*hWnd*/) +{ +} + +} // namespace DuiLib diff --git a/DuiLib/Core/UIBase.h b/DuiLib/Core/UIBase.h new file mode 100644 index 00000000..ce59ba73 --- /dev/null +++ b/DuiLib/Core/UIBase.h @@ -0,0 +1,106 @@ +#ifndef __UIBASE_H__ +#define __UIBASE_H__ + + +#pragma once + +namespace DuiLib { +///////////////////////////////////////////////////////////////////////////////////// +// + +#define UI_WNDSTYLE_CONTAINER (0) +#define UI_WNDSTYLE_FRAME (WS_VISIBLE | WS_OVERLAPPEDWINDOW) +#define UI_WNDSTYLE_CHILD (WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN) +#define UI_WNDSTYLE_DIALOG (WS_VISIBLE | WS_POPUPWINDOW | WS_CAPTION | WS_DLGFRAME | WS_CLIPSIBLINGS | WS_CLIPCHILDREN) + +#define UI_WNDSTYLE_EX_FRAME (WS_EX_WINDOWEDGE) +#define UI_WNDSTYLE_EX_DIALOG (WS_EX_TOOLWINDOW | WS_EX_DLGMODALFRAME) + +#define UI_CLASSSTYLE_CONTAINER (0) +#define UI_CLASSSTYLE_FRAME (CS_VREDRAW | CS_HREDRAW) +#define UI_CLASSSTYLE_CHILD (CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_SAVEBITS) +#define UI_CLASSSTYLE_DIALOG (CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_SAVEBITS) + + +///////////////////////////////////////////////////////////////////////////////////// +// +#ifndef ASSERT +#define ASSERT(expr) _ASSERTE(expr) +#endif + +#ifdef _DEBUG +#ifndef DUITRACE +#define DUITRACE DUI__Trace +#endif +#define DUITRACEMSG DUI__TraceMsg +#else +#ifndef DUITRACE +#define DUITRACE +#endif +#define DUITRACEMSG _T("") +#endif + +void UILIB_API DUI__Trace(LPCTSTR pstrFormat, ...); +LPCTSTR UILIB_API DUI__TraceMsg(UINT uMsg); + +///////////////////////////////////////////////////////////////////////////////////// +// + +class UILIB_API CNotifyPump +{ +public: + bool AddVirtualWnd(CDuiString strName,CNotifyPump* pObject); + bool RemoveVirtualWnd(CDuiString strName); + void NotifyPump(TNotifyUI& msg); + bool LoopDispatch(TNotifyUI& msg); + DUI_DECLARE_MESSAGE_MAP() +private: + CStdStringPtrMap m_VirtualWndMap; +}; + +class UILIB_API CWindowWnd +{ +public: + CWindowWnd(); + + HWND GetHWND() const; + operator HWND() const; + + bool RegisterWindowClass(); + bool RegisterSuperclass(); + + HWND Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, const RECT rc, HMENU hMenu = NULL); + HWND Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x = CW_USEDEFAULT, int y = CW_USEDEFAULT, int cx = CW_USEDEFAULT, int cy = CW_USEDEFAULT, HMENU hMenu = NULL); + HWND CreateDuiWindow(HWND hwndParent, LPCTSTR pstrWindowName,DWORD dwStyle =0, DWORD dwExStyle =0); + HWND Subclass(HWND hWnd); + void Unsubclass(); + void ShowWindow(bool bShow = true, bool bTakeFocus = true); + UINT ShowModal(); + void Close(UINT nRet = IDOK); + void CenterWindow(); // У֧չĻ + void SetIcon(UINT nRes); + + LRESULT SendMessage(UINT uMsg, WPARAM wParam = 0, LPARAM lParam = 0L); + LRESULT PostMessage(UINT uMsg, WPARAM wParam = 0, LPARAM lParam = 0L); + void ResizeClient(int cx = -1, int cy = -1); + +protected: + virtual LPCTSTR GetWindowClassName() const = 0; + virtual LPCTSTR GetSuperClassName() const; + virtual UINT GetClassStyle() const; + + virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual void OnFinalMessage(HWND hWnd); + + static LRESULT CALLBACK __WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + static LRESULT CALLBACK __ControlProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +protected: + HWND m_hWnd; + WNDPROC m_OldWndProc; + bool m_bSubclassed; +}; + +} // namespace DuiLib + +#endif // __UIBASE_H__ diff --git a/DuiLib/Core/UIContainer.cpp b/DuiLib/Core/UIContainer.cpp new file mode 100644 index 00000000..5761a62b --- /dev/null +++ b/DuiLib/Core/UIContainer.cpp @@ -0,0 +1,953 @@ +#include "StdAfx.h" + +namespace DuiLib +{ + + ///////////////////////////////////////////////////////////////////////////////////// + // + // + + CContainerUI::CContainerUI() + : m_iChildPadding(0), + m_bAutoDestroy(true), + m_bDelayedDestroy(true), + m_bMouseChildEnabled(true), + m_pVerticalScrollBar(NULL), + m_pHorizontalScrollBar(NULL), + m_bScrollProcess(false), + m_nScrollStepSize(0) + { + ::ZeroMemory(&m_rcInset, sizeof(m_rcInset)); + } + + CContainerUI::~CContainerUI() + { + m_bDelayedDestroy = false; + RemoveAll(); + if( m_pVerticalScrollBar ) delete m_pVerticalScrollBar; + if( m_pHorizontalScrollBar ) delete m_pHorizontalScrollBar; + } + + LPCTSTR CContainerUI::GetClass() const + { + return _T("ContainerUI"); + } + + LPVOID CContainerUI::GetInterface(LPCTSTR pstrName) + { + if( _tcscmp(pstrName, _T("IContainer")) == 0 ) return static_cast(this); + else if( _tcscmp(pstrName, DUI_CTR_CONTAINER) == 0 ) return static_cast(this); + return CControlUI::GetInterface(pstrName); + } + + CControlUI* CContainerUI::GetItemAt(int iIndex) const + { + if( iIndex < 0 || iIndex >= m_items.GetSize() ) return NULL; + return static_cast(m_items[iIndex]); + } + + int CContainerUI::GetItemIndex(CControlUI* pControl) const + { + for( int it = 0; it < m_items.GetSize(); it++ ) { + if( static_cast(m_items[it]) == pControl ) { + return it; + } + } + + return -1; + } + + bool CContainerUI::SetItemIndex(CControlUI* pControl, int iIndex) + { + for( int it = 0; it < m_items.GetSize(); it++ ) { + if( static_cast(m_items[it]) == pControl ) { + NeedUpdate(); + m_items.Remove(it); + return m_items.InsertAt(iIndex, pControl); + } + } + + return false; + } + + int CContainerUI::GetCount() const + { + return m_items.GetSize(); + } + + bool CContainerUI::Add(CControlUI* pControl) + { + if( pControl == NULL) return false; + + if( m_pManager != NULL ) m_pManager->InitControls(pControl, this); + if( IsVisible() ) NeedUpdate(); + else pControl->SetInternVisible(false); + return m_items.Add(pControl); + } + + bool CContainerUI::AddAt(CControlUI* pControl, int iIndex) + { + if( pControl == NULL) return false; + + if( m_pManager != NULL ) m_pManager->InitControls(pControl, this); + if( IsVisible() ) NeedUpdate(); + else pControl->SetInternVisible(false); + return m_items.InsertAt(iIndex, pControl); + } + + bool CContainerUI::Remove(CControlUI* pControl) + { + if( pControl == NULL) return false; + + for( int it = 0; it < m_items.GetSize(); it++ ) { + if( static_cast(m_items[it]) == pControl ) { + NeedUpdate(); + if( m_bAutoDestroy ) { + if( m_bDelayedDestroy && m_pManager ) m_pManager->AddDelayedCleanup(pControl); + else delete pControl; + } + return m_items.Remove(it); + } + } + return false; + } + + bool CContainerUI::RemoveAt(int iIndex) + { + CControlUI* pControl = GetItemAt(iIndex); + if (pControl != NULL) { + return CContainerUI::Remove(pControl); + } + + return false; + } + + void CContainerUI::RemoveAll() + { + for( int it = 0; m_bAutoDestroy && it < m_items.GetSize(); it++ ) { + if( m_bDelayedDestroy && m_pManager ) m_pManager->AddDelayedCleanup(static_cast(m_items[it])); + else delete static_cast(m_items[it]); + } + m_items.Empty(); + NeedUpdate(); + } + + bool CContainerUI::IsAutoDestroy() const + { + return m_bAutoDestroy; + } + + void CContainerUI::SetAutoDestroy(bool bAuto) + { + m_bAutoDestroy = bAuto; + } + + bool CContainerUI::IsDelayedDestroy() const + { + return m_bDelayedDestroy; + } + + void CContainerUI::SetDelayedDestroy(bool bDelayed) + { + m_bDelayedDestroy = bDelayed; + } + + RECT CContainerUI::GetInset() const + { + return m_rcInset; + } + + void CContainerUI::SetInset(RECT rcInset) + { + m_rcInset = rcInset; + NeedUpdate(); + } + + int CContainerUI::GetChildPadding() const + { + return m_iChildPadding; + } + + void CContainerUI::SetChildPadding(int iPadding) + { + m_iChildPadding = iPadding; + NeedUpdate(); + } + + bool CContainerUI::IsMouseChildEnabled() const + { + return m_bMouseChildEnabled; + } + + void CContainerUI::SetMouseChildEnabled(bool bEnable) + { + m_bMouseChildEnabled = bEnable; + } + + void CContainerUI::SetVisible(bool bVisible) + { + if( m_bVisible == bVisible ) return; + CControlUI::SetVisible(bVisible); + for( int it = 0; it < m_items.GetSize(); it++ ) { + static_cast(m_items[it])->SetInternVisible(IsVisible()); + } + } + + // ߼ϣContainerؼ˷ + // ô˷ĽǣڲӿؼأؼȻʾЧ + void CContainerUI::SetInternVisible(bool bVisible) + { + CControlUI::SetInternVisible(bVisible); + if( m_items.IsEmpty() ) return; + for( int it = 0; it < m_items.GetSize(); it++ ) { + // ӿؼʾ״̬ + // InternVisible״̬ӦӿؼԼ + static_cast(m_items[it])->SetInternVisible(IsVisible()); + } + } + + void CContainerUI::SetEnabled(bool bEnabled) + { + if( m_bEnabled == bEnabled ) return; + + m_bEnabled = bEnabled; + + for( int it = 0; it < m_items.GetSize(); it++ ) { + static_cast(m_items[it])->SetEnabled(m_bEnabled); + } + + Invalidate(); + } + + void CContainerUI::SetMouseEnabled(bool bEnabled) + { + if( m_pVerticalScrollBar != NULL ) m_pVerticalScrollBar->SetMouseEnabled(bEnabled); + if( m_pHorizontalScrollBar != NULL ) m_pHorizontalScrollBar->SetMouseEnabled(bEnabled); + CControlUI::SetMouseEnabled(bEnabled); + } + + void CContainerUI::DoEvent(TEventUI& event) + { + if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) { + if( m_pParent != NULL ) m_pParent->DoEvent(event); + else CControlUI::DoEvent(event); + return; + } + + if( event.Type == UIEVENT_SETFOCUS ) + { + m_bFocused = true; + return; + } + if( event.Type == UIEVENT_KILLFOCUS ) + { + m_bFocused = false; + return; + } + if( m_pVerticalScrollBar != NULL && m_pVerticalScrollBar->IsVisible() && m_pVerticalScrollBar->IsEnabled() ) + { + if( event.Type == UIEVENT_KEYDOWN ) + { + switch( event.chKey ) { + case VK_DOWN: + LineDown(); + return; + case VK_UP: + LineUp(); + return; + case VK_NEXT: + PageDown(); + return; + case VK_PRIOR: + PageUp(); + return; + case VK_HOME: + HomeUp(); + return; + case VK_END: + EndDown(); + return; + } + } + else if( event.Type == UIEVENT_SCROLLWHEEL ) + { + switch( LOWORD(event.wParam) ) { + case SB_LINEUP: + LineUp(); + return; + case SB_LINEDOWN: + LineDown(); + return; + } + } + } + if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() && m_pHorizontalScrollBar->IsEnabled() ) { + if( event.Type == UIEVENT_KEYDOWN ) + { + switch( event.chKey ) { + case VK_DOWN: + LineRight(); + return; + case VK_UP: + LineLeft(); + return; + case VK_NEXT: + PageRight(); + return; + case VK_PRIOR: + PageLeft(); + return; + case VK_HOME: + HomeLeft(); + return; + case VK_END: + EndRight(); + return; + } + } + else if( event.Type == UIEVENT_SCROLLWHEEL ) + { + switch( LOWORD(event.wParam) ) { + case SB_LINEUP: + LineLeft(); + return; + case SB_LINEDOWN: + LineRight(); + return; + } + } + } + CControlUI::DoEvent(event); + } + + SIZE CContainerUI::GetScrollPos() const + { + SIZE sz = {0, 0}; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) sz.cy = m_pVerticalScrollBar->GetScrollPos(); + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) sz.cx = m_pHorizontalScrollBar->GetScrollPos(); + return sz; + } + + SIZE CContainerUI::GetScrollRange() const + { + SIZE sz = {0, 0}; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) sz.cy = m_pVerticalScrollBar->GetScrollRange(); + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) sz.cx = m_pHorizontalScrollBar->GetScrollRange(); + return sz; + } + + void CContainerUI::SetScrollPos(SIZE szPos, bool bMsg) + { + int cx = 0; + int cy = 0; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) { + int iLastScrollPos = m_pVerticalScrollBar->GetScrollPos(); + m_pVerticalScrollBar->SetScrollPos(szPos.cy); + cy = m_pVerticalScrollBar->GetScrollPos() - iLastScrollPos; + } + + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) { + int iLastScrollPos = m_pHorizontalScrollBar->GetScrollPos(); + m_pHorizontalScrollBar->SetScrollPos(szPos.cx); + cx = m_pHorizontalScrollBar->GetScrollPos() - iLastScrollPos; + } + + if( cx == 0 && cy == 0 ) return; + + RECT rcPos; + for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) { + CControlUI* pControl = static_cast(m_items[it2]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) continue; + + rcPos = pControl->GetPos(); + rcPos.left -= cx; + rcPos.right -= cx; + rcPos.top -= cy; + rcPos.bottom -= cy; + pControl->SetPos(rcPos); + } + + Invalidate(); + + // ͹Ϣ + if( m_pManager != NULL && bMsg ) { + int nPage = (m_pVerticalScrollBar->GetScrollPos() + m_pVerticalScrollBar->GetLineSize()) / m_pVerticalScrollBar->GetLineSize(); + m_pManager->SendNotify(this, DUI_MSGTYPE_SCROLL, (WPARAM)nPage); + } + } + + void CContainerUI::SetScrollStepSize(int nSize) + { + if (nSize >0) + m_nScrollStepSize = nSize; + } + + int CContainerUI::GetScrollStepSize() const + { + return m_nScrollStepSize; + } + + void CContainerUI::LineUp() + { + int cyLine = m_nScrollStepSize; + if (m_nScrollStepSize == 0) + { + cyLine = 8; + if( m_pManager ) cyLine = m_pManager->GetDefaultFontInfo()->tm.tmHeight + 8; + } + + SIZE sz = GetScrollPos(); + sz.cy -= cyLine; + SetScrollPos(sz); + } + + void CContainerUI::LineDown() + { + int cyLine = m_nScrollStepSize; + if (m_nScrollStepSize == 0) + { + cyLine = 8; + if( m_pManager ) cyLine = m_pManager->GetDefaultFontInfo()->tm.tmHeight + 8; + } + + SIZE sz = GetScrollPos(); + sz.cy += cyLine; + SetScrollPos(sz); + } + + void CContainerUI::PageUp() + { + SIZE sz = GetScrollPos(); + int iOffset = m_rcItem.bottom - m_rcItem.top - m_rcInset.top - m_rcInset.bottom; + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) iOffset -= m_pHorizontalScrollBar->GetFixedHeight(); + sz.cy -= iOffset; + SetScrollPos(sz); + } + + void CContainerUI::PageDown() + { + SIZE sz = GetScrollPos(); + int iOffset = m_rcItem.bottom - m_rcItem.top - m_rcInset.top - m_rcInset.bottom; + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) iOffset -= m_pHorizontalScrollBar->GetFixedHeight(); + sz.cy += iOffset; + SetScrollPos(sz); + } + + void CContainerUI::HomeUp() + { + SIZE sz = GetScrollPos(); + sz.cy = 0; + SetScrollPos(sz); + } + + void CContainerUI::EndDown() + { + SIZE sz = GetScrollPos(); + sz.cy = GetScrollRange().cy; + SetScrollPos(sz); + } + + void CContainerUI::LineLeft() + { + int cxLine = m_nScrollStepSize == 0 ? 8 : m_nScrollStepSize; + + SIZE sz = GetScrollPos(); + sz.cx -= cxLine; + SetScrollPos(sz); + } + + void CContainerUI::LineRight() + { + int cxLine = m_nScrollStepSize == 0 ? 8 : m_nScrollStepSize; + + SIZE sz = GetScrollPos(); + sz.cx += cxLine; + SetScrollPos(sz); + } + + void CContainerUI::PageLeft() + { + SIZE sz = GetScrollPos(); + int iOffset = m_rcItem.right - m_rcItem.left - m_rcInset.left - m_rcInset.right; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) iOffset -= m_pVerticalScrollBar->GetFixedWidth(); + sz.cx -= iOffset; + SetScrollPos(sz); + } + + void CContainerUI::PageRight() + { + SIZE sz = GetScrollPos(); + int iOffset = m_rcItem.right - m_rcItem.left - m_rcInset.left - m_rcInset.right; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) iOffset -= m_pVerticalScrollBar->GetFixedWidth(); + sz.cx += iOffset; + SetScrollPos(sz); + } + + void CContainerUI::HomeLeft() + { + SIZE sz = GetScrollPos(); + sz.cx = 0; + SetScrollPos(sz); + } + + void CContainerUI::EndRight() + { + SIZE sz = GetScrollPos(); + sz.cx = GetScrollRange().cx; + SetScrollPos(sz); + } + + void CContainerUI::EnableScrollBar(bool bEnableVertical, bool bEnableHorizontal) + { + if( bEnableVertical && !m_pVerticalScrollBar ) { + m_pVerticalScrollBar = new CScrollBarUI; + m_pVerticalScrollBar->SetOwner(this); + m_pVerticalScrollBar->SetManager(m_pManager, NULL, false); + if ( m_pManager ) { + LPCTSTR pDefaultAttributes = m_pManager->GetDefaultAttributeList(_T("VScrollBar")); + if( pDefaultAttributes ) { + m_pVerticalScrollBar->ApplyAttributeList(pDefaultAttributes); + } + } + } + else if( !bEnableVertical && m_pVerticalScrollBar ) { + delete m_pVerticalScrollBar; + m_pVerticalScrollBar = NULL; + } + + if( bEnableHorizontal && !m_pHorizontalScrollBar ) { + m_pHorizontalScrollBar = new CScrollBarUI; + m_pHorizontalScrollBar->SetHorizontal(true); + m_pHorizontalScrollBar->SetOwner(this); + m_pHorizontalScrollBar->SetManager(m_pManager, NULL, false); + + if ( m_pManager ) { + LPCTSTR pDefaultAttributes = m_pManager->GetDefaultAttributeList(_T("HScrollBar")); + if( pDefaultAttributes ) { + m_pHorizontalScrollBar->ApplyAttributeList(pDefaultAttributes); + } + } + } + else if( !bEnableHorizontal && m_pHorizontalScrollBar ) { + delete m_pHorizontalScrollBar; + m_pHorizontalScrollBar = NULL; + } + + NeedUpdate(); + } + + CScrollBarUI* CContainerUI::GetVerticalScrollBar() const + { + return m_pVerticalScrollBar; + } + + CScrollBarUI* CContainerUI::GetHorizontalScrollBar() const + { + return m_pHorizontalScrollBar; + } + + int CContainerUI::FindSelectable(int iIndex, bool bForward /*= true*/) const + { + // NOTE: This is actually a helper-function for the list/combo/ect controls + // that allow them to find the next enabled/available selectable item + if( GetCount() == 0 ) return -1; + iIndex = CLAMP(iIndex, 0, GetCount() - 1); + if( bForward ) { + for( int i = iIndex; i < GetCount(); i++ ) { + if( GetItemAt(i)->GetInterface(_T("ListItem")) != NULL + && GetItemAt(i)->IsVisible() + && GetItemAt(i)->IsEnabled() ) return i; + } + return -1; + } + else { + for( int i = iIndex; i >= 0; --i ) { + if( GetItemAt(i)->GetInterface(_T("ListItem")) != NULL + && GetItemAt(i)->IsVisible() + && GetItemAt(i)->IsEnabled() ) return i; + } + return FindSelectable(0, true); + } + } + + void CContainerUI::SetPos(RECT rc) + { + CControlUI::SetPos(rc); + if( m_items.IsEmpty() ) return; + rc.left += m_rcInset.left; + rc.top += m_rcInset.top; + rc.right -= m_rcInset.right; + rc.bottom -= m_rcInset.bottom; + + for( int it = 0; it < m_items.GetSize(); it++ ) { + CControlUI* pControl = static_cast(m_items[it]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) { + SetFloatPos(it); + } + else { + pControl->SetPos(rc); // зfloatӿؼŴͻ + } + } + } + + void CContainerUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if( _tcscmp(pstrName, _T("inset")) == 0 ) { + RECT rcInset = { 0 }; + LPTSTR pstr = NULL; + rcInset.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + rcInset.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcInset.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcInset.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetInset(rcInset); + } + else if( _tcscmp(pstrName, _T("mousechild")) == 0 ) SetMouseChildEnabled(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("vscrollbar")) == 0 ) { + EnableScrollBar(_tcscmp(pstrValue, _T("true")) == 0, GetHorizontalScrollBar() != NULL); + } + else if( _tcscmp(pstrName, _T("vscrollbarstyle")) == 0 ) { + EnableScrollBar(true, GetHorizontalScrollBar() != NULL); + if( GetVerticalScrollBar() ) GetVerticalScrollBar()->ApplyAttributeList(pstrValue); + } + else if( _tcscmp(pstrName, _T("hscrollbar")) == 0 ) { + EnableScrollBar(GetVerticalScrollBar() != NULL, _tcscmp(pstrValue, _T("true")) == 0); + } + else if( _tcscmp(pstrName, _T("hscrollbarstyle")) == 0 ) { + EnableScrollBar(GetVerticalScrollBar() != NULL, true); + if( GetHorizontalScrollBar() ) GetHorizontalScrollBar()->ApplyAttributeList(pstrValue); + } + else if( _tcscmp(pstrName, _T("childpadding")) == 0 ) SetChildPadding(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("scrollstepsize")) == 0 ) SetScrollStepSize(_ttoi(pstrValue)); + else CControlUI::SetAttribute(pstrName, pstrValue); + } + + void CContainerUI::SetManager(CPaintManagerUI* pManager, CControlUI* pParent, bool bInit) + { + for( int it = 0; it < m_items.GetSize(); it++ ) { + static_cast(m_items[it])->SetManager(pManager, this, bInit); + } + + if( m_pVerticalScrollBar != NULL ) m_pVerticalScrollBar->SetManager(pManager, this, bInit); + if( m_pHorizontalScrollBar != NULL ) m_pHorizontalScrollBar->SetManager(pManager, this, bInit); + CControlUI::SetManager(pManager, pParent, bInit); + } + + CControlUI* CContainerUI::FindControl(FINDCONTROLPROC Proc, LPVOID pData, UINT uFlags) + { + // Check if this guy is valid + if( (uFlags & UIFIND_VISIBLE) != 0 && !IsVisible() ) return NULL; + if( (uFlags & UIFIND_ENABLED) != 0 && !IsEnabled() ) return NULL; + if( (uFlags & UIFIND_HITTEST) != 0 ) { + if( !::PtInRect(&m_rcItem, *(static_cast(pData))) ) return NULL; + if( !m_bMouseChildEnabled ) { + CControlUI* pResult = NULL; + if( m_pVerticalScrollBar != NULL ) pResult = m_pVerticalScrollBar->FindControl(Proc, pData, uFlags); + if( pResult == NULL && m_pHorizontalScrollBar != NULL ) pResult = m_pHorizontalScrollBar->FindControl(Proc, pData, uFlags); + if( pResult == NULL ) pResult = CControlUI::FindControl(Proc, pData, uFlags); + return pResult; + } + } + + CControlUI* pResult = NULL; + if( m_pVerticalScrollBar != NULL ) pResult = m_pVerticalScrollBar->FindControl(Proc, pData, uFlags); + if( pResult == NULL && m_pHorizontalScrollBar != NULL ) pResult = m_pHorizontalScrollBar->FindControl(Proc, pData, uFlags); + if( pResult != NULL ) return pResult; + + if( (uFlags & UIFIND_ME_FIRST) != 0 ) { + CControlUI* pControl = CControlUI::FindControl(Proc, pData, uFlags); + if( pControl != NULL ) return pControl; + } + RECT rc = m_rcItem; + rc.left += m_rcInset.left; + rc.top += m_rcInset.top; + rc.right -= m_rcInset.right; + rc.bottom -= m_rcInset.bottom; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth(); + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight(); + if( (uFlags & UIFIND_TOP_FIRST) != 0 ) { + for( int it = m_items.GetSize() - 1; it >= 0; it-- ) { + CControlUI* pControl = static_cast(m_items[it])->FindControl(Proc, pData, uFlags); + if( pControl != NULL ) { + if( (uFlags & UIFIND_HITTEST) != 0 && !pControl->IsFloat() && !::PtInRect(&rc, *(static_cast(pData))) ) + continue; + else + return pControl; + } + } + } + else { + for( int it = 0; it < m_items.GetSize(); it++ ) { + CControlUI* pControl = static_cast(m_items[it])->FindControl(Proc, pData, uFlags); + if( pControl != NULL ) { + if( (uFlags & UIFIND_HITTEST) != 0 && !pControl->IsFloat() && !::PtInRect(&rc, *(static_cast(pData))) ) + continue; + else + return pControl; + } + } + } + + if( pResult == NULL && (uFlags & UIFIND_ME_FIRST) == 0 ) pResult = CControlUI::FindControl(Proc, pData, uFlags); + return pResult; + } + + void CContainerUI::DoPaint(HDC hDC, const RECT& rcPaint) + { + RECT rcTemp = { 0 }; + if( !::IntersectRect(&rcTemp, &rcPaint, &m_rcItem) ) return; + + CRenderClip clip; + CRenderClip::GenerateClip(hDC, rcTemp, clip); + CControlUI::DoPaint(hDC, rcPaint); + + if( m_items.GetSize() > 0 ) { + RECT rc = m_rcItem; + rc.left += m_rcInset.left; + rc.top += m_rcInset.top; + rc.right -= m_rcInset.right; + rc.bottom -= m_rcInset.bottom; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth(); + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight(); + + if( !::IntersectRect(&rcTemp, &rcPaint, &rc) ) { + for( int it = 0; it < m_items.GetSize(); it++ ) { + CControlUI* pControl = static_cast(m_items[it]); + if( !pControl->IsVisible() ) continue; + if( !::IntersectRect(&rcTemp, &rcPaint, &pControl->GetPos()) ) continue; + if( pControl ->IsFloat() ) { + if( !::IntersectRect(&rcTemp, &m_rcItem, &pControl->GetPos()) ) continue; + pControl->DoPaint(hDC, rcPaint); + } + } + } + else { + CRenderClip childClip; + CRenderClip::GenerateClip(hDC, rcTemp, childClip); + for( int it = 0; it < m_items.GetSize(); it++ ) { + CControlUI* pControl = static_cast(m_items[it]); + if( !pControl->IsVisible() ) continue; + if( !::IntersectRect(&rcTemp, &rcPaint, &pControl->GetPos()) ) continue; + if( pControl ->IsFloat() ) { + if( !::IntersectRect(&rcTemp, &m_rcItem, &pControl->GetPos()) ) continue; + CRenderClip::UseOldClipBegin(hDC, childClip); + pControl->DoPaint(hDC, rcPaint); + CRenderClip::UseOldClipEnd(hDC, childClip); + } + else { + if( !::IntersectRect(&rcTemp, &rc, &pControl->GetPos()) ) continue; + pControl->DoPaint(hDC, rcPaint); + } + } + } + } + + if( m_pVerticalScrollBar != NULL && m_pVerticalScrollBar->IsVisible() ) { + if( ::IntersectRect(&rcTemp, &rcPaint, &m_pVerticalScrollBar->GetPos()) ) { + m_pVerticalScrollBar->DoPaint(hDC, rcPaint); + } + } + + if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() ) { + if( ::IntersectRect(&rcTemp, &rcPaint, &m_pHorizontalScrollBar->GetPos()) ) { + m_pHorizontalScrollBar->DoPaint(hDC, rcPaint); + } + } + } + + void CContainerUI::SetFloatPos(int iIndex) + { + // ΪCControlUI::SetPosfloatIJӰ죬ﲻܶfloatӹӰ + if( iIndex < 0 || iIndex >= m_items.GetSize() ) return; + + CControlUI* pControl = static_cast(m_items[iIndex]); + + if( !pControl->IsVisible() ) return; + if( !pControl->IsFloat() ) return; + + SIZE szXY = pControl->GetFixedXY(); + SIZE sz = {pControl->GetFixedWidth(), pControl->GetFixedHeight()}; + RECT rcCtrl = { 0 }; + if( szXY.cx >= 0 ) { + rcCtrl.left = m_rcItem.left + szXY.cx; + rcCtrl.right = m_rcItem.left + szXY.cx + sz.cx; + } + else { + rcCtrl.left = m_rcItem.right + szXY.cx - sz.cx; + rcCtrl.right = m_rcItem.right + szXY.cx; + } + if( szXY.cy >= 0 ) { + rcCtrl.top = m_rcItem.top + szXY.cy; + rcCtrl.bottom = m_rcItem.top + szXY.cy + sz.cy; + } + else { + rcCtrl.top = m_rcItem.bottom + szXY.cy - sz.cy; + rcCtrl.bottom = m_rcItem.bottom + szXY.cy; + } + if( pControl->IsRelativePos() ) + { + TRelativePosUI tRelativePos = pControl->GetRelativePos(); + SIZE szParent = {m_rcItem.right-m_rcItem.left,m_rcItem.bottom-m_rcItem.top}; + if(tRelativePos.szParent.cx != 0) + { + int nIncrementX = szParent.cx-tRelativePos.szParent.cx; + int nIncrementY = szParent.cy-tRelativePos.szParent.cy; + rcCtrl.left += (nIncrementX*tRelativePos.nMoveXPercent/100); + rcCtrl.top += (nIncrementY*tRelativePos.nMoveYPercent/100); + rcCtrl.right = rcCtrl.left+sz.cx+(nIncrementX*tRelativePos.nZoomXPercent/100); + rcCtrl.bottom = rcCtrl.top+sz.cy+(nIncrementY*tRelativePos.nZoomYPercent/100); + } + pControl->SetRelativeParentSize(szParent); + } + pControl->SetPos(rcCtrl); + } + + void CContainerUI::ProcessScrollBar(RECT rc, int cxRequired, int cyRequired) + { + if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() ) { + RECT rcScrollBarPos = { rc.left, rc.bottom, rc.right, rc.bottom + m_pHorizontalScrollBar->GetFixedHeight()}; + m_pHorizontalScrollBar->SetPos(rcScrollBarPos); + } + + if( m_pVerticalScrollBar == NULL ) return; + + if( cyRequired > rc.bottom - rc.top && !m_pVerticalScrollBar->IsVisible() ) { + m_pVerticalScrollBar->SetVisible(true); + m_pVerticalScrollBar->SetScrollRange(cyRequired - (rc.bottom - rc.top)); + m_pVerticalScrollBar->SetScrollPos(0); + m_bScrollProcess = true; + SetPos(m_rcItem); + m_bScrollProcess = false; + return; + } + // No scrollbar required + if( !m_pVerticalScrollBar->IsVisible() ) return; + + // Scroll not needed anymore? + int cyScroll = cyRequired - (rc.bottom - rc.top); + if( cyScroll <= 0 && !m_bScrollProcess) { + m_pVerticalScrollBar->SetVisible(false); + m_pVerticalScrollBar->SetScrollPos(0); + m_pVerticalScrollBar->SetScrollRange(0); + SetPos(m_rcItem); + } + else + { + RECT rcScrollBarPos = { rc.right, rc.top, rc.right + m_pVerticalScrollBar->GetFixedWidth(), rc.bottom }; + m_pVerticalScrollBar->SetPos(rcScrollBarPos); + + if( m_pVerticalScrollBar->GetScrollRange() != cyScroll ) { + int iScrollPos = m_pVerticalScrollBar->GetScrollPos(); + m_pVerticalScrollBar->SetScrollRange(::abs(cyScroll)); + if( m_pVerticalScrollBar->GetScrollRange() == 0 ) { + m_pVerticalScrollBar->SetVisible(false); + m_pVerticalScrollBar->SetScrollPos(0); + } + if( iScrollPos > m_pVerticalScrollBar->GetScrollPos() ) { + SetPos(m_rcItem); + } + } + } + } + + bool CContainerUI::SetSubControlText( LPCTSTR pstrSubControlName,LPCTSTR pstrText ) + { + CControlUI* pSubControl=NULL; + pSubControl=this->FindSubControl(pstrSubControlName); + if (pSubControl!=NULL) + { + pSubControl->SetText(pstrText); + return TRUE; + } + else + return FALSE; + } + + bool CContainerUI::SetSubControlFixedHeight( LPCTSTR pstrSubControlName,int cy ) + { + CControlUI* pSubControl=NULL; + pSubControl=this->FindSubControl(pstrSubControlName); + if (pSubControl!=NULL) + { + pSubControl->SetFixedHeight(cy); + return TRUE; + } + else + return FALSE; + } + + bool CContainerUI::SetSubControlFixedWdith( LPCTSTR pstrSubControlName,int cx ) + { + CControlUI* pSubControl=NULL; + pSubControl=this->FindSubControl(pstrSubControlName); + if (pSubControl!=NULL) + { + pSubControl->SetFixedWidth(cx); + return TRUE; + } + else + return FALSE; + } + + bool CContainerUI::SetSubControlUserData( LPCTSTR pstrSubControlName,LPCTSTR pstrText ) + { + CControlUI* pSubControl=NULL; + pSubControl=this->FindSubControl(pstrSubControlName); + if (pSubControl!=NULL) + { + pSubControl->SetUserData(pstrText); + return TRUE; + } + else + return FALSE; + } + + DuiLib::CDuiString CContainerUI::GetSubControlText( LPCTSTR pstrSubControlName ) + { + CControlUI* pSubControl=NULL; + pSubControl=this->FindSubControl(pstrSubControlName); + if (pSubControl==NULL) + return _T(""); + else + return pSubControl->GetText(); + } + + int CContainerUI::GetSubControlFixedHeight( LPCTSTR pstrSubControlName ) + { + CControlUI* pSubControl=NULL; + pSubControl=this->FindSubControl(pstrSubControlName); + if (pSubControl==NULL) + return -1; + else + return pSubControl->GetFixedHeight(); + } + + int CContainerUI::GetSubControlFixedWdith( LPCTSTR pstrSubControlName ) + { + CControlUI* pSubControl=NULL; + pSubControl=this->FindSubControl(pstrSubControlName); + if (pSubControl==NULL) + return -1; + else + return pSubControl->GetFixedWidth(); + } + + const CDuiString CContainerUI::GetSubControlUserData( LPCTSTR pstrSubControlName ) + { + CControlUI* pSubControl=NULL; + pSubControl=this->FindSubControl(pstrSubControlName); + if (pSubControl==NULL) + return _T(""); + else + return pSubControl->GetUserData(); + } + + CControlUI* CContainerUI::FindSubControl( LPCTSTR pstrSubControlName ) + { + CControlUI* pSubControl=NULL; + pSubControl=static_cast(GetManager()->FindSubControlByName(this,pstrSubControlName)); + return pSubControl; + } + +} // namespace DuiLib diff --git a/DuiLib/Core/UIContainer.h b/DuiLib/Core/UIContainer.h new file mode 100644 index 00000000..211b800d --- /dev/null +++ b/DuiLib/Core/UIContainer.h @@ -0,0 +1,128 @@ +#ifndef __UICONTAINER_H__ +#define __UICONTAINER_H__ + +#pragma once + +namespace DuiLib { +///////////////////////////////////////////////////////////////////////////////////// +// + +class IContainerUI +{ +public: + virtual CControlUI* GetItemAt(int iIndex) const = 0; + virtual int GetItemIndex(CControlUI* pControl) const = 0; + virtual bool SetItemIndex(CControlUI* pControl, int iIndex) = 0; + virtual int GetCount() const = 0; + virtual bool Add(CControlUI* pControl) = 0; + virtual bool AddAt(CControlUI* pControl, int iIndex) = 0; + virtual bool Remove(CControlUI* pControl) = 0; + virtual bool RemoveAt(int iIndex) = 0; + virtual void RemoveAll() = 0; +}; + + +///////////////////////////////////////////////////////////////////////////////////// +// +class CScrollBarUI; + +class UILIB_API CContainerUI : public CControlUI, public IContainerUI +{ +public: + CContainerUI(); + virtual ~CContainerUI(); + +public: + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + + CControlUI* GetItemAt(int iIndex) const; + int GetItemIndex(CControlUI* pControl) const; + bool SetItemIndex(CControlUI* pControl, int iIndex); + int GetCount() const; + bool Add(CControlUI* pControl); + bool AddAt(CControlUI* pControl, int iIndex); + bool Remove(CControlUI* pControl); + bool RemoveAt(int iIndex); + void RemoveAll(); + + void DoEvent(TEventUI& event); + void SetVisible(bool bVisible = true); + void SetInternVisible(bool bVisible = true); + void SetEnabled(bool bEnabled); + void SetMouseEnabled(bool bEnable = true); + + virtual RECT GetInset() const; + virtual void SetInset(RECT rcInset); // ڱ߾࣬൱ÿͻ + virtual int GetChildPadding() const; + virtual void SetChildPadding(int iPadding); + virtual bool IsAutoDestroy() const; + virtual void SetAutoDestroy(bool bAuto); + virtual bool IsDelayedDestroy() const; + virtual void SetDelayedDestroy(bool bDelayed); + virtual bool IsMouseChildEnabled() const; + virtual void SetMouseChildEnabled(bool bEnable = true); + + virtual int FindSelectable(int iIndex, bool bForward = true) const; + + void SetPos(RECT rc); + void DoPaint(HDC hDC, const RECT& rcPaint); + + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + void SetManager(CPaintManagerUI* pManager, CControlUI* pParent, bool bInit = true); + CControlUI* FindControl(FINDCONTROLPROC Proc, LPVOID pData, UINT uFlags); + + bool SetSubControlText(LPCTSTR pstrSubControlName,LPCTSTR pstrText); + bool SetSubControlFixedHeight(LPCTSTR pstrSubControlName,int cy); + bool SetSubControlFixedWdith(LPCTSTR pstrSubControlName,int cx); + bool SetSubControlUserData(LPCTSTR pstrSubControlName,LPCTSTR pstrText); + + CDuiString GetSubControlText(LPCTSTR pstrSubControlName); + int GetSubControlFixedHeight(LPCTSTR pstrSubControlName); + int GetSubControlFixedWdith(LPCTSTR pstrSubControlName); + const CDuiString GetSubControlUserData(LPCTSTR pstrSubControlName); + CControlUI* FindSubControl(LPCTSTR pstrSubControlName); + + virtual SIZE GetScrollPos() const; + virtual SIZE GetScrollRange() const; + virtual void SetScrollPos(SIZE szPos, bool bMsg = true); + virtual void SetScrollStepSize(int nSize); + virtual int GetScrollStepSize() const; + virtual void LineUp(); + virtual void LineDown(); + virtual void PageUp(); + virtual void PageDown(); + virtual void HomeUp(); + virtual void EndDown(); + virtual void LineLeft(); + virtual void LineRight(); + virtual void PageLeft(); + virtual void PageRight(); + virtual void HomeLeft(); + virtual void EndRight(); + virtual void EnableScrollBar(bool bEnableVertical = true, bool bEnableHorizontal = false); + virtual CScrollBarUI* GetVerticalScrollBar() const; + virtual CScrollBarUI* GetHorizontalScrollBar() const; + +protected: + virtual void SetFloatPos(int iIndex); + virtual void ProcessScrollBar(RECT rc, int cxRequired, int cyRequired); + +protected: + CStdPtrArray m_items; + RECT m_rcInset; + int m_iChildPadding; + bool m_bAutoDestroy; + bool m_bDelayedDestroy; + bool m_bMouseChildEnabled; + bool m_bScrollProcess; // ֹSetPosѭ + int m_nScrollStepSize; + + CScrollBarUI* m_pVerticalScrollBar; + CScrollBarUI* m_pHorizontalScrollBar; +}; + +} // namespace DuiLib + +#endif // __UICONTAINER_H__ diff --git a/DuiLib/Core/UIControl.cpp b/DuiLib/Core/UIControl.cpp new file mode 100644 index 00000000..81b9abd2 --- /dev/null +++ b/DuiLib/Core/UIControl.cpp @@ -0,0 +1,1051 @@ +#include "StdAfx.h" + +namespace DuiLib { + +CControlUI::CControlUI() : +m_pManager(NULL), +m_pParent(NULL), +m_bUpdateNeeded(true), +m_bMenuUsed(false), +m_bVisible(true), +m_bInternVisible(true), +m_bFocused(false), +m_bEnabled(true), +m_bMouseEnabled(true), +m_bKeyboardEnabled(true), +m_bFloat(false), +m_bSetPos(false), +m_chShortcut('\0'), +m_pTag(NULL), +m_dwBackColor(0), +m_dwBackColor2(0), +m_dwBackColor3(0), +m_dwBorderColor(0), +m_dwFocusBorderColor(0), +m_bColorHSL(false), +m_nBorderSize(0), +m_nBorderStyle(PS_SOLID), +m_nTooltipWidth(300) +{ + m_cXY.cx = m_cXY.cy = 0; + m_cxyFixed.cx = m_cxyFixed.cy = 0; + m_cxyMin.cx = m_cxyMin.cy = 0; + m_cxyMax.cx = m_cxyMax.cy = 9999; + m_cxyBorderRound.cx = m_cxyBorderRound.cy = 0; + + ::ZeroMemory(&m_rcPadding, sizeof(RECT)); + ::ZeroMemory(&m_rcItem, sizeof(RECT)); + ::ZeroMemory(&m_rcPaint, sizeof(RECT)); + ::ZeroMemory(&m_rcBorderSize,sizeof(RECT)); + ::ZeroMemory(&m_tRelativePos, sizeof(TRelativePosUI)); +} + +CControlUI::~CControlUI() +{ + if( OnDestroy ) OnDestroy(this); + if( m_pManager != NULL ) m_pManager->ReapObjects(this); +} + +CDuiString CControlUI::GetName() const +{ + return m_sName; +} + +void CControlUI::SetName(LPCTSTR pstrName) +{ + m_sName = pstrName; +} + +LPVOID CControlUI::GetInterface(LPCTSTR pstrName) +{ + if( _tcscmp(pstrName, DUI_CTR_CONTROL) == 0 ) return this; + return NULL; +} + +LPCTSTR CControlUI::GetClass() const +{ + return _T("ControlUI"); +} + +UINT CControlUI::GetControlFlags() const +{ + return 0; +} + +bool CControlUI::Activate() +{ + if( !IsVisible() ) return false; + if( !IsEnabled() ) return false; + return true; +} + +CPaintManagerUI* CControlUI::GetManager() const +{ + return m_pManager; +} + +void CControlUI::SetManager(CPaintManagerUI* pManager, CControlUI* pParent, bool bInit) +{ + m_pManager = pManager; + m_pParent = pParent; + if( bInit && m_pParent ) Init(); +} + +CControlUI* CControlUI::GetParent() const +{ + return m_pParent; +} + +CDuiString CControlUI::GetText() const +{ + return m_sText; +} + +void CControlUI::SetText(LPCTSTR pstrText) +{ + if( m_sText == pstrText ) return; + + m_sText = pstrText; + Invalidate(); +} + +DWORD CControlUI::GetBkColor() const +{ + return m_dwBackColor; +} + +void CControlUI::SetBkColor(DWORD dwBackColor) +{ + if( m_dwBackColor == dwBackColor ) return; + + m_dwBackColor = dwBackColor; + Invalidate(); +} + +DWORD CControlUI::GetBkColor2() const +{ + return m_dwBackColor2; +} + +void CControlUI::SetBkColor2(DWORD dwBackColor) +{ + if( m_dwBackColor2 == dwBackColor ) return; + + m_dwBackColor2 = dwBackColor; + Invalidate(); +} + +DWORD CControlUI::GetBkColor3() const +{ + return m_dwBackColor3; +} + +void CControlUI::SetBkColor3(DWORD dwBackColor) +{ + if( m_dwBackColor3 == dwBackColor ) return; + + m_dwBackColor3 = dwBackColor; + Invalidate(); +} + +LPCTSTR CControlUI::GetBkImage() +{ + return m_sBkImage; +} + +void CControlUI::SetBkImage(LPCTSTR pStrImage) +{ + if( m_sBkImage == pStrImage ) return; + + m_sBkImage = pStrImage; + Invalidate(); +} + +DWORD CControlUI::GetBorderColor() const +{ + return m_dwBorderColor; +} + +void CControlUI::SetBorderColor(DWORD dwBorderColor) +{ + if( m_dwBorderColor == dwBorderColor ) return; + + m_dwBorderColor = dwBorderColor; + Invalidate(); +} + +DWORD CControlUI::GetFocusBorderColor() const +{ + return m_dwFocusBorderColor; +} + +void CControlUI::SetFocusBorderColor(DWORD dwBorderColor) +{ + if( m_dwFocusBorderColor == dwBorderColor ) return; + + m_dwFocusBorderColor = dwBorderColor; + Invalidate(); +} + +bool CControlUI::IsColorHSL() const +{ + return m_bColorHSL; +} + +void CControlUI::SetColorHSL(bool bColorHSL) +{ + if( m_bColorHSL == bColorHSL ) return; + + m_bColorHSL = bColorHSL; + Invalidate(); +} + +int CControlUI::GetBorderSize() const +{ + return m_nBorderSize; +} + +void CControlUI::SetBorderSize(int nSize) +{ + if( m_nBorderSize == nSize ) return; + + m_nBorderSize = nSize; + Invalidate(); +} + +void CControlUI::SetBorderSize( RECT rc ) +{ + m_rcBorderSize = rc; + Invalidate(); +} + +SIZE CControlUI::GetBorderRound() const +{ + return m_cxyBorderRound; +} + +void CControlUI::SetBorderRound(SIZE cxyRound) +{ + m_cxyBorderRound = cxyRound; + Invalidate(); +} + +bool CControlUI::DrawImage(HDC hDC, LPCTSTR pStrImage, LPCTSTR pStrModify) +{ + return CRenderEngine::DrawImageString(hDC, m_pManager, m_rcItem, m_rcPaint, pStrImage, pStrModify); +} + +const RECT& CControlUI::GetPos() const +{ + return m_rcItem; +} + +void CControlUI::SetPos(RECT rc) +{ + if( rc.right < rc.left ) rc.right = rc.left; + if( rc.bottom < rc.top ) rc.bottom = rc.top; + + CDuiRect invalidateRc = m_rcItem; + if( ::IsRectEmpty(&invalidateRc) ) invalidateRc = rc; + + m_rcItem = rc; + if( m_pManager == NULL ) return; + + if( !m_bSetPos ) { + m_bSetPos = true; + if( OnSize ) OnSize(this); + m_bSetPos = false; + } + + if( m_bFloat ) { + CControlUI* pParent = GetParent(); + if( pParent != NULL ) { + RECT rcParentPos = pParent->GetPos(); + if( m_cXY.cx >= 0 ) m_cXY.cx = m_rcItem.left - rcParentPos.left; + else m_cXY.cx = m_rcItem.right - rcParentPos.right; + if( m_cXY.cy >= 0 ) m_cXY.cy = m_rcItem.top - rcParentPos.top; + else m_cXY.cy = m_rcItem.bottom - rcParentPos.bottom; + m_cxyFixed.cx = m_rcItem.right - m_rcItem.left; + m_cxyFixed.cy = m_rcItem.bottom - m_rcItem.top; + } + } + + m_bUpdateNeeded = false; + invalidateRc.Join(m_rcItem); + + CControlUI* pParent = this; + RECT rcTemp; + RECT rcParent; + while( pParent = pParent->GetParent() ) + { + rcTemp = invalidateRc; + rcParent = pParent->GetPos(); + if( !::IntersectRect(&invalidateRc, &rcTemp, &rcParent) ) + { + return; + } + } + m_pManager->Invalidate(invalidateRc); +} + +int CControlUI::GetWidth() const +{ + return m_rcItem.right - m_rcItem.left; +} + +int CControlUI::GetHeight() const +{ + return m_rcItem.bottom - m_rcItem.top; +} + +int CControlUI::GetX() const +{ + return m_rcItem.left; +} + +int CControlUI::GetY() const +{ + return m_rcItem.top; +} + +RECT CControlUI::GetPadding() const +{ + return m_rcPadding; +} + +void CControlUI::SetPadding(RECT rcPadding) +{ + m_rcPadding = rcPadding; + NeedParentUpdate(); +} + +SIZE CControlUI::GetFixedXY() const +{ + return m_cXY; +} + +void CControlUI::SetFixedXY(SIZE szXY) +{ + m_cXY.cx = szXY.cx; + m_cXY.cy = szXY.cy; + if( !m_bFloat ) NeedParentUpdate(); + else NeedUpdate(); +} + +int CControlUI::GetFixedWidth() const +{ + return m_cxyFixed.cx; +} + +void CControlUI::SetFixedWidth(int cx) +{ + if( cx < 0 ) return; + m_cxyFixed.cx = cx; + if( !m_bFloat ) NeedParentUpdate(); + else NeedUpdate(); +} + +int CControlUI::GetFixedHeight() const +{ + return m_cxyFixed.cy; +} + +void CControlUI::SetFixedHeight(int cy) +{ + if( cy < 0 ) return; + m_cxyFixed.cy = cy; + if( !m_bFloat ) NeedParentUpdate(); + else NeedUpdate(); +} + +int CControlUI::GetMinWidth() const +{ + return m_cxyMin.cx; +} + +void CControlUI::SetMinWidth(int cx) +{ + if( m_cxyMin.cx == cx ) return; + + if( cx < 0 ) return; + m_cxyMin.cx = cx; + if( !m_bFloat ) NeedParentUpdate(); + else NeedUpdate(); +} + +int CControlUI::GetMaxWidth() const +{ + return m_cxyMax.cx; +} + +void CControlUI::SetMaxWidth(int cx) +{ + if( m_cxyMax.cx == cx ) return; + + if( cx < 0 ) return; + m_cxyMax.cx = cx; + if( !m_bFloat ) NeedParentUpdate(); + else NeedUpdate(); +} + +int CControlUI::GetMinHeight() const +{ + return m_cxyMin.cy; +} + +void CControlUI::SetMinHeight(int cy) +{ + if( m_cxyMin.cy == cy ) return; + + if( cy < 0 ) return; + m_cxyMin.cy = cy; + if( !m_bFloat ) NeedParentUpdate(); + else NeedUpdate(); +} + +int CControlUI::GetMaxHeight() const +{ + return m_cxyMax.cy; +} + +void CControlUI::SetMaxHeight(int cy) +{ + if( m_cxyMax.cy == cy ) return; + + if( cy < 0 ) return; + m_cxyMax.cy = cy; + if( !m_bFloat ) NeedParentUpdate(); + else NeedUpdate(); +} + +void CControlUI::SetRelativePos(SIZE szMove,SIZE szZoom) +{ + m_tRelativePos.bRelative = TRUE; + m_tRelativePos.nMoveXPercent = szMove.cx; + m_tRelativePos.nMoveYPercent = szMove.cy; + m_tRelativePos.nZoomXPercent = szZoom.cx; + m_tRelativePos.nZoomYPercent = szZoom.cy; +} + +void CControlUI::SetRelativeParentSize(SIZE sz) +{ + m_tRelativePos.szParent = sz; +} + +TRelativePosUI CControlUI::GetRelativePos() const +{ + return m_tRelativePos; +} + +bool CControlUI::IsRelativePos() const +{ + return m_tRelativePos.bRelative; +} + +CDuiString CControlUI::GetToolTip() const +{ + return m_sToolTip; +} + +void CControlUI::SetToolTip(LPCTSTR pstrText) +{ + CDuiString strTemp(pstrText); + strTemp.Replace(_T(""),_T("\r\n")); + m_sToolTip=strTemp; +} + +void CControlUI::SetToolTipWidth( int nWidth ) +{ + m_nTooltipWidth=nWidth; +} + +int CControlUI::GetToolTipWidth( void ) +{ + return m_nTooltipWidth; +} + +TCHAR CControlUI::GetShortcut() const +{ + return m_chShortcut; +} + +void CControlUI::SetShortcut(TCHAR ch) +{ + m_chShortcut = ch; +} + +bool CControlUI::IsContextMenuUsed() const +{ + return m_bMenuUsed; +} + +void CControlUI::SetContextMenuUsed(bool bMenuUsed) +{ + m_bMenuUsed = bMenuUsed; +} + +const CDuiString& CControlUI::GetUserData() +{ + return m_sUserData; +} + +void CControlUI::SetUserData(LPCTSTR pstrText) +{ + m_sUserData = pstrText; +} + +UINT_PTR CControlUI::GetTag() const +{ + return m_pTag; +} + +void CControlUI::SetTag(UINT_PTR pTag) +{ + m_pTag = pTag; +} + +bool CControlUI::IsVisible() const +{ + + return m_bVisible && m_bInternVisible; +} + +void CControlUI::SetVisible(bool bVisible) +{ + if( m_bVisible == bVisible ) return; + + bool v = IsVisible(); + m_bVisible = bVisible; + if( m_bFocused ) m_bFocused = false; + if (!bVisible && m_pManager && m_pManager->GetFocus() == this) { + m_pManager->SetFocus(NULL) ; + } + if( IsVisible() != v ) { + NeedParentUpdate(); + } +} + +void CControlUI::SetInternVisible(bool bVisible) +{ + m_bInternVisible = bVisible; + if (!bVisible && m_pManager && m_pManager->GetFocus() == this) { + m_pManager->SetFocus(NULL) ; + } +} + +bool CControlUI::IsEnabled() const +{ + return m_bEnabled; +} + +void CControlUI::SetEnabled(bool bEnabled) +{ + if( m_bEnabled == bEnabled ) return; + + m_bEnabled = bEnabled; + Invalidate(); +} + +bool CControlUI::IsMouseEnabled() const +{ + return m_bMouseEnabled; +} + +void CControlUI::SetMouseEnabled(bool bEnabled) +{ + m_bMouseEnabled = bEnabled; +} + +bool CControlUI::IsKeyboardEnabled() const +{ + return m_bKeyboardEnabled ; +} +void CControlUI::SetKeyboardEnabled(bool bEnabled) +{ + m_bKeyboardEnabled = bEnabled ; +} + +bool CControlUI::IsFocused() const +{ + return m_bFocused; +} + +void CControlUI::SetFocus() +{ + if( m_pManager != NULL ) m_pManager->SetFocus(this); +} + +bool CControlUI::IsFloat() const +{ + return m_bFloat; +} + +void CControlUI::SetFloat(bool bFloat) +{ + if( m_bFloat == bFloat ) return; + + m_bFloat = bFloat; + NeedParentUpdate(); +} + +CControlUI* CControlUI::FindControl(FINDCONTROLPROC Proc, LPVOID pData, UINT uFlags) +{ + if( (uFlags & UIFIND_VISIBLE) != 0 && !IsVisible() ) return NULL; + if( (uFlags & UIFIND_ENABLED) != 0 && !IsEnabled() ) return NULL; + if( (uFlags & UIFIND_HITTEST) != 0 && (!m_bMouseEnabled || !::PtInRect(&m_rcItem, * static_cast(pData))) ) return NULL; + return Proc(this, pData); +} + +void CControlUI::Invalidate() +{ + if( !IsVisible() ) return; + + RECT invalidateRc = m_rcItem; + + CControlUI* pParent = this; + RECT rcTemp; + RECT rcParent; + while( pParent = pParent->GetParent() ) + { + rcTemp = invalidateRc; + rcParent = pParent->GetPos(); + if( !::IntersectRect(&invalidateRc, &rcTemp, &rcParent) ) + { + return; + } + } + + if( m_pManager != NULL ) m_pManager->Invalidate(invalidateRc); +} + +bool CControlUI::IsUpdateNeeded() const +{ + return m_bUpdateNeeded; +} + +void CControlUI::NeedUpdate() +{ + if( !IsVisible() ) return; + m_bUpdateNeeded = true; + Invalidate(); + + if( m_pManager != NULL ) m_pManager->NeedUpdate(); +} + +void CControlUI::NeedParentUpdate() +{ + if( GetParent() ) { + GetParent()->NeedUpdate(); + GetParent()->Invalidate(); + } + else { + NeedUpdate(); + } + + if( m_pManager != NULL ) m_pManager->NeedUpdate(); +} + +DWORD CControlUI::GetAdjustColor(DWORD dwColor) +{ + if( !m_bColorHSL ) return dwColor; + short H, S, L; + CPaintManagerUI::GetHSL(&H, &S, &L); + return CRenderEngine::AdjustColor(dwColor, H, S, L); +} + +void CControlUI::Init() +{ + DoInit(); + if( OnInit ) OnInit(this); +} + +void CControlUI::DoInit() +{ + +} + +void CControlUI::Event(TEventUI& event) +{ + if( OnEvent(&event) ) DoEvent(event); +} + +void CControlUI::DoEvent(TEventUI& event) +{ + if( event.Type == UIEVENT_SETCURSOR ) + { + ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); + return; + } + if( event.Type == UIEVENT_SETFOCUS ) + { + m_bFocused = true; + Invalidate(); + return; + } + if( event.Type == UIEVENT_KILLFOCUS ) + { + m_bFocused = false; + Invalidate(); + return; + } + if( event.Type == UIEVENT_TIMER ) + { + m_pManager->SendNotify(this, DUI_MSGTYPE_TIMER, event.wParam, event.lParam); + return; + } + if( event.Type == UIEVENT_CONTEXTMENU ) + { + if( IsContextMenuUsed() ) { + m_pManager->SendNotify(this, DUI_MSGTYPE_MENU, event.wParam, event.lParam); + return; + } + } + + if( m_pParent != NULL ) m_pParent->DoEvent(event); +} + + +void CControlUI::SetVirtualWnd(LPCTSTR pstrValue) +{ + m_sVirtualWnd = pstrValue; + m_pManager->UsedVirtualWnd(true); +} + +CDuiString CControlUI::GetVirtualWnd() const +{ + CDuiString str; + if( !m_sVirtualWnd.IsEmpty() ){ + str = m_sVirtualWnd; + } + else{ + CControlUI* pParent = GetParent(); + if( pParent != NULL){ + str = pParent->GetVirtualWnd(); + } + else{ + str = _T(""); + } + } + return str; +} + +void CControlUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) +{ + if( _tcscmp(pstrName, _T("pos")) == 0 ) { + RECT rcPos = { 0 }; + LPTSTR pstr = NULL; + rcPos.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + rcPos.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcPos.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcPos.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SIZE szXY = {rcPos.left >= 0 ? rcPos.left : rcPos.right, rcPos.top >= 0 ? rcPos.top : rcPos.bottom}; + SetFixedXY(szXY); + SetFixedWidth(rcPos.right - rcPos.left); + SetFixedHeight(rcPos.bottom - rcPos.top); + } + else if( _tcscmp(pstrName, _T("relativepos")) == 0 ) { + SIZE szMove,szZoom; + LPTSTR pstr = NULL; + szMove.cx = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + szMove.cy = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + szZoom.cx = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + szZoom.cy = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetRelativePos(szMove,szZoom); + } + else if( _tcscmp(pstrName, _T("padding")) == 0 ) { + RECT rcPadding = { 0 }; + LPTSTR pstr = NULL; + rcPadding.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + rcPadding.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcPadding.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcPadding.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetPadding(rcPadding); + } + else if( _tcscmp(pstrName, _T("bkcolor")) == 0 || _tcscmp(pstrName, _T("bkcolor1")) == 0 ) { + while( *pstrValue > _T('\0') && *pstrValue <= _T(' ') ) pstrValue = ::CharNext(pstrValue); + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetBkColor(clrColor); + } + else if( _tcscmp(pstrName, _T("bkcolor2")) == 0 ) { + while( *pstrValue > _T('\0') && *pstrValue <= _T(' ') ) pstrValue = ::CharNext(pstrValue); + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetBkColor2(clrColor); + } + else if( _tcscmp(pstrName, _T("bkcolor3")) == 0 ) { + while( *pstrValue > _T('\0') && *pstrValue <= _T(' ') ) pstrValue = ::CharNext(pstrValue); + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetBkColor3(clrColor); + } + else if( _tcscmp(pstrName, _T("bordercolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetBorderColor(clrColor); + } + else if( _tcscmp(pstrName, _T("focusbordercolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + SetFocusBorderColor(clrColor); + } + else if( _tcscmp(pstrName, _T("colorhsl")) == 0 ) SetColorHSL(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("bordersize")) == 0 ) { + CDuiString nValue = pstrValue; + if(nValue.Find(',') < 0) + { + SetBorderSize(_ttoi(pstrValue)); + RECT rcPadding = {0}; + SetBorderSize(rcPadding); + } + else + { + RECT rcPadding = { 0 }; + LPTSTR pstr = NULL; + rcPadding.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + rcPadding.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcPadding.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcPadding.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetBorderSize(rcPadding); + } + } + else if( _tcscmp(pstrName, _T("leftbordersize")) == 0 ) SetLeftBorderSize(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("topbordersize")) == 0 ) SetTopBorderSize(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("rightbordersize")) == 0 ) SetRightBorderSize(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("bottombordersize")) == 0 ) SetBottomBorderSize(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("borderstyle")) == 0 ) SetBorderStyle(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("borderround")) == 0 ) { + SIZE cxyRound = { 0 }; + LPTSTR pstr = NULL; + cxyRound.cx = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + cxyRound.cy = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetBorderRound(cxyRound); + } + else if( _tcscmp(pstrName, _T("bkimage")) == 0 ) SetBkImage(pstrValue); + else if( _tcscmp(pstrName, _T("width")) == 0 ) SetFixedWidth(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("height")) == 0 ) SetFixedHeight(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("minwidth")) == 0 ) SetMinWidth(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("minheight")) == 0 ) SetMinHeight(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("maxwidth")) == 0 ) SetMaxWidth(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("maxheight")) == 0 ) SetMaxHeight(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("name")) == 0 ) SetName(pstrValue); + else if( _tcscmp(pstrName, _T("text")) == 0 ) SetText(pstrValue); + else if( _tcscmp(pstrName, _T("tooltip")) == 0 ) SetToolTip(pstrValue); + else if( _tcscmp(pstrName, _T("userdata")) == 0 ) SetUserData(pstrValue); + else if( _tcscmp(pstrName, _T("enabled")) == 0 ) SetEnabled(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("mouse")) == 0 ) SetMouseEnabled(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("keyboard")) == 0 ) SetKeyboardEnabled(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("visible")) == 0 ) SetVisible(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("float")) == 0 ) SetFloat(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("shortcut")) == 0 ) SetShortcut(pstrValue[0]); + else if( _tcscmp(pstrName, _T("menu")) == 0 ) SetContextMenuUsed(_tcscmp(pstrValue, _T("true")) == 0); + else if( _tcscmp(pstrName, _T("virtualwnd")) == 0 ) SetVirtualWnd(pstrValue); +} + +CControlUI* CControlUI::ApplyAttributeList(LPCTSTR pstrList) +{ + CDuiString sItem; + CDuiString sValue; + while( *pstrList != _T('\0') ) { + sItem.Empty(); + sValue.Empty(); + while( *pstrList != _T('\0') && *pstrList != _T('=') ) { + LPTSTR pstrTemp = ::CharNext(pstrList); + while( pstrList < pstrTemp) { + sItem += *pstrList++; + } + } + ASSERT( *pstrList == _T('=') ); + if( *pstrList++ != _T('=') ) return this; + ASSERT( *pstrList == _T('\"') ); + if( *pstrList++ != _T('\"') ) return this; + while( *pstrList != _T('\0') && *pstrList != _T('\"') ) { + LPTSTR pstrTemp = ::CharNext(pstrList); + while( pstrList < pstrTemp) { + sValue += *pstrList++; + } + } + ASSERT( *pstrList == _T('\"') ); + if( *pstrList++ != _T('\"') ) return this; + SetAttribute(sItem, sValue); + if( *pstrList++ != _T(' ') ) return this; + } + return this; +} + +SIZE CControlUI::EstimateSize(SIZE szAvailable) +{ + return m_cxyFixed; +} + +void CControlUI::DoPaint(HDC hDC, const RECT& rcPaint) +{ + if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return; + + // ѭ򣺱ɫ->ͼ->״̬ͼ->ı->߿ + if( m_cxyBorderRound.cx > 0 || m_cxyBorderRound.cy > 0 ) { + CRenderClip roundClip; + CRenderClip::GenerateRoundClip(hDC, m_rcPaint, m_rcItem, m_cxyBorderRound.cx, m_cxyBorderRound.cy, roundClip); + PaintBkColor(hDC); + PaintBkImage(hDC); + PaintStatusImage(hDC); + PaintText(hDC); + PaintBorder(hDC); + } + else { + PaintBkColor(hDC); + PaintBkImage(hDC); + PaintStatusImage(hDC); + PaintText(hDC); + PaintBorder(hDC); + } +} + +void CControlUI::PaintBkColor(HDC hDC) +{ + if( m_dwBackColor != 0 ) { + if( m_dwBackColor2 != 0 ) { + if( m_dwBackColor3 != 0 ) { + RECT rc = m_rcItem; + rc.bottom = (rc.bottom + rc.top) / 2; + CRenderEngine::DrawGradient(hDC, rc, GetAdjustColor(m_dwBackColor), GetAdjustColor(m_dwBackColor2), true, 8); + rc.top = rc.bottom; + rc.bottom = m_rcItem.bottom; + CRenderEngine::DrawGradient(hDC, rc, GetAdjustColor(m_dwBackColor2), GetAdjustColor(m_dwBackColor3), true, 8); + } + else + CRenderEngine::DrawGradient(hDC, m_rcItem, GetAdjustColor(m_dwBackColor), GetAdjustColor(m_dwBackColor2), true, 16); + } + else if( m_dwBackColor >= 0xFF000000 ) CRenderEngine::DrawColor(hDC, m_rcPaint, GetAdjustColor(m_dwBackColor)); + else CRenderEngine::DrawColor(hDC, m_rcItem, GetAdjustColor(m_dwBackColor)); + } +} + +void CControlUI::PaintBkImage(HDC hDC) +{ + if( m_sBkImage.IsEmpty() ) return; + if( !DrawImage(hDC, (LPCTSTR)m_sBkImage) ) m_sBkImage.Empty(); +} + +void CControlUI::PaintStatusImage(HDC hDC) +{ + return; +} + +void CControlUI::PaintText(HDC hDC) +{ + return; +} + +void CControlUI::PaintBorder(HDC hDC) +{ + if(m_dwBorderColor != 0 || m_dwFocusBorderColor != 0) + { + if(m_nBorderSize > 0 && ( m_cxyBorderRound.cx > 0 || m_cxyBorderRound.cy > 0 ))//ԲDZ߿ + { + if (IsFocused() && m_dwFocusBorderColor != 0) + CRenderEngine::DrawRoundRect(hDC, m_rcItem, m_nBorderSize, m_cxyBorderRound.cx, m_cxyBorderRound.cy, GetAdjustColor(m_dwFocusBorderColor)); + else + CRenderEngine::DrawRoundRect(hDC, m_rcItem, m_nBorderSize, m_cxyBorderRound.cx, m_cxyBorderRound.cy, GetAdjustColor(m_dwBorderColor)); + } + else + { + if (IsFocused() && m_dwFocusBorderColor != 0 && m_nBorderSize > 0) + CRenderEngine::DrawRect(hDC, m_rcItem, m_nBorderSize, GetAdjustColor(m_dwFocusBorderColor)); + else if(m_rcBorderSize.left > 0 || m_rcBorderSize.top > 0 || m_rcBorderSize.right > 0 || m_rcBorderSize.bottom > 0) + { + RECT rcBorder; + + if(m_rcBorderSize.left > 0){ + rcBorder = m_rcItem; + rcBorder.right = m_rcItem.left; + CRenderEngine::DrawLine(hDC,rcBorder,m_rcBorderSize.left,GetAdjustColor(m_dwBorderColor),m_nBorderStyle); + } + if(m_rcBorderSize.top > 0){ + rcBorder = m_rcItem; + rcBorder.bottom = m_rcItem.top; + CRenderEngine::DrawLine(hDC,rcBorder,m_rcBorderSize.top,GetAdjustColor(m_dwBorderColor),m_nBorderStyle); + } + if(m_rcBorderSize.right > 0){ + rcBorder = m_rcItem; + rcBorder.left = m_rcItem.right; + CRenderEngine::DrawLine(hDC,rcBorder,m_rcBorderSize.right,GetAdjustColor(m_dwBorderColor),m_nBorderStyle); + } + if(m_rcBorderSize.bottom > 0){ + rcBorder = m_rcItem; + rcBorder.top = m_rcItem.bottom; + CRenderEngine::DrawLine(hDC,rcBorder,m_rcBorderSize.bottom,GetAdjustColor(m_dwBorderColor),m_nBorderStyle); + } + } + else if(m_nBorderSize > 0) + CRenderEngine::DrawRect(hDC, m_rcItem, m_nBorderSize, GetAdjustColor(m_dwBorderColor)); + } + } +} + +void CControlUI::DoPostPaint(HDC hDC, const RECT& rcPaint) +{ + return; +} + +int CControlUI::GetLeftBorderSize() const +{ + return m_rcBorderSize.left; +} + +void CControlUI::SetLeftBorderSize( int nSize ) +{ + m_rcBorderSize.left = nSize; + Invalidate(); +} + +int CControlUI::GetTopBorderSize() const +{ + return m_rcBorderSize.top; +} + +void CControlUI::SetTopBorderSize( int nSize ) +{ + m_rcBorderSize.top = nSize; + Invalidate(); +} + +int CControlUI::GetRightBorderSize() const +{ + return m_rcBorderSize.right; +} + +void CControlUI::SetRightBorderSize( int nSize ) +{ + m_rcBorderSize.right = nSize; + Invalidate(); +} + +int CControlUI::GetBottomBorderSize() const +{ + return m_rcBorderSize.bottom; +} + +void CControlUI::SetBottomBorderSize( int nSize ) +{ + m_rcBorderSize.bottom = nSize; + Invalidate(); +} + +int CControlUI::GetBorderStyle() const +{ + return m_nBorderStyle; +} + +void CControlUI::SetBorderStyle( int nStyle ) +{ + m_nBorderStyle = nStyle; + Invalidate(); +} + +} // namespace DuiLib diff --git a/DuiLib/Core/UIControl.h b/DuiLib/Core/UIControl.h new file mode 100644 index 00000000..a3fe6b71 --- /dev/null +++ b/DuiLib/Core/UIControl.h @@ -0,0 +1,219 @@ +#ifndef __UICONTROL_H__ +#define __UICONTROL_H__ + +#pragma once + +namespace DuiLib { + +///////////////////////////////////////////////////////////////////////////////////// +// + +typedef CControlUI* (CALLBACK* FINDCONTROLPROC)(CControlUI*, LPVOID); + +class UILIB_API CControlUI +{ +public: + CControlUI(); + virtual ~CControlUI(); + +public: + virtual CDuiString GetName() const; + virtual void SetName(LPCTSTR pstrName); + virtual LPCTSTR GetClass() const; + virtual LPVOID GetInterface(LPCTSTR pstrName); + virtual UINT GetControlFlags() const; + + virtual bool Activate(); + virtual CPaintManagerUI* GetManager() const; + virtual void SetManager(CPaintManagerUI* pManager, CControlUI* pParent, bool bInit = true); + virtual CControlUI* GetParent() const; + + // ı + virtual CDuiString GetText() const; + virtual void SetText(LPCTSTR pstrText); + + // ͼ + DWORD GetBkColor() const; + void SetBkColor(DWORD dwBackColor); + DWORD GetBkColor2() const; + void SetBkColor2(DWORD dwBackColor); + DWORD GetBkColor3() const; + void SetBkColor3(DWORD dwBackColor); + LPCTSTR GetBkImage(); + void SetBkImage(LPCTSTR pStrImage); + DWORD GetFocusBorderColor() const; + void SetFocusBorderColor(DWORD dwBorderColor); + bool IsColorHSL() const; + void SetColorHSL(bool bColorHSL); + SIZE GetBorderRound() const; + void SetBorderRound(SIZE cxyRound); + bool DrawImage(HDC hDC, LPCTSTR pStrImage, LPCTSTR pStrModify = NULL); + + //߿ + int GetBorderSize() const; + void SetBorderSize(int nSize); + DWORD GetBorderColor() const; + void SetBorderColor(DWORD dwBorderColor); + + void SetBorderSize(RECT rc); + int GetLeftBorderSize() const; + void SetLeftBorderSize(int nSize); + int GetTopBorderSize() const; + void SetTopBorderSize(int nSize); + int GetRightBorderSize() const; + void SetRightBorderSize(int nSize); + int GetBottomBorderSize() const; + void SetBottomBorderSize(int nSize); + int GetBorderStyle() const; + void SetBorderStyle(int nStyle); + + // λ + virtual const RECT& GetPos() const; + virtual void SetPos(RECT rc); + virtual int GetWidth() const; + virtual int GetHeight() const; + virtual int GetX() const; + virtual int GetY() const; + virtual RECT GetPadding() const; + virtual void SetPadding(RECT rcPadding); // ߾࣬ϲ㴰ڻ + virtual SIZE GetFixedXY() const; // ʵʴСλʹGetPosȡõԤIJοֵ + virtual void SetFixedXY(SIZE szXY); // floatΪtrueʱЧ + virtual int GetFixedWidth() const; // ʵʴСλʹGetPosȡõԤIJοֵ + virtual void SetFixedWidth(int cx); // ԤIJοֵ + virtual int GetFixedHeight() const; // ʵʴСλʹGetPosȡõԤIJοֵ + virtual void SetFixedHeight(int cy); // ԤIJοֵ + virtual int GetMinWidth() const; + virtual void SetMinWidth(int cx); + virtual int GetMaxWidth() const; + virtual void SetMaxWidth(int cx); + virtual int GetMinHeight() const; + virtual void SetMinHeight(int cy); + virtual int GetMaxHeight() const; + virtual void SetMaxHeight(int cy); + virtual void SetRelativePos(SIZE szMove,SIZE szZoom); + virtual void SetRelativeParentSize(SIZE sz); + virtual TRelativePosUI GetRelativePos() const; + virtual bool IsRelativePos() const; + + // ʾ + virtual CDuiString GetToolTip() const; + virtual void SetToolTip(LPCTSTR pstrText); + virtual void SetToolTipWidth(int nWidth); + virtual int GetToolTipWidth(void); // ToolTip + + // ݼ + virtual TCHAR GetShortcut() const; + virtual void SetShortcut(TCHAR ch); + + // ˵ + virtual bool IsContextMenuUsed() const; + virtual void SetContextMenuUsed(bool bMenuUsed); + + // û + virtual const CDuiString& GetUserData(); // ûʹ + virtual void SetUserData(LPCTSTR pstrText); // ûʹ + virtual UINT_PTR GetTag() const; // ûʹ + virtual void SetTag(UINT_PTR pTag); // ûʹ + + // һЩҪ + virtual bool IsVisible() const; + virtual void SetVisible(bool bVisible = true); + virtual void SetInternVisible(bool bVisible = true); // ڲãЩUIӵдھҪд˺ + virtual bool IsEnabled() const; + virtual void SetEnabled(bool bEnable = true); + virtual bool IsMouseEnabled() const; + virtual void SetMouseEnabled(bool bEnable = true); + virtual bool IsKeyboardEnabled() const; + virtual void SetKeyboardEnabled(bool bEnable = true); + virtual bool IsFocused() const; + virtual void SetFocus(); + virtual bool IsFloat() const; + virtual void SetFloat(bool bFloat = true); + + virtual CControlUI* FindControl(FINDCONTROLPROC Proc, LPVOID pData, UINT uFlags); + + void Invalidate(); + bool IsUpdateNeeded() const; + void NeedUpdate(); + void NeedParentUpdate(); + DWORD GetAdjustColor(DWORD dwColor); + + virtual void Init(); + virtual void DoInit(); + + virtual void Event(TEventUI& event); + virtual void DoEvent(TEventUI& event); + + virtual void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + CControlUI* ApplyAttributeList(LPCTSTR pstrList); + + virtual SIZE EstimateSize(SIZE szAvailable); + + virtual void DoPaint(HDC hDC, const RECT& rcPaint); + virtual void PaintBkColor(HDC hDC); + virtual void PaintBkImage(HDC hDC); + virtual void PaintStatusImage(HDC hDC); + virtual void PaintText(HDC hDC); + virtual void PaintBorder(HDC hDC); + + virtual void DoPostPaint(HDC hDC, const RECT& rcPaint); + + //ⴰڲ + void SetVirtualWnd(LPCTSTR pstrValue); + CDuiString GetVirtualWnd() const; + +public: + CEventSource OnInit; + CEventSource OnDestroy; + CEventSource OnSize; + CEventSource OnEvent; + CEventSource OnNotify; + +protected: + CPaintManagerUI* m_pManager; + CControlUI* m_pParent; + CDuiString m_sVirtualWnd; + CDuiString m_sName; + bool m_bUpdateNeeded; + bool m_bMenuUsed; + RECT m_rcItem; + RECT m_rcPadding; + SIZE m_cXY; + SIZE m_cxyFixed; + SIZE m_cxyMin; + SIZE m_cxyMax; + bool m_bVisible; + bool m_bInternVisible; + bool m_bEnabled; + bool m_bMouseEnabled; + bool m_bKeyboardEnabled ; + bool m_bFocused; + bool m_bFloat; + bool m_bSetPos; // ֹSetPosѭ + TRelativePosUI m_tRelativePos; + + CDuiString m_sText; + CDuiString m_sToolTip; + TCHAR m_chShortcut; + CDuiString m_sUserData; + UINT_PTR m_pTag; + + DWORD m_dwBackColor; + DWORD m_dwBackColor2; + DWORD m_dwBackColor3; + CDuiString m_sBkImage; + CDuiString m_sForeImage; + DWORD m_dwBorderColor; + DWORD m_dwFocusBorderColor; + bool m_bColorHSL; + int m_nBorderSize; + int m_nBorderStyle; + int m_nTooltipWidth; + SIZE m_cxyBorderRound; + RECT m_rcPaint; + RECT m_rcBorderSize; +}; + +} // namespace DuiLib + +#endif // __UICONTROL_H__ diff --git a/DuiLib/Core/UIDefine.h b/DuiLib/Core/UIDefine.h new file mode 100644 index 00000000..e4e7d18d --- /dev/null +++ b/DuiLib/Core/UIDefine.h @@ -0,0 +1,272 @@ +#pragma once +namespace DuiLib +{ + enum DuiSig + { + DuiSig_end = 0, // [marks end of message map] + DuiSig_lwl, // LRESULT (WPARAM, LPARAM) + DuiSig_vn, // void (TNotifyUI) + }; + + class CControlUI; + + // Structure for notifications to the outside world + typedef struct tagTNotifyUI + { + CDuiString sType; + CDuiString sVirtualWnd; + CControlUI* pSender; + DWORD dwTimestamp; + POINT ptMouse; + WPARAM wParam; + LPARAM lParam; + } TNotifyUI; + + class CNotifyPump; + typedef void (CNotifyPump::*DUI_PMSG)(TNotifyUI& msg); //ָ + + union DuiMessageMapFunctions + { + DUI_PMSG pfn; // generic member function pointer + LRESULT(CNotifyPump::*pfn_Notify_lwl)(WPARAM, LPARAM); + void (CNotifyPump::*pfn_Notify_vn)(TNotifyUI&); + }; + + //Ϣ + ////////////////////////////////////////////////////////////////////////// + +#define DUI_MSGTYPE_MENU (_T("menu")) +#define DUI_MSGTYPE_LINK (_T("link")) + +#define DUI_MSGTYPE_TIMER (_T("timer")) +#define DUI_MSGTYPE_CLICK (_T("click")) +#define DUI_MSGTYPE_DBCLICK (_T("dbclick")) + +#define DUI_MSGTYPE_RETURN (_T("return")) +#define DUI_MSGTYPE_SCROLL (_T("scroll")) + +#define DUI_MSGTYPE_DROPDOWN (_T("dropdown")) +#define DUI_MSGTYPE_SETFOCUS (_T("setfocus")) + +#define DUI_MSGTYPE_KILLFOCUS (_T("killfocus")) +#define DUI_MSGTYPE_ITEMCLICK (_T("itemclick")) +#define DUI_MSGTYPE_ITEMRCLICK (_T("itemrclick")) +#define DUI_MSGTYPE_TABSELECT (_T("tabselect")) + +#define DUI_MSGTYPE_ITEMSELECT (_T("itemselect")) +#define DUI_MSGTYPE_ITEMEXPAND (_T("itemexpand")) +#define DUI_MSGTYPE_WINDOWINIT (_T("windowinit")) +#define DUI_MSGTYPE_BUTTONDOWN (_T("buttondown")) +#define DUI_MSGTYPE_MOUSEENTER (_T("mouseenter")) +#define DUI_MSGTYPE_MOUSELEAVE (_T("mouseleave")) + +#define DUI_MSGTYPE_TEXTCHANGED (_T("textchanged")) +#define DUI_MSGTYPE_HEADERCLICK (_T("headerclick")) +#define DUI_MSGTYPE_ITEMDBCLICK (_T("itemdbclick")) +#define DUI_MSGTYPE_SHOWACTIVEX (_T("showactivex")) + +#define DUI_MSGTYPE_ITEMCOLLAPSE (_T("itemcollapse")) +#define DUI_MSGTYPE_ITEMACTIVATE (_T("itemactivate")) +#define DUI_MSGTYPE_VALUECHANGED (_T("valuechanged")) +#define DUI_MSGTYPE_VALUECHANGED_MOVE (_T("movevaluechanged")) + +#define DUI_MSGTYPE_SELECTCHANGED (_T("selectchanged")) +#define DUI_MSGTYPE_UNSELECTED (_T("unselected")) + + ////////////////////////////////////////////////////////////////////////// + + + + struct DUI_MSGMAP_ENTRY; + struct DUI_MSGMAP + { +#ifndef UILIB_STATIC + const DUI_MSGMAP* (PASCAL* pfnGetBaseMap)(); +#else + const DUI_MSGMAP* pBaseMap; +#endif + const DUI_MSGMAP_ENTRY* lpEntries; + }; + + //ṹ + struct DUI_MSGMAP_ENTRY //һṹ壬ϢϢ + { + CDuiString sMsgType; // DUIϢ + CDuiString sCtrlName; // ؼ + UINT nSig; // Ǻָ + DUI_PMSG pfn; // ָָ + }; + + // +#ifndef UILIB_STATIC +#define DUI_DECLARE_MESSAGE_MAP() \ +private: \ + static const DUI_MSGMAP_ENTRY _messageEntries[]; \ +protected: \ + static const DUI_MSGMAP messageMap; \ + static const DUI_MSGMAP* PASCAL _GetBaseMessageMap(); \ + virtual const DUI_MSGMAP* GetMessageMap() const; \ + +#else +#define DUI_DECLARE_MESSAGE_MAP() \ +private: \ + static const DUI_MSGMAP_ENTRY _messageEntries[]; \ +protected: \ + static const DUI_MSGMAP messageMap; \ + virtual const DUI_MSGMAP* GetMessageMap() const; \ + +#endif + + + //ʼ +#ifndef UILIB_STATIC +#define DUI_BASE_BEGIN_MESSAGE_MAP(theClass) \ + const DUI_MSGMAP* PASCAL theClass::_GetBaseMessageMap() \ + { return NULL; } \ + const DUI_MSGMAP* theClass::GetMessageMap() const \ + { return &theClass::messageMap; } \ + UILIB_COMDAT const DUI_MSGMAP theClass::messageMap = \ + { &theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] };\ + UILIB_COMDAT const DUI_MSGMAP_ENTRY theClass::_messageEntries[] = \ + { \ + +#else +#define DUI_BASE_BEGIN_MESSAGE_MAP(theClass) \ + const DUI_MSGMAP* theClass::GetMessageMap() const \ + { return &theClass::messageMap; } \ + UILIB_COMDAT const DUI_MSGMAP theClass::messageMap = \ + { NULL, &theClass::_messageEntries[0] }; \ + UILIB_COMDAT const DUI_MSGMAP_ENTRY theClass::_messageEntries[] = \ + { \ + +#endif + + + //ʼ +#ifndef UILIB_STATIC +#define DUI_BEGIN_MESSAGE_MAP(theClass, baseClass) \ + const DUI_MSGMAP* PASCAL theClass::_GetBaseMessageMap() \ + { return &baseClass::messageMap; } \ + const DUI_MSGMAP* theClass::GetMessageMap() const \ + { return &theClass::messageMap; } \ + UILIB_COMDAT const DUI_MSGMAP theClass::messageMap = \ + { &theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] }; \ + UILIB_COMDAT const DUI_MSGMAP_ENTRY theClass::_messageEntries[] = \ + { \ + +#else +#define DUI_BEGIN_MESSAGE_MAP(theClass, baseClass) \ + const DUI_MSGMAP* theClass::GetMessageMap() const \ + { return &theClass::messageMap; } \ + UILIB_COMDAT const DUI_MSGMAP theClass::messageMap = \ + { &baseClass::messageMap, &theClass::_messageEntries[0] }; \ + UILIB_COMDAT const DUI_MSGMAP_ENTRY theClass::_messageEntries[] = \ + { \ + +#endif + + + // +#define DUI_END_MESSAGE_MAP() \ + { _T(""), _T(""), DuiSig_end, (DUI_PMSG)0 } \ + }; \ + + + //Ϣ--ִк +#define DUI_ON_MSGTYPE(msgtype, memberFxn) \ + { msgtype, _T(""), DuiSig_vn, (DUI_PMSG)&memberFxn}, \ + + + //Ϣ--ؼ--ִк +#define DUI_ON_MSGTYPE_CTRNAME(msgtype,ctrname,memberFxn) \ + { msgtype, ctrname, DuiSig_vn, (DUI_PMSG)&memberFxn }, \ + + + //clickϢĿؼ--ִк +#define DUI_ON_CLICK_CTRNAME(ctrname,memberFxn) \ + { DUI_MSGTYPE_CLICK, ctrname, DuiSig_vn, (DUI_PMSG)&memberFxn }, \ + + + //selectchangedϢĿؼ--ִк +#define DUI_ON_SELECTCHANGED_CTRNAME(ctrname,memberFxn) \ + { DUI_MSGTYPE_SELECTCHANGED,ctrname,DuiSig_vn,(DUI_PMSG)&memberFxn }, \ + + + //killfocusϢĿؼ--ִк +#define DUI_ON_KILLFOCUS_CTRNAME(ctrname,memberFxn) \ + { DUI_MSGTYPE_KILLFOCUS,ctrname,DuiSig_vn,(DUI_PMSG)&memberFxn }, \ + + + //menuϢĿؼ--ִк +#define DUI_ON_MENU_CTRNAME(ctrname,memberFxn) \ + { DUI_MSGTYPE_MENU,ctrname,DuiSig_vn,(DUI_PMSG)&memberFxn }, \ + + + //ؼ޹صϢ + + //timerϢ--ִк +#define DUI_ON_TIMER() \ + { DUI_MSGTYPE_TIMER, _T(""), DuiSig_vn,(DUI_PMSG)&OnTimer }, \ + + + /// + //////////////ENDϢӳ궨//////////////////////////////////////////////////// + + + //////////////BEGINؼƺ궨////////////////////////////////////////////////// + /// + +#define DUI_CTR_EDIT (_T("Edit")) +#define DUI_CTR_LIST (_T("List")) +#define DUI_CTR_TEXT (_T("Text")) + +#define DUI_CTR_COMBO (_T("Combo")) +#define DUI_CTR_LABEL (_T("Label")) +#define DUI_CTR_FLASH (_T("Flash")) + +#define DUI_CTR_BUTTON (_T("Button")) +#define DUI_CTR_OPTION (_T("Option")) +#define DUI_CTR_SLIDER (_T("Slider")) + +#define DUI_CTR_CONTROL (_T("Control")) +#define DUI_CTR_ACTIVEX (_T("ActiveX")) +#define DUI_CTR_GIFANIM (_T("GifAnim")) + +#define DUI_CTR_LISTITEM (_T("ListItem")) +#define DUI_CTR_PROGRESS (_T("Progress")) +#define DUI_CTR_RICHEDIT (_T("RichEdit")) +#define DUI_CTR_CHECKBOX (_T("CheckBox")) +#define DUI_CTR_COMBOBOX (_T("ComboBox")) +#define DUI_CTR_DATETIME (_T("DateTime")) +#define DUI_CTR_TREEVIEW (_T("TreeView")) +#define DUI_CTR_TREENODE (_T("TreeNode")) + +#define DUI_CTR_CONTAINER (_T("Container")) +#define DUI_CTR_TABLAYOUT (_T("TabLayout")) +#define DUI_CTR_SCROLLBAR (_T("ScrollBar")) + +#define DUI_CTR_LISTHEADER (_T("ListHeader")) +#define DUI_CTR_TILELAYOUT (_T("TileLayout")) +#define DUI_CTR_WEBBROWSER (_T("WebBrowser")) + +#define DUI_CTR_CHILDLAYOUT (_T("ChildLayout")) +#define DUI_CTR_LISTELEMENT (_T("ListElement")) + +#define DUI_CTR_DIALOGLAYOUT (_T("DialogLayout")) + +#define DUI_CTR_VERTICALLAYOUT (_T("VerticalLayout")) +#define DUI_CTR_LISTHEADERITEM (_T("ListHeaderItem")) + +#define DUI_CTR_LISTTEXTELEMENT (_T("ListTextElement")) + +#define DUI_CTR_HORIZONTALLAYOUT (_T("HorizontalLayout")) +#define DUI_CTR_LISTLABELELEMENT (_T("ListLabelElement")) + +#define DUI_CTR_LISTCONTAINERELEMENT (_T("ListContainerElement")) + + /// + //////////////ENDؼƺ궨////////////////////////////////////////////////// + + + }// namespace DuiLib + diff --git a/DuiLib/Core/UIDlgBuilder.cpp b/DuiLib/Core/UIDlgBuilder.cpp new file mode 100644 index 00000000..2cdd3fdb --- /dev/null +++ b/DuiLib/Core/UIDlgBuilder.cpp @@ -0,0 +1,500 @@ +#include "StdAfx.h" + +namespace DuiLib { + +CDialogBuilder::CDialogBuilder() : m_pCallback(NULL), m_pstrtype(NULL) +{ + +} + +CControlUI* CDialogBuilder::Create(STRINGorID xml, LPCTSTR type, IDialogBuilderCallback* pCallback, + CPaintManagerUI* pManager, CControlUI* pParent) +{ + //ԴIDΪ0-65535ֽڣַָΪ4ֽ + //ַ<ͷΪXMLַΪXMLļ + + if( HIWORD(xml.m_lpstr) != NULL ) { + if( *(xml.m_lpstr) == _T('<') ) { + if( !m_xml.Load(xml.m_lpstr) ) return NULL; + } + else { + if( !m_xml.LoadFromFile(xml.m_lpstr) ) return NULL; + } + } + else { + HRSRC hResource = ::FindResource(CPaintManagerUI::GetResourceDll(), xml.m_lpstr, type); + if( hResource == NULL ) return NULL; + HGLOBAL hGlobal = ::LoadResource(CPaintManagerUI::GetResourceDll(), hResource); + if( hGlobal == NULL ) { + FreeResource(hResource); + return NULL; + } + + m_pCallback = pCallback; + if( !m_xml.LoadFromMem((BYTE*)::LockResource(hGlobal), ::SizeofResource(CPaintManagerUI::GetResourceDll(), hResource) )) return NULL; + ::FreeResource(hResource); + m_pstrtype = type; + } + + return Create(pCallback, pManager, pParent); +} + +CControlUI* CDialogBuilder::Create(IDialogBuilderCallback* pCallback, CPaintManagerUI* pManager, CControlUI* pParent) +{ + m_pCallback = pCallback; + CMarkupNode root = m_xml.GetRoot(); + if( !root.IsValid() ) return NULL; + + if( pManager ) { + LPCTSTR pstrClass = NULL; + int nAttributes = 0; + LPCTSTR pstrName = NULL; + LPCTSTR pstrValue = NULL; + LPTSTR pstr = NULL; + for( CMarkupNode node = root.GetChild() ; node.IsValid(); node = node.GetSibling() ) { + pstrClass = node.GetName(); + if( _tcscmp(pstrClass, _T("Image")) == 0 ) { + nAttributes = node.GetAttributeCount(); + LPCTSTR pImageName = NULL; + LPCTSTR pImageResType = NULL; + DWORD mask = 0; + for( int i = 0; i < nAttributes; i++ ) { + pstrName = node.GetAttributeName(i); + pstrValue = node.GetAttributeValue(i); + if( _tcscmp(pstrName, _T("name")) == 0 ) { + pImageName = pstrValue; + } + else if( _tcscmp(pstrName, _T("restype")) == 0 ) { + pImageResType = pstrValue; + } + else if( _tcscmp(pstrName, _T("mask")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + mask = _tcstoul(pstrValue, &pstr, 16); + } + } + if( pImageName ) pManager->AddImage(pImageName, pImageResType, mask); + } + else if( _tcscmp(pstrClass, _T("Font")) == 0 ) { + nAttributes = node.GetAttributeCount(); + LPCTSTR pFontName = NULL; + int size = 12; + bool bold = false; + bool underline = false; + bool italic = false; + bool defaultfont = false; + for( int i = 0; i < nAttributes; i++ ) { + pstrName = node.GetAttributeName(i); + pstrValue = node.GetAttributeValue(i); + if( _tcscmp(pstrName, _T("name")) == 0 ) { + pFontName = pstrValue; + } + else if( _tcscmp(pstrName, _T("size")) == 0 ) { + size = _tcstol(pstrValue, &pstr, 10); + } + else if( _tcscmp(pstrName, _T("bold")) == 0 ) { + bold = (_tcscmp(pstrValue, _T("true")) == 0); + } + else if( _tcscmp(pstrName, _T("underline")) == 0 ) { + underline = (_tcscmp(pstrValue, _T("true")) == 0); + } + else if( _tcscmp(pstrName, _T("italic")) == 0 ) { + italic = (_tcscmp(pstrValue, _T("true")) == 0); + } + else if( _tcscmp(pstrName, _T("default")) == 0 ) { + defaultfont = (_tcscmp(pstrValue, _T("true")) == 0); + } + } + if( pFontName ) { + pManager->AddFont(pFontName, size, bold, underline, italic); + if( defaultfont ) pManager->SetDefaultFont(pFontName, size, bold, underline, italic); + } + } + else if( _tcscmp(pstrClass, _T("Default")) == 0 ) { + nAttributes = node.GetAttributeCount(); + LPCTSTR pControlName = NULL; + LPCTSTR pControlValue = NULL; + for( int i = 0; i < nAttributes; i++ ) { + pstrName = node.GetAttributeName(i); + pstrValue = node.GetAttributeValue(i); + if( _tcscmp(pstrName, _T("name")) == 0 ) { + pControlName = pstrValue; + } + else if( _tcscmp(pstrName, _T("value")) == 0 ) { + pControlValue = pstrValue; + } + } + if( pControlName ) { + pManager->AddDefaultAttributeList(pControlName, pControlValue); + } + } + } + + pstrClass = root.GetName(); + if( _tcscmp(pstrClass, _T("Window")) == 0 ) { + if( pManager->GetPaintWindow() ) { + int nAttributes = root.GetAttributeCount(); + for( int i = 0; i < nAttributes; i++ ) { + pstrName = root.GetAttributeName(i); + pstrValue = root.GetAttributeValue(i); + if( _tcscmp(pstrName, _T("size")) == 0 ) { + LPTSTR pstr = NULL; + int cx = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + int cy = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + pManager->SetInitSize(cx, cy); + } + else if( _tcscmp(pstrName, _T("sizebox")) == 0 ) { + RECT rcSizeBox = { 0 }; + LPTSTR pstr = NULL; + rcSizeBox.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + rcSizeBox.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcSizeBox.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcSizeBox.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + pManager->SetSizeBox(rcSizeBox); + } + else if( _tcscmp(pstrName, _T("caption")) == 0 ) { + RECT rcCaption = { 0 }; + LPTSTR pstr = NULL; + rcCaption.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + rcCaption.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcCaption.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcCaption.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + pManager->SetCaptionRect(rcCaption); + } + else if( _tcscmp(pstrName, _T("roundcorner")) == 0 ) { + LPTSTR pstr = NULL; + int cx = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + int cy = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + pManager->SetRoundCorner(cx, cy); + } + else if( _tcscmp(pstrName, _T("mininfo")) == 0 ) { + LPTSTR pstr = NULL; + int cx = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + int cy = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + pManager->SetMinInfo(cx, cy); + } + else if( _tcscmp(pstrName, _T("maxinfo")) == 0 ) { + LPTSTR pstr = NULL; + int cx = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + int cy = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + pManager->SetMaxInfo(cx, cy); + } + else if( _tcscmp(pstrName, _T("alpha")) == 0 ) { + pManager->SetTransparent(_ttoi(pstrValue)); + } + else if( _tcscmp(pstrName, _T("bktrans")) == 0 ) { + pManager->SetBackgroundTransparent(_tcscmp(pstrValue, _T("true")) == 0); + } + else if( _tcscmp(pstrName, _T("disabledfontcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + pManager->SetDefaultDisabledColor(clrColor); + } + else if( _tcscmp(pstrName, _T("defaultfontcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + pManager->SetDefaultFontColor(clrColor); + } + else if( _tcscmp(pstrName, _T("linkfontcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + pManager->SetDefaultLinkFontColor(clrColor); + } + else if( _tcscmp(pstrName, _T("linkhoverfontcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + pManager->SetDefaultLinkHoverFontColor(clrColor); + } + else if( _tcscmp(pstrName, _T("selectedcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + pManager->SetDefaultSelectedBkColor(clrColor); + } + else if( _tcscmp(pstrName, _T("shadowsize")) == 0 ) { + pManager->GetShadow()->SetSize(_ttoi(pstrValue)); + } + else if( _tcscmp(pstrName, _T("shadowsharpness")) == 0 ) { + pManager->GetShadow()->SetSharpness(_ttoi(pstrValue)); + } + else if( _tcscmp(pstrName, _T("shadowdarkness")) == 0 ) { + pManager->GetShadow()->SetDarkness(_ttoi(pstrValue)); + } + else if( _tcscmp(pstrName, _T("shadowposition")) == 0 ) { + LPTSTR pstr = NULL; + int cx = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + int cy = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + pManager->GetShadow()->SetPosition(cx, cy); + } + else if( _tcscmp(pstrName, _T("shadowcolor")) == 0 ) { + if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue); + LPTSTR pstr = NULL; + DWORD clrColor = _tcstoul(pstrValue, &pstr, 16); + pManager->GetShadow()->SetColor(clrColor); + } + else if( _tcscmp(pstrName, _T("shadowcorner")) == 0 ) { + RECT rcCorner = { 0 }; + LPTSTR pstr = NULL; + rcCorner.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + rcCorner.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcCorner.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcCorner.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + pManager->GetShadow()->SetShadowCorner(rcCorner); + } + else if( _tcscmp(pstrName, _T("shadowimage")) == 0 ) { + pManager->GetShadow()->SetImage(pstrValue); + } + else if( _tcscmp(pstrName, _T("showshadow")) == 0 ) { + pManager->GetShadow()->ShowShadow(_tcscmp(pstrValue, _T("true")) == 0); + } + else if( _tcscmp(pstrName, _T("gdiplustext")) == 0 ) { + pManager->SetUseGdiplusText(_tcscmp(pstrValue, _T("true")) == 0); + } + } + } + } + } + return _Parse(&root, pParent, pManager); +} + +CMarkup* CDialogBuilder::GetMarkup() +{ + return &m_xml; +} + +void CDialogBuilder::GetLastErrorMessage(LPTSTR pstrMessage, SIZE_T cchMax) const +{ + return m_xml.GetLastErrorMessage(pstrMessage, cchMax); +} + +void CDialogBuilder::GetLastErrorLocation(LPTSTR pstrSource, SIZE_T cchMax) const +{ + return m_xml.GetLastErrorLocation(pstrSource, cchMax); +} + +CControlUI* CDialogBuilder::_Parse(CMarkupNode* pRoot, CControlUI* pParent, CPaintManagerUI* pManager) +{ + IContainerUI* pContainer = NULL; + CControlUI* pReturn = NULL; + for( CMarkupNode node = pRoot->GetChild() ; node.IsValid(); node = node.GetSibling() ) { + LPCTSTR pstrClass = node.GetName(); + if( _tcscmp(pstrClass, _T("Image")) == 0 || _tcscmp(pstrClass, _T("Font")) == 0 \ + || _tcscmp(pstrClass, _T("Default")) == 0 ) continue; + + CControlUI* pControl = NULL; + if( _tcscmp(pstrClass, _T("Include")) == 0 ) { + if( !node.HasAttributes() ) continue; + int count = 1; + LPTSTR pstr = NULL; + TCHAR szValue[500] = { 0 }; + SIZE_T cchLen = lengthof(szValue) - 1; + if ( node.GetAttributeValue(_T("count"), szValue, cchLen) ) + count = _tcstol(szValue, &pstr, 10); + cchLen = lengthof(szValue) - 1; + if ( !node.GetAttributeValue(_T("source"), szValue, cchLen) ) continue; + for ( int i = 0; i < count; i++ ) { + CDialogBuilder builder; + if( m_pstrtype != NULL ) { // ʹԴdllԴжȡ + WORD id = (WORD)_tcstol(szValue, &pstr, 10); + pControl = builder.Create((UINT)id, m_pstrtype, m_pCallback, pManager, pParent); + } + else { + pControl = builder.Create((LPCTSTR)szValue, (UINT)0, m_pCallback, pManager, pParent); + } + } + continue; + } + //ؼXML + else if( _tcscmp(pstrClass, _T("TreeNode")) == 0 ) { + CTreeNodeUI* pParentNode = static_cast(pParent->GetInterface(_T("TreeNode"))); + CTreeNodeUI* pNode = new CTreeNodeUI(); + if(pParentNode){ + if(!pParentNode->Add(pNode)){ + delete pNode; + continue; + } + } + + // пؼĬȳʼĬ + if( pManager ) { + pNode->SetManager(pManager, NULL, false); + LPCTSTR pDefaultAttributes = pManager->GetDefaultAttributeList(pstrClass); + if( pDefaultAttributes ) { + pNode->ApplyAttributeList(pDefaultAttributes); + } + } + + // ԲĬ + if( node.HasAttributes() ) { + TCHAR szValue[500] = { 0 }; + SIZE_T cchLen = lengthof(szValue) - 1; + // Set ordinary attributes + int nAttributes = node.GetAttributeCount(); + for( int i = 0; i < nAttributes; i++ ) { + pNode->SetAttribute(node.GetAttributeName(i), node.GetAttributeValue(i)); + } + } + + //ӽڵ㼰ӿؼ + if(node.HasChildren()){ + CControlUI* pSubControl = _Parse(&node,pNode,pManager); + if(pSubControl && _tcscmp(pSubControl->GetClass(),_T("TreeNodeUI")) != 0) + { + // pSubControl->SetFixedWidth(30); + // CHorizontalLayoutUI* pHorz = pNode->GetTreeNodeHoriznotal(); + // pHorz->Add(new CEditUI()); + // continue; + } + } + + if(!pParentNode){ + CTreeViewUI* pTreeView = static_cast(pParent->GetInterface(_T("TreeView"))); + ASSERT(pTreeView); + if( pTreeView == NULL ) return NULL; + if( !pTreeView->Add(pNode) ) { + delete pNode; + continue; + } + } + continue; + } + else { + SIZE_T cchLen = _tcslen(pstrClass); + switch( cchLen ) { + case 4: + if( _tcscmp(pstrClass, DUI_CTR_EDIT) == 0 ) pControl = new CEditUI; + else if( _tcscmp(pstrClass, DUI_CTR_LIST) == 0 ) pControl = new CListUI; + else if( _tcscmp(pstrClass, DUI_CTR_TEXT) == 0 ) pControl = new CTextUI; + break; + case 5: + if( _tcscmp(pstrClass, DUI_CTR_COMBO) == 0 ) pControl = new CComboUI; + else if( _tcscmp(pstrClass, DUI_CTR_LABEL) == 0 ) pControl = new CLabelUI; + else if( _tcscmp(pstrClass, DUI_CTR_FLASH) == 0 ) pControl = new CFlashUI; + break; + case 6: + if( _tcscmp(pstrClass, DUI_CTR_BUTTON) == 0 ) pControl = new CButtonUI; + else if( _tcscmp(pstrClass, DUI_CTR_OPTION) == 0 ) pControl = new COptionUI; + else if( _tcscmp(pstrClass, DUI_CTR_SLIDER) == 0 ) pControl = new CSliderUI; + break; + case 7: + if( _tcscmp(pstrClass, DUI_CTR_CONTROL) == 0 ) pControl = new CControlUI; + else if( _tcscmp(pstrClass, DUI_CTR_ACTIVEX) == 0 ) pControl = new CActiveXUI; + else if( _tcscmp(pstrClass, DUI_CTR_GIFANIM) == 0 ) pControl = new CGifAnimUI; + break; + case 8: + if( _tcscmp(pstrClass, DUI_CTR_PROGRESS) == 0 ) pControl = new CProgressUI; + else if( _tcscmp(pstrClass, DUI_CTR_RICHEDIT) == 0 ) pControl = new CRichEditUI; + else if( _tcscmp(pstrClass, DUI_CTR_CHECKBOX) == 0 ) pControl = new COptionUI; + else if( _tcscmp(pstrClass, DUI_CTR_COMBOBOX) == 0 ) pControl = new CComboBoxUI; + else if( _tcscmp(pstrClass, DUI_CTR_DATETIME) == 0 ) pControl = new CDateTimeUI; + else if( _tcscmp(pstrClass, DUI_CTR_TREEVIEW) == 0 ) pControl = new CTreeViewUI; + break; + case 9: + if( _tcscmp(pstrClass, DUI_CTR_CONTAINER) == 0 ) pControl = new CContainerUI; + else if( _tcscmp(pstrClass, DUI_CTR_TABLAYOUT) == 0 ) pControl = new CTabLayoutUI; + else if( _tcscmp(pstrClass, DUI_CTR_SCROLLBAR) == 0 ) pControl = new CScrollBarUI; + break; + case 10: + if( _tcscmp(pstrClass, DUI_CTR_LISTHEADER) == 0 ) pControl = new CListHeaderUI; + else if( _tcscmp(pstrClass, DUI_CTR_TILELAYOUT) == 0 ) pControl = new CTileLayoutUI; + else if( _tcscmp(pstrClass, DUI_CTR_WEBBROWSER) == 0 ) pControl = new CWebBrowserUI; + break; + case 11: + if (_tcscmp(pstrClass, DUI_CTR_CHILDLAYOUT) == 0) pControl = new CChildLayoutUI; + break; + case 14: + if( _tcscmp(pstrClass, DUI_CTR_VERTICALLAYOUT) == 0 ) pControl = new CVerticalLayoutUI; + else if( _tcscmp(pstrClass, DUI_CTR_LISTHEADERITEM) == 0 ) pControl = new CListHeaderItemUI; + break; + case 15: + if( _tcscmp(pstrClass, DUI_CTR_LISTTEXTELEMENT) == 0 ) pControl = new CListTextElementUI; + break; + case 16: + if( _tcscmp(pstrClass, DUI_CTR_HORIZONTALLAYOUT) == 0 ) pControl = new CHorizontalLayoutUI; + else if( _tcscmp(pstrClass, DUI_CTR_LISTLABELELEMENT) == 0 ) pControl = new CListLabelElementUI; + break; + case 20: + if( _tcscmp(pstrClass, DUI_CTR_LISTCONTAINERELEMENT) == 0 ) pControl = new CListContainerElementUI; + break; + } + // User-supplied control factory + if( pControl == NULL ) { + CStdPtrArray* pPlugins = CPaintManagerUI::GetPlugins(); + LPCREATECONTROL lpCreateControl = NULL; + for( int i = 0; i < pPlugins->GetSize(); ++i ) { + lpCreateControl = (LPCREATECONTROL)pPlugins->GetAt(i); + if( lpCreateControl != NULL ) { + pControl = lpCreateControl(pstrClass); + if( pControl != NULL ) break; + } + } + } + if( pControl == NULL && m_pCallback != NULL ) { + pControl = m_pCallback->CreateControl(pstrClass); + } + } + +#ifndef _DEBUG + ASSERT(pControl); +#endif // _DEBUG + if( pControl == NULL ) + { +#ifdef _DEBUG + DUITRACE(_T("δ֪ؼ:%s"),pstrClass); +#else + continue; +#endif + } + + // Add children + if( node.HasChildren() ) { + _Parse(&node, pControl, pManager); + } + // Attach to parent + // ΪijЩԺ͸أselectedAdd + if( pParent != NULL ) { + CTreeNodeUI* pContainerNode = static_cast(pParent->GetInterface(_T("TreeNode"))); + if(pContainerNode) + pContainerNode->GetTreeNodeHoriznotal()->Add(pControl); + else + { + if( pContainer == NULL ) pContainer = static_cast(pParent->GetInterface(_T("IContainer"))); + ASSERT(pContainer); + if( pContainer == NULL ) return NULL; + if( !pContainer->Add(pControl) ) { + delete pControl; + continue; + } + } + } + // Init default attributes + if( pManager ) { + pControl->SetManager(pManager, NULL, false); + LPCTSTR pDefaultAttributes = pManager->GetDefaultAttributeList(pstrClass); + if( pDefaultAttributes ) { + pControl->ApplyAttributeList(pDefaultAttributes); + } + } + // Process attributes + if( node.HasAttributes() ) { + TCHAR szValue[500] = { 0 }; + SIZE_T cchLen = lengthof(szValue) - 1; + // Set ordinary attributes + int nAttributes = node.GetAttributeCount(); + for( int i = 0; i < nAttributes; i++ ) { + pControl->SetAttribute(node.GetAttributeName(i), node.GetAttributeValue(i)); + } + } + if( pManager ) { + pControl->SetManager(NULL, NULL, false); + } + // Return first item + if( pReturn == NULL ) pReturn = pControl; + } + return pReturn; +} + +} // namespace DuiLib diff --git a/DuiLib/Core/UIDlgBuilder.h b/DuiLib/Core/UIDlgBuilder.h new file mode 100644 index 00000000..359fee4b --- /dev/null +++ b/DuiLib/Core/UIDlgBuilder.h @@ -0,0 +1,38 @@ +#ifndef __UIDLGBUILDER_H__ +#define __UIDLGBUILDER_H__ + +#pragma once + +namespace DuiLib { + +class IDialogBuilderCallback +{ +public: + virtual CControlUI* CreateControl(LPCTSTR pstrClass) = 0; +}; + + +class UILIB_API CDialogBuilder +{ +public: + CDialogBuilder(); + CControlUI* Create(STRINGorID xml, LPCTSTR type = NULL, IDialogBuilderCallback* pCallback = NULL, + CPaintManagerUI* pManager = NULL, CControlUI* pParent = NULL); + CControlUI* Create(IDialogBuilderCallback* pCallback = NULL, CPaintManagerUI* pManager = NULL, + CControlUI* pParent = NULL); + + CMarkup* GetMarkup(); + + void GetLastErrorMessage(LPTSTR pstrMessage, SIZE_T cchMax) const; + void GetLastErrorLocation(LPTSTR pstrSource, SIZE_T cchMax) const; +private: + CControlUI* _Parse(CMarkupNode* parent, CControlUI* pParent = NULL, CPaintManagerUI* pManager = NULL); + + CMarkup m_xml; + IDialogBuilderCallback* m_pCallback; + LPCTSTR m_pstrtype; +}; + +} // namespace DuiLib + +#endif // __UIDLGBUILDER_H__ diff --git a/DuiLib/Core/UIManager.cpp b/DuiLib/Core/UIManager.cpp new file mode 100644 index 00000000..00b62d38 --- /dev/null +++ b/DuiLib/Core/UIManager.cpp @@ -0,0 +1,2435 @@ +#include "StdAfx.h" +#include + +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened +typedef DWORD ZRESULT; +#define OpenZip OpenZipU +#define CloseZip(hz) CloseZipU(hz) +extern HZIP OpenZipU(void *z,unsigned int len,DWORD flags); +extern ZRESULT CloseZipU(HZIP hz); + +namespace DuiLib { + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +static UINT MapKeyState() +{ + UINT uState = 0; + if( ::GetKeyState(VK_CONTROL) < 0 ) uState |= MK_CONTROL; + if( ::GetKeyState(VK_RBUTTON) < 0 ) uState |= MK_LBUTTON; + if( ::GetKeyState(VK_LBUTTON) < 0 ) uState |= MK_RBUTTON; + if( ::GetKeyState(VK_SHIFT) < 0 ) uState |= MK_SHIFT; + if( ::GetKeyState(VK_MENU) < 0 ) uState |= MK_ALT; + return uState; +} + +typedef struct tagFINDTABINFO +{ + CControlUI* pFocus; + CControlUI* pLast; + bool bForward; + bool bNextIsIt; +} FINDTABINFO; + +typedef struct tagFINDSHORTCUT +{ + TCHAR ch; + bool bPickNext; +} FINDSHORTCUT; + +typedef struct tagTIMERINFO +{ + CControlUI* pSender; + UINT nLocalID; + HWND hWnd; + UINT uWinTimer; + bool bKilled; +} TIMERINFO; + +///////////////////////////////////////////////////////////////////////////////////// + +HPEN m_hUpdateRectPen = NULL; +HINSTANCE CPaintManagerUI::m_hInstance = NULL; +HINSTANCE CPaintManagerUI::m_hResourceInstance = NULL; +CDuiString CPaintManagerUI::m_pStrDefaultFontName;//added by cddjr at 05/18/2012 +CDuiString CPaintManagerUI::m_pStrResourcePath; +CDuiString CPaintManagerUI::m_pStrResourceZip; +bool CPaintManagerUI::m_bCachedResourceZip = false; +HANDLE CPaintManagerUI::m_hResourceZip = NULL; +short CPaintManagerUI::m_H = 180; +short CPaintManagerUI::m_S = 100; +short CPaintManagerUI::m_L = 100; +CStdPtrArray CPaintManagerUI::m_aPreMessages; +CStdPtrArray CPaintManagerUI::m_aPlugins; +const UINT kCaretTimerID = 0xF1; + +CPaintManagerUI::CPaintManagerUI() : +m_hWndPaint(NULL), +m_hDcPaint(NULL), +m_hDcOffscreen(NULL), +m_hbmpOffscreen(NULL), +m_pBmpOffscreenBits(NULL), +m_bOffscreenPaint(true), +m_bAlphaBackground(false), +m_bIsRestore(false), + +m_hwndTooltip(NULL), +m_uTimerID(0x1000), +m_pRoot(NULL), +m_pFocus(NULL), +m_pEventHover(NULL), +m_pEventClick(NULL), +m_pEventKey(NULL), +m_bFirstLayout(true), +m_bFocusNeeded(false), +m_bUpdateNeeded(false), +m_bMouseTracking(false), +m_bMouseCapture(false), +m_bUsedVirtualWnd(false), +m_nOpacity(255), +m_pParentResourcePM(NULL), +m_bCaretActive(false), +m_bCaretShowing(false), +m_currentCaretObject(NULL), +m_bUseGdiplusText(false) +{ + m_dwDefaultDisabledColor = 0xFFA7A6AA; + m_dwDefaultFontColor = 0xFF000001; + m_dwDefaultLinkFontColor = 0xFF0000FF; + m_dwDefaultLinkHoverFontColor = 0xFFD3215F; + m_dwDefaultSelectedBkColor = 0xFFBAE4FF; + LOGFONT lf = { 0 }; + ::GetObject(::GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &lf); + lf.lfCharSet = DEFAULT_CHARSET; + + if (CPaintManagerUI::m_pStrDefaultFontName.GetLength()>0) + { + _tcscpy_s(lf.lfFaceName, LF_FACESIZE, CPaintManagerUI::m_pStrDefaultFontName.GetData()); + } + HFONT hDefaultFont = ::CreateFontIndirect(&lf); + m_DefaultFontInfo.hFont = hDefaultFont; + m_DefaultFontInfo.sFontName = lf.lfFaceName; + m_DefaultFontInfo.iSize = -lf.lfHeight; + m_DefaultFontInfo.bBold = (lf.lfWeight >= FW_BOLD); + m_DefaultFontInfo.bUnderline = (lf.lfUnderline == TRUE); + m_DefaultFontInfo.bItalic = (lf.lfItalic == TRUE); + ::ZeroMemory(&m_DefaultFontInfo.tm, sizeof(m_DefaultFontInfo.tm)); + + if( m_hUpdateRectPen == NULL ) { + m_hUpdateRectPen = ::CreatePen(PS_SOLID, 1, RGB(220, 0, 0)); + // Boot Windows Common Controls (for the ToolTip control) + ::InitCommonControls(); + ::LoadLibrary(_T("msimg32.dll")); + } + + m_pGdiplusStartupInput = new Gdiplus::GdiplusStartupInput; + Gdiplus::GdiplusStartup( &m_gdiplusToken, m_pGdiplusStartupInput, NULL); // GDIӿ + + m_szMinWindow.cx = 0; + m_szMinWindow.cy = 0; + m_szMaxWindow.cx = 0; + m_szMaxWindow.cy = 0; + m_szInitWindowSize.cx = 0; + m_szInitWindowSize.cy = 0; + m_szRoundCorner.cx = m_szRoundCorner.cy = 0; + ::ZeroMemory(&m_rcSizeBox, sizeof(m_rcSizeBox)); + ::ZeroMemory(&m_rcCaption, sizeof(m_rcCaption)); + ::ZeroMemory(&m_rtCaret, sizeof(m_rtCaret)); + ::ZeroMemory(&m_rcInvalidate, sizeof(m_rcInvalidate)); + m_ptLastMousePos.x = m_ptLastMousePos.y = -1; +} + +CPaintManagerUI::~CPaintManagerUI() +{ + // Delete the control-tree structures + for( int i = 0; i < m_aDelayedCleanup.GetSize(); i++ ) delete static_cast(m_aDelayedCleanup[i]); + for( int i = 0; i < m_aAsyncNotify.GetSize(); i++ ) delete static_cast(m_aAsyncNotify[i]); + m_mNameHash.Resize(0); + delete m_pRoot; + + Gdiplus::GdiplusShutdown( m_gdiplusToken ); // жGDIӿ + delete m_pGdiplusStartupInput; + + ::DeleteObject(m_DefaultFontInfo.hFont); + RemoveAllFonts(); + RemoveAllImages(); + RemoveAllDefaultAttributeList(); + RemoveAllOptionGroups(); + RemoveAllTimers(); + + // Reset other parts... + if( m_hwndTooltip != NULL ) ::DestroyWindow(m_hwndTooltip); + if( m_hDcOffscreen != NULL ) ::DeleteDC(m_hDcOffscreen); + if( m_hbmpOffscreen != NULL ) ::DeleteObject(m_hbmpOffscreen); + if( m_hDcPaint != NULL ) ::ReleaseDC(m_hWndPaint, m_hDcPaint); + m_aPreMessages.Remove(m_aPreMessages.Find(this)); +} + +void CPaintManagerUI::Init(HWND hWnd) +{ + ASSERT(::IsWindow(hWnd)); + // Remember the window context we came from + m_hWndPaint = hWnd; + m_hDcPaint = ::GetDC(hWnd); + // We'll want to filter messages globally too + m_aPreMessages.Add(this); +} + +HINSTANCE CPaintManagerUI::GetInstance() +{ + return m_hInstance; +} + +CDuiString CPaintManagerUI::GetInstancePath() +{ + if( m_hInstance == NULL ) return _T('\0'); + + TCHAR tszModule[MAX_PATH + 1] = { 0 }; + ::GetModuleFileName(m_hInstance, tszModule, MAX_PATH); + CDuiString sInstancePath = tszModule; + int pos = sInstancePath.ReverseFind(_T('\\')); + if( pos >= 0 ) sInstancePath = sInstancePath.Left(pos + 1); + return sInstancePath; +} + +CDuiString CPaintManagerUI::GetCurrentPath() +{ + TCHAR tszModule[MAX_PATH + 1] = { 0 }; + ::GetCurrentDirectory(MAX_PATH, tszModule); + return tszModule; +} + +HINSTANCE CPaintManagerUI::GetResourceDll() +{ + if( m_hResourceInstance == NULL ) return m_hInstance; + return m_hResourceInstance; +} + +const CDuiString& CPaintManagerUI::GetResourcePath() +{ + return m_pStrResourcePath; +} + +const CDuiString& CPaintManagerUI::GetResourceZip() +{ + return m_pStrResourceZip; +} + +bool CPaintManagerUI::IsCachedResourceZip() +{ + return m_bCachedResourceZip; +} + +HANDLE CPaintManagerUI::GetResourceZipHandle() +{ + return m_hResourceZip; +} + +void CPaintManagerUI::SetInstance(HINSTANCE hInst) +{ + m_hInstance = hInst; + CShadowUI::Initialize(hInst); +} + +void CPaintManagerUI::SetCurrentPath(LPCTSTR pStrPath) +{ + ::SetCurrentDirectory(pStrPath); +} + +void CPaintManagerUI::SetResourceDll(HINSTANCE hInst) +{ + m_hResourceInstance = hInst; +} + +void CPaintManagerUI::SetResourcePath(LPCTSTR pStrPath) +{ + m_pStrResourcePath = pStrPath; + if( m_pStrResourcePath.IsEmpty() ) return; + TCHAR cEnd = m_pStrResourcePath.GetAt(m_pStrResourcePath.GetLength() - 1); + if( cEnd != _T('\\') && cEnd != _T('/') ) m_pStrResourcePath += _T('\\'); +} + +void CPaintManagerUI::SetResourceZip(LPVOID pVoid, unsigned int len) +{ + if( m_pStrResourceZip == _T("membuffer") ) return; + if( m_bCachedResourceZip && m_hResourceZip != NULL ) { + CloseZip((HZIP)m_hResourceZip); + m_hResourceZip = NULL; + } + m_pStrResourceZip = _T("membuffer"); + m_bCachedResourceZip = true; + if( m_bCachedResourceZip ) + m_hResourceZip = (HANDLE)OpenZip(pVoid, len, 3); +} + +void CPaintManagerUI::SetResourceZip(LPCTSTR pStrPath, bool bCachedResourceZip) +{ + if( m_pStrResourceZip == pStrPath && m_bCachedResourceZip == bCachedResourceZip ) return; + if( m_bCachedResourceZip && m_hResourceZip != NULL ) { + CloseZip((HZIP)m_hResourceZip); + m_hResourceZip = NULL; + } + m_pStrResourceZip = pStrPath; + m_bCachedResourceZip = bCachedResourceZip; + if( m_bCachedResourceZip ) { + CDuiString sFile = CPaintManagerUI::GetResourcePath(); + sFile += CPaintManagerUI::GetResourceZip(); + m_hResourceZip = (HANDLE)OpenZip((void*)sFile.GetData(), 0, 2); + } +} + +void CPaintManagerUI::GetHSL(short* H, short* S, short* L) +{ + *H = m_H; + *S = m_S; + *L = m_L; +} + +void CPaintManagerUI::SetHSL(bool bUseHSL, short H, short S, short L) +{ + if( H == m_H && S == m_S && L == m_L ) return; + m_H = CLAMP(H, 0, 360); + m_S = CLAMP(S, 0, 200); + m_L = CLAMP(L, 0, 200); + for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) { + CPaintManagerUI* pManager = static_cast(m_aPreMessages[i]); + if( pManager != NULL && pManager->GetRoot() != NULL ) + pManager->GetRoot()->Invalidate(); + } +} + +void CPaintManagerUI::ReloadSkin() +{ + for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) { + CPaintManagerUI* pManager = static_cast(m_aPreMessages[i]); + pManager->ReloadAllImages(); + } +} + +bool CPaintManagerUI::LoadPlugin(LPCTSTR pstrModuleName) +{ + ASSERT( !::IsBadStringPtr(pstrModuleName,-1) || pstrModuleName == NULL ); + if( pstrModuleName == NULL ) return false; + HMODULE hModule = ::LoadLibrary(pstrModuleName); + if( hModule != NULL ) { + LPCREATECONTROL lpCreateControl = (LPCREATECONTROL)::GetProcAddress(hModule, "CreateControl"); + if( lpCreateControl != NULL ) { + if( m_aPlugins.Find(lpCreateControl) >= 0 ) return true; + m_aPlugins.Add(lpCreateControl); + return true; + } + } + return false; +} + +CStdPtrArray* CPaintManagerUI::GetPlugins() +{ + return &m_aPlugins; +} + +HWND CPaintManagerUI::GetPaintWindow() const +{ + return m_hWndPaint; +} + +HWND CPaintManagerUI::GetTooltipWindow() const +{ + return m_hwndTooltip; +} + +HDC CPaintManagerUI::GetPaintDC() const +{ + return m_hDcPaint; +} + +POINT CPaintManagerUI::GetMousePos() const +{ + return m_ptLastMousePos; +} + +SIZE CPaintManagerUI::GetClientSize() const +{ + RECT rcClient = { 0 }; + ::GetClientRect(m_hWndPaint, &rcClient); + return CDuiSize(rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); +} + +SIZE CPaintManagerUI::GetInitSize() +{ + return m_szInitWindowSize; +} + +void CPaintManagerUI::SetInitSize(int cx, int cy) +{ + m_szInitWindowSize.cx = cx; + m_szInitWindowSize.cy = cy; + if( m_pRoot == NULL && m_hWndPaint != NULL ) { + ::SetWindowPos(m_hWndPaint, NULL, 0, 0, cx, cy, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); + } +} + +RECT& CPaintManagerUI::GetSizeBox() +{ + return m_rcSizeBox; +} + +void CPaintManagerUI::SetSizeBox(RECT& rcSizeBox) +{ + m_rcSizeBox = rcSizeBox; +} + +RECT& CPaintManagerUI::GetCaptionRect() +{ + return m_rcCaption; +} + +void CPaintManagerUI::SetCaptionRect(RECT& rcCaption) +{ + m_rcCaption = rcCaption; +} + +SIZE CPaintManagerUI::GetRoundCorner() const +{ + return m_szRoundCorner; +} + +void CPaintManagerUI::SetRoundCorner(int cx, int cy) +{ + m_szRoundCorner.cx = cx; + m_szRoundCorner.cy = cy; +} + +SIZE CPaintManagerUI::GetMinInfo() const +{ + return m_szMinWindow; +} + +void CPaintManagerUI::SetMinInfo(int cx, int cy) +{ + ASSERT(cx>=0 && cy>=0); + m_szMinWindow.cx = cx; + m_szMinWindow.cy = cy; +} + +SIZE CPaintManagerUI::GetMaxInfo() const +{ + return m_szMaxWindow; +} + +void CPaintManagerUI::SetMaxInfo(int cx, int cy) +{ + ASSERT(cx>=0 && cy>=0); + m_szMaxWindow.cx = cx; + m_szMaxWindow.cy = cy; +} + +int CPaintManagerUI::GetTransparent() const +{ + return m_nOpacity; +} +void CPaintManagerUI::SetTransparent(int nOpacity) +{ + if (nOpacity<0) + m_nOpacity = 0; + else if (nOpacity>255) + m_nOpacity = 255; + else + m_nOpacity = nOpacity; + if( m_hWndPaint != NULL ) { + typedef BOOL (__stdcall *PFUNCSETLAYEREDWINDOWATTR)(HWND, COLORREF, BYTE, DWORD); + PFUNCSETLAYEREDWINDOWATTR fSetLayeredWindowAttributes; + + HMODULE hUser32 = ::GetModuleHandle(_T("User32.dll")); + if (hUser32) + { + fSetLayeredWindowAttributes = + (PFUNCSETLAYEREDWINDOWATTR)::GetProcAddress(hUser32, "SetLayeredWindowAttributes"); + if( fSetLayeredWindowAttributes == NULL ) return; + } + + DWORD dwStyle = ::GetWindowLong(m_hWndPaint, GWL_EXSTYLE); + DWORD dwNewStyle = dwStyle; + if( nOpacity >= 0 && nOpacity < 256 ) dwNewStyle |= WS_EX_LAYERED; + else dwNewStyle &= ~WS_EX_LAYERED; + if(dwStyle != dwNewStyle) ::SetWindowLong(m_hWndPaint, GWL_EXSTYLE, dwNewStyle); + fSetLayeredWindowAttributes(m_hWndPaint, 0, nOpacity, LWA_ALPHA); + } +} + +void CPaintManagerUI::SetBackgroundTransparent(bool bTrans) +{ + m_bAlphaBackground = bTrans; +} + +bool CPaintManagerUI::IsBackgroundTransparent() +{ + return m_bAlphaBackground; +} + +bool CPaintManagerUI::ShowCaret(bool bShow) +{ + if(m_bCaretShowing == bShow) + return true; + + m_bCaretShowing = bShow; + if(!bShow) + { + ::KillTimer(m_hWndPaint, kCaretTimerID); + if(m_bCaretActive) + { + Invalidate(m_rtCaret); + } + m_bCaretActive = false; + } + else + { + ::SetTimer(m_hWndPaint, kCaretTimerID, ::GetCaretBlinkTime(), NULL); + if(!m_bCaretActive) + { + Invalidate(m_rtCaret); + m_bCaretActive = true; + } + } + + return true; +} + +bool CPaintManagerUI::SetCaretPos(CRichEditUI* obj, int x, int y) +{ + if(!::SetCaretPos(x, y)) + return false; + + m_currentCaretObject = obj; + RECT tempRt = m_rtCaret; + int w = m_rtCaret.right - m_rtCaret.left; + int h = m_rtCaret.bottom - m_rtCaret.top; + m_rtCaret.left = x; + m_rtCaret.top = y; + m_rtCaret.right = x + w; + m_rtCaret.bottom = y + h; + Invalidate(tempRt); + Invalidate(m_rtCaret); + + return true; +} + +CRichEditUI* CPaintManagerUI::GetCurrentCaretObject() +{ + return m_currentCaretObject; +} + +bool CPaintManagerUI::CreateCaret(HBITMAP hBmp, int nWidth, int nHeight) +{ + ::CreateCaret(m_hWndPaint, hBmp, nWidth, nHeight); + //TODO hBmpλͼ + m_rtCaret.right = m_rtCaret.left + nWidth; + m_rtCaret.bottom = m_rtCaret.top + nHeight; + return true; +} + +void CPaintManagerUI::DrawCaret(HDC hDC, const RECT& rcPaint) +{ + if(m_currentCaretObject && (!m_currentCaretObject->IsFocused() || m_hWndPaint != ::GetFocus())) + { + ::KillTimer(m_hWndPaint, kCaretTimerID); + if(m_bCaretActive) + { + Invalidate(m_rtCaret); + } + m_bCaretActive = false; + return; + } + + if(m_bCaretActive && m_bCaretShowing && m_currentCaretObject) + { + RECT temp = {}; + if(::IntersectRect(&temp, &rcPaint, &m_rtCaret)) + { + DWORD dwColor = m_currentCaretObject->GetTextColor(); + if(dwColor == 0) + dwColor = m_dwDefaultFontColor; + CRenderEngine::DrawColor(hDC, temp, dwColor); + } + } +} + +CShadowUI* CPaintManagerUI::GetShadow() +{ + return &m_shadow; +} + +void CPaintManagerUI::SetUseGdiplusText(bool bUse) +{ + m_bUseGdiplusText = bUse; +} + +bool CPaintManagerUI::IsUseGdiplusText() const +{ + return m_bUseGdiplusText; +} + +bool CPaintManagerUI::PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& /*lRes*/) +{ + for( int i = 0; i < m_aPreMessageFilters.GetSize(); i++ ) + { + bool bHandled = false; + LRESULT lResult = static_cast(m_aPreMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled); + if( bHandled ) { + return true; + } + } + switch( uMsg ) { + case WM_KEYDOWN: + { + // Tabbing between controls + if( wParam == VK_TAB ) { + if( m_pFocus && m_pFocus->IsVisible() && m_pFocus->IsEnabled() && _tcsstr(m_pFocus->GetClass(), _T("RichEditUI")) != NULL ) { + if( static_cast(m_pFocus)->IsWantTab() ) return false; + } + SetNextTabControl(::GetKeyState(VK_SHIFT) >= 0); + return true; + } + } + break; + case WM_SYSCHAR: + { + // Handle ALT-shortcut key-combinations + FINDSHORTCUT fs = { 0 }; + fs.ch = toupper((int)wParam); + CControlUI* pControl = m_pRoot->FindControl(__FindControlFromShortcut, &fs, UIFIND_ENABLED | UIFIND_ME_FIRST | UIFIND_TOP_FIRST); + if( pControl != NULL ) { + pControl->SetFocus(); + pControl->Activate(); + return true; + } + } + break; + case WM_SYSKEYDOWN: + { + if( m_pFocus != NULL ) { + TEventUI event = { 0 }; + event.Type = UIEVENT_SYSKEY; + event.chKey = (TCHAR)wParam; + event.ptMouse = m_ptLastMousePos; + event.wKeyState = MapKeyState(); + event.dwTimestamp = ::GetTickCount(); + m_pFocus->Event(event); + } + } + break; + } + return false; +} + +bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes) +{ +//#ifdef _DEBUG +// switch( uMsg ) { +// case WM_NCPAINT: +// case WM_NCHITTEST: +// case WM_SETCURSOR: +// break; +// default: +// DUITRACE(_T("MSG: %-20s (%08ld)"), DUITRACEMSG(uMsg), ::GetTickCount()); +// } +//#endif + // Not ready yet? + if( m_hWndPaint == NULL ) return false; + + TNotifyUI* pMsg = NULL; + while( pMsg = static_cast(m_aAsyncNotify.GetAt(0)) ) { + m_aAsyncNotify.Remove(0); + if( pMsg->pSender != NULL ) { + if( pMsg->pSender->OnNotify ) pMsg->pSender->OnNotify(pMsg); + } + for( int j = 0; j < m_aNotifiers.GetSize(); j++ ) { + static_cast(m_aNotifiers[j])->Notify(*pMsg); + } + delete pMsg; + } + + // Cycle through listeners + for( int i = 0; i < m_aMessageFilters.GetSize(); i++ ) + { + bool bHandled = false; + LRESULT lResult = static_cast(m_aMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled); + if( bHandled ) { + lRes = lResult; + return true; + } + } + // Custom handling of events + switch( uMsg ) { + case WM_APP + 1: + { + for( int i = 0; i < m_aDelayedCleanup.GetSize(); i++ ) + delete static_cast(m_aDelayedCleanup[i]); + m_aDelayedCleanup.Empty(); + } + break; + case WM_CLOSE: + { + // Make sure all matching "closing" events are sent + TEventUI event = { 0 }; + event.ptMouse = m_ptLastMousePos; + event.dwTimestamp = ::GetTickCount(); + if( m_pEventHover != NULL ) { + event.Type = UIEVENT_MOUSELEAVE; + event.pSender = m_pEventHover; + m_pEventHover->Event(event); + } + if( m_pEventClick != NULL ) { + event.Type = UIEVENT_BUTTONUP; + event.pSender = m_pEventClick; + m_pEventClick->Event(event); + } + + SetFocus(NULL); + + // Hmmph, the usual Windows tricks to avoid + // focus loss... + HWND hwndParent = GetWindowOwner(m_hWndPaint); + if( hwndParent != NULL ) ::SetFocus(hwndParent); + } + break; + case WM_ERASEBKGND: + { + // We'll do the painting here... + lRes = 1; + } + return true; + case WM_PAINT: + { + // Should we paint? + RECT rcPaint = {0}; + if(!::GetUpdateRect(m_hWndPaint, &rcPaint, FALSE)) return true; + if(m_pRoot == NULL) + { + PAINTSTRUCT ps = {0}; + ::BeginPaint(m_hWndPaint, &ps); + ::EndPaint(m_hWndPaint, &ps); + return true; + } + + // Begin Windows paint + PAINTSTRUCT ps = {0}; + ::BeginPaint(m_hWndPaint, &ps); + + + // Do we need to resize anything? + // This is the time where we layout the controls on the form. + // We delay this even from the WM_SIZE messages since resizing can be + // a very expensize operation. + if(m_bUpdateNeeded) + { + m_bUpdateNeeded = false; + RECT rcClient = {0}; + ::GetClientRect(m_hWndPaint, &rcClient); + if(!::IsRectEmpty(&rcClient)) + { + if(m_pRoot->IsUpdateNeeded()) + { + if( !::IsIconic(m_hWndPaint)) //redrain޸bug + m_pRoot->SetPos(rcClient); + if(m_hDcOffscreen != NULL) ::DeleteDC(m_hDcOffscreen); + if(m_hbmpOffscreen != NULL) ::DeleteObject(m_hbmpOffscreen); + m_hDcOffscreen = NULL; + m_hbmpOffscreen = NULL; + m_pBmpOffscreenBits = NULL; + } + else + { + CControlUI* pControl = NULL; + while(pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST)) + { + pControl->SetPos(pControl->GetPos()); + } + } + // We'll want to notify the window when it is first initialized + // with the correct layout. The window form would take the time + // to submit swipes/animations. + if(m_bFirstLayout) + { + m_bFirstLayout = false; + SendNotify(m_pRoot, _T("windowinit"), 0, 0, false); + } + } + } + // Set focus to first control? + if(m_bFocusNeeded) + { + SetNextTabControl(); + } + + // Ƿ˰͸ģʽ + if(m_bAlphaBackground) + { + // òʽ + DWORD dwExStyle = GetWindowLong(m_hWndPaint, GWL_EXSTYLE); + if((dwExStyle&WS_EX_LAYERED) != WS_EX_LAYERED) + SetWindowLong(m_hWndPaint, GWL_EXSTYLE, dwExStyle|WS_EX_LAYERED); + + RECT rcClient = {0}; + GetClientRect(m_hWndPaint, &rcClient); + // Сָˢ + if (!m_bIsRestore) + { + UnionRect(&rcPaint, &rcPaint, &m_rcInvalidate); + ::ZeroMemory(&m_rcInvalidate, sizeof(m_rcInvalidate)); + } + else + { + rcPaint = rcClient; + m_bIsRestore = false; + } + + int nClientWidth = rcClient.right - rcClient.left; + int nClientHeight = rcClient.bottom - rcClient.top; + if(m_bOffscreenPaint && m_hbmpOffscreen == NULL) + { + m_hDcOffscreen = ::CreateCompatibleDC(m_hDcPaint); + BITMAPINFO bmi; + ::ZeroMemory(&bmi, sizeof(BITMAPINFO)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = nClientWidth; + bmi.bmiHeader.biHeight = -nClientHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = nClientWidth * nClientHeight * 4; + bmi.bmiHeader.biClrUsed = 0; + m_hbmpOffscreen = ::CreateDIBSection(m_hDcPaint, &bmi, DIB_RGB_COLORS, + (void**)&m_pBmpOffscreenBits, NULL, 0); + + ASSERT(m_hDcOffscreen); + ASSERT(m_hbmpOffscreen); + } + + HBITMAP hOldBitmap = (HBITMAP)::SelectObject(m_hDcOffscreen, m_hbmpOffscreen); + int iSaveDC = ::SaveDC(m_hDcOffscreen); + CRenderEngine::ClearAlphaPixel(m_pBmpOffscreenBits, nClientWidth, &rcPaint); + m_pRoot->DoPaint(m_hDcOffscreen, rcPaint); + DrawCaret(m_hDcOffscreen, rcPaint); + for(int i = 0; i < m_aPostPaintControls.GetSize(); i++) + { + CControlUI* pPostPaintControl = static_cast(m_aPostPaintControls[i]); + pPostPaintControl->DoPostPaint(m_hDcOffscreen, ps.rcPaint); + } + CRenderEngine::RestoreAlphaColor(m_pBmpOffscreenBits, nClientWidth, &rcPaint); + ::RestoreDC(m_hDcOffscreen, iSaveDC); + + // + RECT rcWnd = {0}; + ::GetWindowRect(m_hWndPaint, &rcWnd); + POINT pt = {rcWnd.left, rcWnd.top}; + SIZE szWindow = {rcWnd.right-rcWnd.left, rcWnd.bottom-rcWnd.top}; + POINT ptSrc = {0, 0}; + BLENDFUNCTION blendPixelFunction = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA}; + ::UpdateLayeredWindow(m_hWndPaint, NULL, &pt, &szWindow, m_hDcOffscreen, + &ptSrc, 0, &blendPixelFunction, ULW_ALPHA); + + ::SelectObject(m_hDcOffscreen, hOldBitmap); + } + else + { + if(m_bOffscreenPaint && m_hbmpOffscreen == NULL) + { + RECT rcClient = {0}; + ::GetClientRect(m_hWndPaint, &rcClient); + m_hDcOffscreen = ::CreateCompatibleDC(m_hDcPaint); + m_hbmpOffscreen = ::CreateCompatibleBitmap(m_hDcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); + ASSERT(m_hDcOffscreen); + ASSERT(m_hbmpOffscreen); + } + + if(m_bOffscreenPaint) + { + HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(m_hDcOffscreen, m_hbmpOffscreen); + int iSaveDC = ::SaveDC(m_hDcOffscreen); + m_pRoot->DoPaint(m_hDcOffscreen, ps.rcPaint); + DrawCaret(m_hDcOffscreen, ps.rcPaint); + for(int i = 0; i < m_aPostPaintControls.GetSize(); i++) + { + CControlUI* pPostPaintControl = static_cast(m_aPostPaintControls[i]); + pPostPaintControl->DoPostPaint(m_hDcOffscreen, ps.rcPaint); + } + ::RestoreDC(m_hDcOffscreen, iSaveDC); + ::BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, + ps.rcPaint.bottom - ps.rcPaint.top, m_hDcOffscreen, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY); + ::SelectObject(m_hDcOffscreen, hOldBitmap); + + } + else + { + // A standard paint job + int iSaveDC = ::SaveDC(ps.hdc); + m_pRoot->DoPaint(ps.hdc, ps.rcPaint); + DrawCaret(ps.hdc, ps.rcPaint); + ::RestoreDC(ps.hdc, iSaveDC); + } + + } + + // All Done! + ::EndPaint(m_hWndPaint, &ps); + + } + // If any of the painting requested a resize again, we'll need + // to invalidate the entire window once more. + if(m_bUpdateNeeded) + { + ::InvalidateRect(m_hWndPaint, NULL, FALSE); + } + return true; + case WM_SYSCOMMAND: + { + if (SC_RESTORE == (wParam & 0xfff0)) + { + m_bIsRestore = true; + } + return true; + } + break; + case WM_GETMINMAXINFO: + { + LPMINMAXINFO lpMMI = (LPMINMAXINFO) lParam; + if( m_szMinWindow.cx > 0 ) lpMMI->ptMinTrackSize.x = m_szMinWindow.cx; + if( m_szMinWindow.cy > 0 ) lpMMI->ptMinTrackSize.y = m_szMinWindow.cy; + if( m_szMaxWindow.cx > 0 ) lpMMI->ptMaxTrackSize.x = m_szMaxWindow.cx; + if( m_szMaxWindow.cy > 0 ) lpMMI->ptMaxTrackSize.y = m_szMaxWindow.cy; + } + break; + case WM_SIZE: + { + if( m_pFocus != NULL ) { + TEventUI event = { 0 }; + event.Type = UIEVENT_WINDOWSIZE; + event.pSender = m_pFocus; + event.dwTimestamp = ::GetTickCount(); + m_pFocus->Event(event); + } + if( m_pRoot != NULL ) m_pRoot->NeedUpdate(); + } + return true; + case WM_TIMER: + { + if(kCaretTimerID == LOWORD(wParam)){ + //DUI__Trace(_T("WM_TIMER:%d (%d,%d)"), m_bCaretActive, m_rtCaret.left, m_rtCaret.top); + Invalidate(m_rtCaret); + m_bCaretActive = !m_bCaretActive; + } + else{ + for( int i = 0; i < m_aTimers.GetSize(); i++ ) { + const TIMERINFO* pTimer = static_cast(m_aTimers[i]); + if(pTimer->hWnd == m_hWndPaint && + pTimer->uWinTimer == LOWORD(wParam) && + pTimer->bKilled == false) + { + TEventUI event = { 0 }; + event.Type = UIEVENT_TIMER; + event.pSender = pTimer->pSender; + event.wParam = pTimer->nLocalID; + event.dwTimestamp = ::GetTickCount(); + pTimer->pSender->Event(event); + break; + } + } + } + + } + break; + case WM_MOUSEHOVER: + { + m_bMouseTracking = false; + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + CControlUI* pHover = FindControl(pt); + if( pHover == NULL ) break; + // Generate mouse hover event + if( m_pEventHover != NULL ) { + TEventUI event = { 0 }; + event.ptMouse = pt; + event.Type = UIEVENT_MOUSEHOVER; + event.pSender = m_pEventHover; + event.dwTimestamp = ::GetTickCount(); + m_pEventHover->Event(event); + } + // Create tooltip information + CDuiString sToolTip = pHover->GetToolTip(); + if( sToolTip.IsEmpty() ) return true; + ::ZeroMemory(&m_ToolTip, sizeof(TOOLINFO)); + m_ToolTip.cbSize = sizeof(TOOLINFO); + m_ToolTip.uFlags = TTF_IDISHWND; + m_ToolTip.hwnd = m_hWndPaint; + m_ToolTip.uId = (UINT_PTR) m_hWndPaint; + m_ToolTip.hinst = m_hInstance; + m_ToolTip.lpszText = const_cast( (LPCTSTR) sToolTip ); + m_ToolTip.rect = pHover->GetPos(); + if( m_hwndTooltip == NULL ) { + m_hwndTooltip = ::CreateWindowEx(0, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hWndPaint, NULL, m_hInstance, NULL); + ::SendMessage(m_hwndTooltip, TTM_ADDTOOL, 0, (LPARAM) &m_ToolTip); + } + ::SendMessage( m_hwndTooltip,TTM_SETMAXTIPWIDTH,0, pHover->GetToolTipWidth()); + ::SendMessage(m_hwndTooltip, TTM_SETTOOLINFO, 0, (LPARAM) &m_ToolTip); + ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, TRUE, (LPARAM) &m_ToolTip); + } + return true; + case WM_MOUSELEAVE: + { + if( m_hwndTooltip != NULL ) ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, FALSE, (LPARAM) &m_ToolTip); + if( m_bMouseTracking ) ::SendMessage(m_hWndPaint, WM_MOUSEMOVE, 0, (LPARAM) -1); + m_bMouseTracking = false; + } + break; + case WM_MOUSEMOVE: + { + // Start tracking this entire window again... + if( !m_bMouseTracking ) { + TRACKMOUSEEVENT tme = { 0 }; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_HOVER | TME_LEAVE; + tme.hwndTrack = m_hWndPaint; + tme.dwHoverTime = m_hwndTooltip == NULL ? 400UL : (DWORD) ::SendMessage(m_hwndTooltip, TTM_GETDELAYTIME, TTDT_INITIAL, 0L); + _TrackMouseEvent(&tme); + m_bMouseTracking = true; + } + // Generate the appropriate mouse messages + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + m_ptLastMousePos = pt; + CControlUI* pNewHover = FindControl(pt); + if( pNewHover != NULL && pNewHover->GetManager() != this ) break; + TEventUI event = { 0 }; + event.ptMouse = pt; + event.dwTimestamp = ::GetTickCount(); + if( pNewHover != m_pEventHover && m_pEventHover != NULL ) { + event.Type = UIEVENT_MOUSELEAVE; + event.pSender = m_pEventHover; + m_pEventHover->Event(event); + m_pEventHover = NULL; + if( m_hwndTooltip != NULL ) ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, FALSE, (LPARAM) &m_ToolTip); + } + if( pNewHover != m_pEventHover && pNewHover != NULL ) { + event.Type = UIEVENT_MOUSEENTER; + event.pSender = pNewHover; + pNewHover->Event(event); + m_pEventHover = pNewHover; + } + if( m_pEventClick != NULL ) { + event.Type = UIEVENT_MOUSEMOVE; + event.pSender = m_pEventClick; + m_pEventClick->Event(event); + } + else if( pNewHover != NULL ) { + event.Type = UIEVENT_MOUSEMOVE; + event.pSender = pNewHover; + pNewHover->Event(event); + } + } + break; + case WM_LBUTTONDOWN: + { + // We alway set focus back to our app (this helps + // when Win32 child windows are placed on the dialog + // and we need to remove them on focus change). + ::SetFocus(m_hWndPaint); + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + m_ptLastMousePos = pt; + CControlUI* pControl = FindControl(pt); + if( pControl == NULL ) break; + if( pControl->GetManager() != this ) break; + m_pEventClick = pControl; + pControl->SetFocus(); + SetCapture(); + TEventUI event = { 0 }; + event.Type = UIEVENT_BUTTONDOWN; + event.pSender = pControl; + event.wParam = wParam; + event.lParam = lParam; + event.ptMouse = pt; + event.wKeyState = (WORD)wParam; + event.dwTimestamp = ::GetTickCount(); + pControl->Event(event); + } + break; + case WM_LBUTTONDBLCLK: + { + ::SetFocus(m_hWndPaint); + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + m_ptLastMousePos = pt; + CControlUI* pControl = FindControl(pt); + if( pControl == NULL ) break; + if( pControl->GetManager() != this ) break; + SetCapture(); + TEventUI event = { 0 }; + event.Type = UIEVENT_DBLCLICK; + event.pSender = pControl; + event.ptMouse = pt; + event.wKeyState = (WORD)wParam; + event.dwTimestamp = ::GetTickCount(); + pControl->Event(event); + m_pEventClick = pControl; + } + break; + case WM_LBUTTONUP: + { + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + m_ptLastMousePos = pt; + if( m_pEventClick == NULL ) break; + ReleaseCapture(); + TEventUI event = { 0 }; + event.Type = UIEVENT_BUTTONUP; + event.pSender = m_pEventClick; + event.wParam = wParam; + event.lParam = lParam; + event.ptMouse = pt; + event.wKeyState = (WORD)wParam; + event.dwTimestamp = ::GetTickCount(); + m_pEventClick->Event(event); + m_pEventClick = NULL; + } + break; + case WM_RBUTTONDOWN: + { + ::SetFocus(m_hWndPaint); + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + m_ptLastMousePos = pt; + CControlUI* pControl = FindControl(pt); + if( pControl == NULL ) break; + if( pControl->GetManager() != this ) break; + pControl->SetFocus(); + SetCapture(); + TEventUI event = { 0 }; + event.Type = UIEVENT_RBUTTONDOWN; + event.pSender = pControl; + event.wParam = wParam; + event.lParam = lParam; + event.ptMouse = pt; + event.wKeyState = (WORD)wParam; + event.dwTimestamp = ::GetTickCount(); + pControl->Event(event); + m_pEventClick = pControl; + } + break; + case WM_CONTEXTMENU: + { + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + ::ScreenToClient(m_hWndPaint, &pt); + m_ptLastMousePos = pt; + if( m_pEventClick == NULL ) break; + ReleaseCapture(); + TEventUI event = { 0 }; + event.Type = UIEVENT_CONTEXTMENU; + event.pSender = m_pEventClick; + event.ptMouse = pt; + event.wKeyState = (WORD)wParam; + event.lParam = (LPARAM)m_pEventClick; + event.dwTimestamp = ::GetTickCount(); + m_pEventClick->Event(event); + m_pEventClick = NULL; + } + break; + case WM_MOUSEWHEEL: + { + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + ::ScreenToClient(m_hWndPaint, &pt); + m_ptLastMousePos = pt; + CControlUI* pControl = FindControl(pt); + if( pControl == NULL ) break; + if( pControl->GetManager() != this ) break; + int zDelta = (int) (short) HIWORD(wParam); + TEventUI event = { 0 }; + event.Type = UIEVENT_SCROLLWHEEL; + event.pSender = pControl; + event.wParam = MAKELPARAM(zDelta < 0 ? SB_LINEDOWN : SB_LINEUP, 0); + event.lParam = lParam; + event.wKeyState = MapKeyState(); + event.dwTimestamp = ::GetTickCount(); + pControl->Event(event); + + // Let's make sure that the scroll item below the cursor is the same as before... + ::SendMessage(m_hWndPaint, WM_MOUSEMOVE, 0, (LPARAM) MAKELPARAM(m_ptLastMousePos.x, m_ptLastMousePos.y)); + } + break; + case WM_CHAR: + { + if( m_pFocus == NULL ) break; + TEventUI event = { 0 }; + event.Type = UIEVENT_CHAR; + event.chKey = (TCHAR)wParam; + event.ptMouse = m_ptLastMousePos; + event.wKeyState = MapKeyState(); + event.dwTimestamp = ::GetTickCount(); + m_pFocus->Event(event); + } + break; + case WM_KEYDOWN: + { + if( m_pFocus == NULL ) break; + TEventUI event = { 0 }; + event.Type = UIEVENT_KEYDOWN; + event.chKey = (TCHAR)wParam; + event.ptMouse = m_ptLastMousePos; + event.wKeyState = MapKeyState(); + event.dwTimestamp = ::GetTickCount(); + m_pFocus->Event(event); + m_pEventKey = m_pFocus; + } + break; + case WM_KEYUP: + { + if( m_pEventKey == NULL ) break; + TEventUI event = { 0 }; + event.Type = UIEVENT_KEYUP; + event.chKey = (TCHAR)wParam; + event.ptMouse = m_ptLastMousePos; + event.wKeyState = MapKeyState(); + event.dwTimestamp = ::GetTickCount(); + m_pEventKey->Event(event); + m_pEventKey = NULL; + } + break; + case WM_SETCURSOR: + { + if( LOWORD(lParam) != HTCLIENT ) break; + if( m_bMouseCapture ) return true; + + POINT pt = { 0 }; + ::GetCursorPos(&pt); + ::ScreenToClient(m_hWndPaint, &pt); + CControlUI* pControl = FindControl(pt); + if( pControl == NULL ) break; + if( (pControl->GetControlFlags() & UIFLAG_SETCURSOR) == 0 ) break; + TEventUI event = { 0 }; + event.Type = UIEVENT_SETCURSOR; + event.wParam = wParam; + event.lParam = lParam; + event.ptMouse = pt; + event.wKeyState = MapKeyState(); + event.dwTimestamp = ::GetTickCount(); + pControl->Event(event); + } + return true; + case WM_NOTIFY: + { + LPNMHDR lpNMHDR = (LPNMHDR) lParam; + if( lpNMHDR != NULL ) lRes = ::SendMessage(lpNMHDR->hwndFrom, OCM__BASE + uMsg, wParam, lParam); + return true; + } + break; + case WM_COMMAND: + { + if( lParam == 0 ) break; + HWND hWndChild = (HWND) lParam; + lRes = ::SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam); + return true; + } + break; + case WM_CTLCOLOREDIT: + case WM_CTLCOLORSTATIC: + { + // Refer To: http://msdn.microsoft.com/en-us/library/bb761691(v=vs.85).aspx + // Read-only or disabled edit controls do not send the WM_CTLCOLOREDIT message; instead, they send the WM_CTLCOLORSTATIC message. + if( lParam == 0 ) break; + HWND hWndChild = (HWND) lParam; + lRes = ::SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam); + return true; + } + break; + default: + break; + } + + pMsg = NULL; + while( pMsg = static_cast(m_aAsyncNotify.GetAt(0)) ) { + m_aAsyncNotify.Remove(0); + if( pMsg->pSender != NULL ) { + if( pMsg->pSender->OnNotify ) pMsg->pSender->OnNotify(pMsg); + } + for( int j = 0; j < m_aNotifiers.GetSize(); j++ ) { + static_cast(m_aNotifiers[j])->Notify(*pMsg); + } + delete pMsg; + } + + return false; +} + +void CPaintManagerUI::NeedUpdate() +{ + m_bUpdateNeeded = true; +} + +void CPaintManagerUI::Invalidate(RECT& rcItem) +{ + ::InvalidateRect(m_hWndPaint, &rcItem, FALSE); + m_rcInvalidate = rcItem; + +} + +bool CPaintManagerUI::AttachDialog(CControlUI* pControl) +{ + ASSERT(::IsWindow(m_hWndPaint)); + // Reset any previous attachment + SetFocus(NULL); + m_pEventKey = NULL; + m_pEventHover = NULL; + m_pEventClick = NULL; + // Remove the existing control-tree. We might have gotten inside this function as + // a result of an event fired or similar, so we cannot just delete the objects and + // pull the internal memory of the calling code. We'll delay the cleanup. + if( m_pRoot != NULL ) { + m_aPostPaintControls.Empty(); + AddDelayedCleanup(m_pRoot); + } + // Set the dialog root element + m_pRoot = pControl; + // Go ahead... + m_bUpdateNeeded = true; + m_bFirstLayout = true; + m_bFocusNeeded = true; + + m_shadow.Create(this); + + // Initiate all control + return InitControls(pControl); +} + +bool CPaintManagerUI::InitControls(CControlUI* pControl, CControlUI* pParent /*= NULL*/) +{ + ASSERT(pControl); + if( pControl == NULL ) return false; + pControl->SetManager(this, pParent != NULL ? pParent : pControl->GetParent(), true); + pControl->FindControl(__FindControlFromNameHash, this, UIFIND_ALL); + return true; +} + +void CPaintManagerUI::ReapObjects(CControlUI* pControl) +{ + if( pControl == m_pEventKey ) m_pEventKey = NULL; + if( pControl == m_pEventHover ) m_pEventHover = NULL; + if( pControl == m_pEventClick ) m_pEventClick = NULL; + if( pControl == m_pFocus ) m_pFocus = NULL; + KillTimer(pControl); + const CDuiString& sName = pControl->GetName(); + if( !sName.IsEmpty() ) { + if( pControl == FindControl(sName) ) m_mNameHash.Remove(sName); + } + for( int i = 0; i < m_aAsyncNotify.GetSize(); i++ ) { + TNotifyUI* pMsg = static_cast(m_aAsyncNotify[i]); + if( pMsg->pSender == pControl ) pMsg->pSender = NULL; + } +} + +bool CPaintManagerUI::AddOptionGroup(LPCTSTR pStrGroupName, CControlUI* pControl) +{ + LPVOID lp = m_mOptionGroup.Find(pStrGroupName); + if( lp ) { + CStdPtrArray* aOptionGroup = static_cast(lp); + for( int i = 0; i < aOptionGroup->GetSize(); i++ ) { + if( static_cast(aOptionGroup->GetAt(i)) == pControl ) { + return false; + } + } + aOptionGroup->Add(pControl); + } + else { + CStdPtrArray* aOptionGroup = new CStdPtrArray(6); + aOptionGroup->Add(pControl); + m_mOptionGroup.Insert(pStrGroupName, aOptionGroup); + } + return true; +} + +CStdPtrArray* CPaintManagerUI::GetOptionGroup(LPCTSTR pStrGroupName) +{ + LPVOID lp = m_mOptionGroup.Find(pStrGroupName); + if( lp ) return static_cast(lp); + return NULL; +} + +void CPaintManagerUI::RemoveOptionGroup(LPCTSTR pStrGroupName, CControlUI* pControl) +{ + LPVOID lp = m_mOptionGroup.Find(pStrGroupName); + if( lp ) { + CStdPtrArray* aOptionGroup = static_cast(lp); + if( aOptionGroup == NULL ) return; + for( int i = 0; i < aOptionGroup->GetSize(); i++ ) { + if( static_cast(aOptionGroup->GetAt(i)) == pControl ) { + aOptionGroup->Remove(i); + break; + } + } + if( aOptionGroup->IsEmpty() ) { + delete aOptionGroup; + m_mOptionGroup.Remove(pStrGroupName); + } + } +} + +void CPaintManagerUI::RemoveAllOptionGroups() +{ + CStdPtrArray* aOptionGroup; + for( int i = 0; i< m_mOptionGroup.GetSize(); i++ ) { + if(LPCTSTR key = m_mOptionGroup.GetAt(i)) { + aOptionGroup = static_cast(m_mOptionGroup.Find(key)); + delete aOptionGroup; + } + } + m_mOptionGroup.RemoveAll(); +} + +void CPaintManagerUI::MessageLoop() +{ + MSG msg = { 0 }; + while( ::GetMessage(&msg, NULL, 0, 0) ) { + if( !CPaintManagerUI::TranslateMessage(&msg) ) { + ::TranslateMessage(&msg); + try{ + ::DispatchMessage(&msg); + } catch(...) { + DUITRACE(_T("EXCEPTION: %s(%d)\n"), __FILET__, __LINE__); + #ifdef _DEBUG + throw "CPaintManagerUI::MessageLoop"; + #endif + } + } + } +} + +void CPaintManagerUI::Term() +{ + if( m_bCachedResourceZip && m_hResourceZip != NULL ) { + CloseZip((HZIP)m_hResourceZip); + m_hResourceZip = NULL; + } +} + +CControlUI* CPaintManagerUI::GetFocus() const +{ + return m_pFocus; +} + +void CPaintManagerUI::SetFocus(CControlUI* pControl) +{ + // Paint manager window has focus? + HWND hFocusWnd = ::GetFocus(); + if( hFocusWnd != m_hWndPaint && pControl != m_pFocus ) ::SetFocus(m_hWndPaint); + // Already has focus? + if( pControl == m_pFocus ) return; + // Remove focus from old control + if( m_pFocus != NULL ) + { + TEventUI event = { 0 }; + event.Type = UIEVENT_KILLFOCUS; + event.pSender = pControl; + event.dwTimestamp = ::GetTickCount(); + m_pFocus->Event(event); + SendNotify(m_pFocus, DUI_MSGTYPE_KILLFOCUS); + m_pFocus = NULL; + } + if( pControl == NULL ) return; + // Set focus to new control + if( pControl != NULL + && pControl->GetManager() == this + && pControl->IsVisible() + && pControl->IsEnabled() ) + { + m_pFocus = pControl; + TEventUI event = { 0 }; + event.Type = UIEVENT_SETFOCUS; + event.pSender = pControl; + event.dwTimestamp = ::GetTickCount(); + m_pFocus->Event(event); + SendNotify(m_pFocus, DUI_MSGTYPE_SETFOCUS); + } +} + +void CPaintManagerUI::SetFocusNeeded(CControlUI* pControl) +{ + ::SetFocus(m_hWndPaint); + if( pControl == NULL ) return; + if( m_pFocus != NULL ) { + TEventUI event = { 0 }; + event.Type = UIEVENT_KILLFOCUS; + event.pSender = pControl; + event.dwTimestamp = ::GetTickCount(); + m_pFocus->Event(event); + SendNotify(m_pFocus, DUI_MSGTYPE_KILLFOCUS); + m_pFocus = NULL; + } + FINDTABINFO info = { 0 }; + info.pFocus = pControl; + info.bForward = false; + m_pFocus = m_pRoot->FindControl(__FindControlFromTab, &info, UIFIND_VISIBLE | UIFIND_ENABLED | UIFIND_ME_FIRST); + m_bFocusNeeded = true; + if( m_pRoot != NULL ) m_pRoot->NeedUpdate(); +} + +bool CPaintManagerUI::SetTimer(CControlUI* pControl, UINT nTimerID, UINT uElapse) +{ + ASSERT(pControl!=NULL); + ASSERT(uElapse>0); + for( int i = 0; i< m_aTimers.GetSize(); i++ ) { + TIMERINFO* pTimer = static_cast(m_aTimers[i]); + if( pTimer->pSender == pControl + && pTimer->hWnd == m_hWndPaint + && pTimer->nLocalID == nTimerID ) { + if( pTimer->bKilled == true ) { + if( ::SetTimer(m_hWndPaint, pTimer->uWinTimer, uElapse, NULL) ) { + pTimer->bKilled = false; + return true; + } + return false; + } + return false; + } + } + + m_uTimerID = (++m_uTimerID) % 0xF0; //0xf1-0xfe; + if( !::SetTimer(m_hWndPaint, m_uTimerID, uElapse, NULL) ) return FALSE; + TIMERINFO* pTimer = new TIMERINFO; + if( pTimer == NULL ) return FALSE; + pTimer->hWnd = m_hWndPaint; + pTimer->pSender = pControl; + pTimer->nLocalID = nTimerID; + pTimer->uWinTimer = m_uTimerID; + pTimer->bKilled = false; + return m_aTimers.Add(pTimer); +} + +bool CPaintManagerUI::KillTimer(CControlUI* pControl, UINT nTimerID) +{ + ASSERT(pControl!=NULL); + for( int i = 0; i< m_aTimers.GetSize(); i++ ) { + TIMERINFO* pTimer = static_cast(m_aTimers[i]); + if( pTimer->pSender == pControl + && pTimer->hWnd == m_hWndPaint + && pTimer->nLocalID == nTimerID ) + { + if( pTimer->bKilled == false ) { + if( ::IsWindow(m_hWndPaint) ) ::KillTimer(pTimer->hWnd, pTimer->uWinTimer); + pTimer->bKilled = true; + return true; + } + } + } + return false; +} + +void CPaintManagerUI::KillTimer(CControlUI* pControl) +{ + ASSERT(pControl!=NULL); + int count = m_aTimers.GetSize(); + for( int i = 0, j = 0; i < count; i++ ) { + TIMERINFO* pTimer = static_cast(m_aTimers[i - j]); + if( pTimer->pSender == pControl && pTimer->hWnd == m_hWndPaint ) { + if( pTimer->bKilled == false ) ::KillTimer(pTimer->hWnd, pTimer->uWinTimer); + delete pTimer; + m_aTimers.Remove(i - j); + j++; + } + } +} + +void CPaintManagerUI::RemoveAllTimers() +{ + for( int i = 0; i < m_aTimers.GetSize(); i++ ) { + TIMERINFO* pTimer = static_cast(m_aTimers[i]); + if( pTimer->hWnd == m_hWndPaint ) { + if( pTimer->bKilled == false ) { + if( ::IsWindow(m_hWndPaint) ) ::KillTimer(m_hWndPaint, pTimer->uWinTimer); + } + delete pTimer; + } + } + + m_aTimers.Empty(); +} + +void CPaintManagerUI::SetCapture() +{ + ::SetCapture(m_hWndPaint); + m_bMouseCapture = true; +} + +void CPaintManagerUI::ReleaseCapture() +{ + ::ReleaseCapture(); + m_bMouseCapture = false; +} + +bool CPaintManagerUI::IsCaptured() +{ + return m_bMouseCapture; +} + +bool CPaintManagerUI::SetNextTabControl(bool bForward) +{ + // If we're in the process of restructuring the layout we can delay the + // focus calulation until the next repaint. + if( m_bUpdateNeeded && bForward ) { + m_bFocusNeeded = true; + ::InvalidateRect(m_hWndPaint, NULL, FALSE); + return true; + } + // Find next/previous tabbable control + FINDTABINFO info1 = { 0 }; + info1.pFocus = m_pFocus; + info1.bForward = bForward; + CControlUI* pControl = m_pRoot->FindControl(__FindControlFromTab, &info1, UIFIND_VISIBLE | UIFIND_ENABLED | UIFIND_ME_FIRST); + if( pControl == NULL ) { + if( bForward ) { + // Wrap around + FINDTABINFO info2 = { 0 }; + info2.pFocus = bForward ? NULL : info1.pLast; + info2.bForward = bForward; + pControl = m_pRoot->FindControl(__FindControlFromTab, &info2, UIFIND_VISIBLE | UIFIND_ENABLED | UIFIND_ME_FIRST); + } + else { + pControl = info1.pLast; + } + } + if( pControl != NULL ) SetFocus(pControl); + m_bFocusNeeded = false; + return true; +} + +bool CPaintManagerUI::AddNotifier(INotifyUI* pNotifier) +{ + ASSERT(m_aNotifiers.Find(pNotifier)<0); + return m_aNotifiers.Add(pNotifier); +} + +bool CPaintManagerUI::RemoveNotifier(INotifyUI* pNotifier) +{ + for( int i = 0; i < m_aNotifiers.GetSize(); i++ ) { + if( static_cast(m_aNotifiers[i]) == pNotifier ) { + return m_aNotifiers.Remove(i); + } + } + return false; +} + +bool CPaintManagerUI::AddPreMessageFilter(IMessageFilterUI* pFilter) +{ + ASSERT(m_aPreMessageFilters.Find(pFilter)<0); + return m_aPreMessageFilters.Add(pFilter); +} + +bool CPaintManagerUI::RemovePreMessageFilter(IMessageFilterUI* pFilter) +{ + for( int i = 0; i < m_aPreMessageFilters.GetSize(); i++ ) { + if( static_cast(m_aPreMessageFilters[i]) == pFilter ) { + return m_aPreMessageFilters.Remove(i); + } + } + return false; +} + +bool CPaintManagerUI::AddMessageFilter(IMessageFilterUI* pFilter) +{ + ASSERT(m_aMessageFilters.Find(pFilter)<0); + return m_aMessageFilters.Add(pFilter); +} + +bool CPaintManagerUI::RemoveMessageFilter(IMessageFilterUI* pFilter) +{ + for( int i = 0; i < m_aMessageFilters.GetSize(); i++ ) { + if( static_cast(m_aMessageFilters[i]) == pFilter ) { + return m_aMessageFilters.Remove(i); + } + } + return false; +} + +int CPaintManagerUI::GetPostPaintCount() const +{ + return m_aPostPaintControls.GetSize(); +} + +bool CPaintManagerUI::AddPostPaint(CControlUI* pControl) +{ + ASSERT(m_aPostPaintControls.Find(pControl) < 0); + return m_aPostPaintControls.Add(pControl); +} + +bool CPaintManagerUI::RemovePostPaint(CControlUI* pControl) +{ + for( int i = 0; i < m_aPostPaintControls.GetSize(); i++ ) { + if( static_cast(m_aPostPaintControls[i]) == pControl ) { + return m_aPostPaintControls.Remove(i); + } + } + return false; +} + +bool CPaintManagerUI::SetPostPaintIndex(CControlUI* pControl, int iIndex) +{ + RemovePostPaint(pControl); + return m_aPostPaintControls.InsertAt(iIndex, pControl); +} + +void CPaintManagerUI::AddDelayedCleanup(CControlUI* pControl) +{ + pControl->SetManager(this, NULL, false); + m_aDelayedCleanup.Add(pControl); + ::PostMessage(m_hWndPaint, WM_APP + 1, 0, 0L); +} + +void CPaintManagerUI::SendNotify(CControlUI* pControl, LPCTSTR pstrMessage, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/, bool bAsync /*= false*/) +{ + TNotifyUI Msg; + Msg.pSender = pControl; + Msg.sType = pstrMessage; + Msg.wParam = wParam; + Msg.lParam = lParam; + SendNotify(Msg, bAsync); +} + +void CPaintManagerUI::SendNotify(TNotifyUI& Msg, bool bAsync /*= false*/) +{ + Msg.ptMouse = m_ptLastMousePos; + Msg.dwTimestamp = ::GetTickCount(); + if( m_bUsedVirtualWnd ) + { + Msg.sVirtualWnd = Msg.pSender->GetVirtualWnd(); + } + + if( !bAsync ) { + // Send to all listeners + if( Msg.pSender != NULL ) { + if( Msg.pSender->OnNotify ) Msg.pSender->OnNotify(&Msg); + } + for( int i = 0; i < m_aNotifiers.GetSize(); i++ ) { + static_cast(m_aNotifiers[i])->Notify(Msg); + } + } + else { + TNotifyUI *pMsg = new TNotifyUI; + pMsg->pSender = Msg.pSender; + pMsg->sType = Msg.sType; + pMsg->wParam = Msg.wParam; + pMsg->lParam = Msg.lParam; + pMsg->ptMouse = Msg.ptMouse; + pMsg->dwTimestamp = Msg.dwTimestamp; + m_aAsyncNotify.Add(pMsg); + } +} + +bool CPaintManagerUI::UseParentResource(CPaintManagerUI* pm) +{ + if( pm == NULL ) { + m_pParentResourcePM = NULL; + return true; + } + if( pm == this ) return false; + + CPaintManagerUI* pParentPM = pm->GetParentResource(); + while( pParentPM ) { + if( pParentPM == this ) return false; + pParentPM = pParentPM->GetParentResource(); + } + m_pParentResourcePM = pm; + return true; +} + +CPaintManagerUI* CPaintManagerUI::GetParentResource() const +{ + return m_pParentResourcePM; +} + +DWORD CPaintManagerUI::GetDefaultDisabledColor() const +{ + if( m_pParentResourcePM ) return m_pParentResourcePM->GetDefaultDisabledColor(); + return m_dwDefaultDisabledColor; +} + +void CPaintManagerUI::SetDefaultDisabledColor(DWORD dwColor) +{ + m_dwDefaultDisabledColor = dwColor; +} + +DWORD CPaintManagerUI::GetDefaultFontColor() const +{ + if( m_pParentResourcePM ) return m_pParentResourcePM->GetDefaultFontColor(); + return m_dwDefaultFontColor; +} + +void CPaintManagerUI::SetDefaultFontColor(DWORD dwColor) +{ + m_dwDefaultFontColor = dwColor; +} + +DWORD CPaintManagerUI::GetDefaultLinkFontColor() const +{ + if( m_pParentResourcePM ) return m_pParentResourcePM->GetDefaultLinkFontColor(); + return m_dwDefaultLinkFontColor; +} + +void CPaintManagerUI::SetDefaultLinkFontColor(DWORD dwColor) +{ + m_dwDefaultLinkFontColor = dwColor; +} + +DWORD CPaintManagerUI::GetDefaultLinkHoverFontColor() const +{ + if( m_pParentResourcePM ) return m_pParentResourcePM->GetDefaultLinkHoverFontColor(); + return m_dwDefaultLinkHoverFontColor; +} + +void CPaintManagerUI::SetDefaultLinkHoverFontColor(DWORD dwColor) +{ + m_dwDefaultLinkHoverFontColor = dwColor; +} + +DWORD CPaintManagerUI::GetDefaultSelectedBkColor() const +{ + if( m_pParentResourcePM ) return m_pParentResourcePM->GetDefaultSelectedBkColor(); + return m_dwDefaultSelectedBkColor; +} + +void CPaintManagerUI::SetDefaultSelectedBkColor(DWORD dwColor) +{ + m_dwDefaultSelectedBkColor = dwColor; +} + +TFontInfo* CPaintManagerUI::GetDefaultFontInfo() +{ + if( m_pParentResourcePM ) return m_pParentResourcePM->GetDefaultFontInfo(); + + if( m_DefaultFontInfo.tm.tmHeight == 0 ) { + HFONT hOldFont = (HFONT) ::SelectObject(m_hDcPaint, m_DefaultFontInfo.hFont); + ::GetTextMetrics(m_hDcPaint, &m_DefaultFontInfo.tm); + ::SelectObject(m_hDcPaint, hOldFont); + } + return &m_DefaultFontInfo; +} + +void CPaintManagerUI::SetDefaultFont(LPCTSTR pStrFontName, int nSize, bool bBold, bool bUnderline, bool bItalic) +{ + LOGFONT lf = { 0 }; + ::GetObject(::GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &lf); + _tcsncpy(lf.lfFaceName, pStrFontName, LF_FACESIZE); + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfHeight = -nSize; + if( bBold ) lf.lfWeight += FW_BOLD; + if( bUnderline ) lf.lfUnderline = TRUE; + if( bItalic ) lf.lfItalic = TRUE; + HFONT hFont = ::CreateFontIndirect(&lf); + if( hFont == NULL ) return; + + ::DeleteObject(m_DefaultFontInfo.hFont); + m_DefaultFontInfo.hFont = hFont; + m_DefaultFontInfo.sFontName = pStrFontName; + m_DefaultFontInfo.iSize = nSize; + m_DefaultFontInfo.bBold = bBold; + m_DefaultFontInfo.bUnderline = bUnderline; + m_DefaultFontInfo.bItalic = bItalic; + ::ZeroMemory(&m_DefaultFontInfo.tm, sizeof(m_DefaultFontInfo.tm)); + if( m_hDcPaint ) { + HFONT hOldFont = (HFONT) ::SelectObject(m_hDcPaint, hFont); + ::GetTextMetrics(m_hDcPaint, &m_DefaultFontInfo.tm); + ::SelectObject(m_hDcPaint, hOldFont); + } +} + +DWORD CPaintManagerUI::GetCustomFontCount() const +{ + return m_aCustomFonts.GetSize(); +} + +HFONT CPaintManagerUI::AddFont(LPCTSTR pStrFontName, int nSize, bool bBold, bool bUnderline, bool bItalic) +{ + LOGFONT lf = { 0 }; + ::GetObject(::GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &lf); + _tcsncpy(lf.lfFaceName, pStrFontName, LF_FACESIZE); + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfHeight = -nSize; + lf.lfQuality = CLEARTYPE_QUALITY; + if( bBold ) lf.lfWeight += FW_BOLD; + if( bUnderline ) lf.lfUnderline = TRUE; + if( bItalic ) lf.lfItalic = TRUE; + HFONT hFont = ::CreateFontIndirect(&lf); + if( hFont == NULL ) return NULL; + + TFontInfo* pFontInfo = new TFontInfo; + if( !pFontInfo ) return false; + ::ZeroMemory(pFontInfo, sizeof(TFontInfo)); + pFontInfo->hFont = hFont; + pFontInfo->sFontName = pStrFontName; + pFontInfo->iSize = nSize; + pFontInfo->bBold = bBold; + pFontInfo->bUnderline = bUnderline; + pFontInfo->bItalic = bItalic; + if( m_hDcPaint ) { + HFONT hOldFont = (HFONT) ::SelectObject(m_hDcPaint, hFont); + ::GetTextMetrics(m_hDcPaint, &pFontInfo->tm); + ::SelectObject(m_hDcPaint, hOldFont); + } + if( !m_aCustomFonts.Add(pFontInfo) ) { + ::DeleteObject(hFont); + delete pFontInfo; + return NULL; + } + + return hFont; +} + +HFONT CPaintManagerUI::AddFontAt(int index, LPCTSTR pStrFontName, int nSize, bool bBold, bool bUnderline, bool bItalic) +{ + LOGFONT lf = { 0 }; + ::GetObject(::GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &lf); + _tcsncpy(lf.lfFaceName, pStrFontName, LF_FACESIZE); + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfHeight = -nSize; + lf.lfQuality = CLEARTYPE_QUALITY; + if( bBold ) lf.lfWeight += FW_BOLD; + if( bUnderline ) lf.lfUnderline = TRUE; + if( bItalic ) lf.lfItalic = TRUE; + HFONT hFont = ::CreateFontIndirect(&lf); + if( hFont == NULL ) return NULL; + + TFontInfo* pFontInfo = new TFontInfo; + if( !pFontInfo ) return false; + ::ZeroMemory(pFontInfo, sizeof(TFontInfo)); + pFontInfo->hFont = hFont; + pFontInfo->sFontName = pStrFontName; + pFontInfo->iSize = nSize; + pFontInfo->bBold = bBold; + pFontInfo->bUnderline = bUnderline; + pFontInfo->bItalic = bItalic; + if( m_hDcPaint ) { + HFONT hOldFont = (HFONT) ::SelectObject(m_hDcPaint, hFont); + ::GetTextMetrics(m_hDcPaint, &pFontInfo->tm); + ::SelectObject(m_hDcPaint, hOldFont); + } + if( !m_aCustomFonts.InsertAt(index, pFontInfo) ) { + ::DeleteObject(hFont); + delete pFontInfo; + return NULL; + } + + return hFont; +} + +HFONT CPaintManagerUI::GetFont(int index) +{ + if( index < 0 || index >= m_aCustomFonts.GetSize() ) return GetDefaultFontInfo()->hFont; + TFontInfo* pFontInfo = static_cast(m_aCustomFonts[index]); + return pFontInfo->hFont; +} + +HFONT CPaintManagerUI::GetFont(LPCTSTR pStrFontName, int nSize, bool bBold, bool bUnderline, bool bItalic) +{ + TFontInfo* pFontInfo = NULL; + for( int it = 0; it < m_aCustomFonts.GetSize(); it++ ) { + pFontInfo = static_cast(m_aCustomFonts[it]); + if( pFontInfo->sFontName == pStrFontName && pFontInfo->iSize == nSize && + pFontInfo->bBold == bBold && pFontInfo->bUnderline == bUnderline && pFontInfo->bItalic == bItalic) + return pFontInfo->hFont; + } + if( m_pParentResourcePM ) return m_pParentResourcePM->GetFont(pStrFontName, nSize, bBold, bUnderline, bItalic); + return NULL; +} + +bool CPaintManagerUI::FindFont(HFONT hFont) +{ + TFontInfo* pFontInfo = NULL; + for( int it = 0; it < m_aCustomFonts.GetSize(); it++ ) { + pFontInfo = static_cast(m_aCustomFonts[it]); + if( pFontInfo->hFont == hFont ) return true; + } + if( m_pParentResourcePM ) return m_pParentResourcePM->FindFont(hFont); + return false; +} + +bool CPaintManagerUI::FindFont(LPCTSTR pStrFontName, int nSize, bool bBold, bool bUnderline, bool bItalic) +{ + TFontInfo* pFontInfo = NULL; + for( int it = 0; it < m_aCustomFonts.GetSize(); it++ ) { + pFontInfo = static_cast(m_aCustomFonts[it]); + if( pFontInfo->sFontName == pStrFontName && pFontInfo->iSize == nSize && + pFontInfo->bBold == bBold && pFontInfo->bUnderline == bUnderline && pFontInfo->bItalic == bItalic) + return true; + } + if( m_pParentResourcePM ) return m_pParentResourcePM->FindFont(pStrFontName, nSize, bBold, bUnderline, bItalic); + return false; +} + +int CPaintManagerUI::GetFontIndex(HFONT hFont) +{ + TFontInfo* pFontInfo = NULL; + for( int it = 0; it < m_aCustomFonts.GetSize(); it++ ) { + pFontInfo = static_cast(m_aCustomFonts[it]); + if( pFontInfo->hFont == hFont ) return it; + } + return -1; +} + +int CPaintManagerUI::GetFontIndex(LPCTSTR pStrFontName, int nSize, bool bBold, bool bUnderline, bool bItalic) +{ + TFontInfo* pFontInfo = NULL; + for( int it = 0; it < m_aCustomFonts.GetSize(); it++ ) { + pFontInfo = static_cast(m_aCustomFonts[it]); + if( pFontInfo->sFontName == pStrFontName && pFontInfo->iSize == nSize && + pFontInfo->bBold == bBold && pFontInfo->bUnderline == bUnderline && pFontInfo->bItalic == bItalic) + return it; + } + return -1; +} + +bool CPaintManagerUI::RemoveFont(HFONT hFont) +{ + TFontInfo* pFontInfo = NULL; + for( int it = 0; it < m_aCustomFonts.GetSize(); it++ ) { + pFontInfo = static_cast(m_aCustomFonts[it]); + if( pFontInfo->hFont == hFont ) { + ::DeleteObject(pFontInfo->hFont); + delete pFontInfo; + return m_aCustomFonts.Remove(it); + } + } + + return false; +} + +bool CPaintManagerUI::RemoveFontAt(int index) +{ + if( index < 0 || index >= m_aCustomFonts.GetSize() ) return false; + TFontInfo* pFontInfo = static_cast(m_aCustomFonts[index]); + ::DeleteObject(pFontInfo->hFont); + delete pFontInfo; + return m_aCustomFonts.Remove(index); +} + +void CPaintManagerUI::RemoveAllFonts() +{ + TFontInfo* pFontInfo; + for( int it = 0; it < m_aCustomFonts.GetSize(); it++ ) { + pFontInfo = static_cast(m_aCustomFonts[it]); + ::DeleteObject(pFontInfo->hFont); + delete pFontInfo; + } + m_aCustomFonts.Empty(); +} + +TFontInfo* CPaintManagerUI::GetFontInfo(int index) +{ + if( index < 0 || index >= m_aCustomFonts.GetSize() ) return GetDefaultFontInfo(); + TFontInfo* pFontInfo = static_cast(m_aCustomFonts[index]); + if( pFontInfo->tm.tmHeight == 0 ) { + HFONT hOldFont = (HFONT) ::SelectObject(m_hDcPaint, pFontInfo->hFont); + ::GetTextMetrics(m_hDcPaint, &pFontInfo->tm); + ::SelectObject(m_hDcPaint, hOldFont); + } + return pFontInfo; +} + +TFontInfo* CPaintManagerUI::GetFontInfo(HFONT hFont) +{ + TFontInfo* pFontInfo = NULL; + for( int it = 0; it < m_aCustomFonts.GetSize(); it++ ) { + pFontInfo = static_cast(m_aCustomFonts[it]); + if( pFontInfo->hFont == hFont ) { + if( pFontInfo->tm.tmHeight == 0 ) { + HFONT hOldFont = (HFONT) ::SelectObject(m_hDcPaint, pFontInfo->hFont); + ::GetTextMetrics(m_hDcPaint, &pFontInfo->tm); + ::SelectObject(m_hDcPaint, hOldFont); + } + return pFontInfo; + } + } + + if( m_pParentResourcePM ) return m_pParentResourcePM->GetFontInfo(hFont); + return GetDefaultFontInfo(); +} + +const TImageInfo* CPaintManagerUI::GetImage(LPCTSTR bitmap) +{ + TImageInfo* data = static_cast(m_mImageHash.Find(bitmap)); + if( !data && m_pParentResourcePM ) return m_pParentResourcePM->GetImage(bitmap); + else return data; +} + +const TImageInfo* CPaintManagerUI::GetImageEx(LPCTSTR bitmap, LPCTSTR type, DWORD mask) +{ + TImageInfo* data = static_cast(m_mImageHash.Find(bitmap)); + if( !data ) { + if( AddImage(bitmap, type, mask) ) { + data = static_cast(m_mImageHash.Find(bitmap)); + } + } + + return data; +} + +const TImageInfo* CPaintManagerUI::AddImage(LPCTSTR bitmap, LPCTSTR type, DWORD mask) +{ + TImageInfo* data = NULL; + if( type != NULL ) { + if( isdigit(*bitmap) ) { + LPTSTR pstr = NULL; + int iIndex = _tcstol(bitmap, &pstr, 10); + data = CRenderEngine::LoadImage(iIndex, type, mask); + } + } + else { + data = CRenderEngine::LoadImage(bitmap, NULL, mask); + } + + if( !data ) return NULL; + if( type != NULL ) data->sResType = type; + data->dwMask = mask; + if( !m_mImageHash.Insert(bitmap, data) ) { + ::DeleteObject(data->hBitmap); + delete data; + } + + return data; +} + +const TImageInfo* CPaintManagerUI::AddImage(LPCTSTR bitmap, HBITMAP hBitmap, int iWidth, int iHeight, bool bAlpha) +{ + if( hBitmap == NULL || iWidth <= 0 || iHeight <= 0 ) return NULL; + + TImageInfo* data = new TImageInfo; + data->hBitmap = hBitmap; + data->nX = iWidth; + data->nY = iHeight; + data->alphaChannel = bAlpha; + //data->sResType = _T(""); + data->dwMask = 0; + if( !m_mImageHash.Insert(bitmap, data) ) { + ::DeleteObject(data->hBitmap); + delete data; + } + + return data; +} + +bool CPaintManagerUI::RemoveImage(LPCTSTR bitmap) +{ + const TImageInfo* data = GetImage(bitmap); + if( !data ) return false; + + CRenderEngine::FreeImage(data) ; + + return m_mImageHash.Remove(bitmap); +} + +void CPaintManagerUI::RemoveAllImages() +{ + TImageInfo* data; + for( int i = 0; i< m_mImageHash.GetSize(); i++ ) { + if(LPCTSTR key = m_mImageHash.GetAt(i)) { + data = static_cast(m_mImageHash.Find(key, false)); + if (data) { + CRenderEngine::FreeImage(data); + } + } + } + m_mImageHash.RemoveAll(); +} + +void CPaintManagerUI::ReloadAllImages() +{ + bool bRedraw = false; + TImageInfo* data; + TImageInfo* pNewData; + for( int i = 0; i< m_mImageHash.GetSize(); i++ ) { + if(LPCTSTR bitmap = m_mImageHash.GetAt(i)) { + data = static_cast(m_mImageHash.Find(bitmap)); + if( data != NULL ) { + if( !data->sResType.IsEmpty() ) { + if( isdigit(*bitmap) ) { + LPTSTR pstr = NULL; + int iIndex = _tcstol(bitmap, &pstr, 10); + pNewData = CRenderEngine::LoadImage(iIndex, data->sResType.GetData(), data->dwMask); + } + } + else { + pNewData = CRenderEngine::LoadImage(bitmap, NULL, data->dwMask); + } + if( pNewData == NULL ) continue; + + if( data->hBitmap != NULL ) ::DeleteObject(data->hBitmap); + data->hBitmap = pNewData->hBitmap; + data->nX = pNewData->nX; + data->nY = pNewData->nY; + data->alphaChannel = pNewData->alphaChannel; + + delete pNewData; + bRedraw = true; + } + } + } + if( bRedraw && m_pRoot ) m_pRoot->Invalidate(); +} + +void CPaintManagerUI::AddDefaultAttributeList(LPCTSTR pStrControlName, LPCTSTR pStrControlAttrList) +{ + CDuiString* pDefaultAttr = new CDuiString(pStrControlAttrList); + if (pDefaultAttr != NULL) + { + if (m_DefaultAttrHash.Find(pStrControlName) == NULL) + m_DefaultAttrHash.Set(pStrControlName, (LPVOID)pDefaultAttr); + else + delete pDefaultAttr; + } +} + +LPCTSTR CPaintManagerUI::GetDefaultAttributeList(LPCTSTR pStrControlName) const +{ + CDuiString* pDefaultAttr = static_cast(m_DefaultAttrHash.Find(pStrControlName)); + if( !pDefaultAttr && m_pParentResourcePM ) return m_pParentResourcePM->GetDefaultAttributeList(pStrControlName); + + if( pDefaultAttr ) return pDefaultAttr->GetData(); + else return NULL; +} + +bool CPaintManagerUI::RemoveDefaultAttributeList(LPCTSTR pStrControlName) +{ + CDuiString* pDefaultAttr = static_cast(m_DefaultAttrHash.Find(pStrControlName)); + if( !pDefaultAttr ) return false; + + delete pDefaultAttr; + return m_DefaultAttrHash.Remove(pStrControlName); +} + +const CStdStringPtrMap& CPaintManagerUI::GetDefaultAttribultes() const +{ + return m_DefaultAttrHash; +} + +void CPaintManagerUI::RemoveAllDefaultAttributeList() +{ + CDuiString* pDefaultAttr; + for( int i = 0; i< m_DefaultAttrHash.GetSize(); i++ ) { + if(LPCTSTR key = m_DefaultAttrHash.GetAt(i)) { + pDefaultAttr = static_cast(m_DefaultAttrHash.Find(key)); + delete pDefaultAttr; + } + } + m_DefaultAttrHash.RemoveAll(); +} + +CControlUI* CPaintManagerUI::GetRoot() const +{ + ASSERT(m_pRoot); + return m_pRoot; +} + +CControlUI* CPaintManagerUI::FindControl(POINT pt) const +{ + ASSERT(m_pRoot); + return m_pRoot->FindControl(__FindControlFromPoint, &pt, UIFIND_VISIBLE | UIFIND_HITTEST | UIFIND_TOP_FIRST); +} + +CControlUI* CPaintManagerUI::FindControl(LPCTSTR pstrName) const +{ + ASSERT(m_pRoot); + return static_cast(m_mNameHash.Find(pstrName)); +} + +CControlUI* CPaintManagerUI::FindSubControlByPoint(CControlUI* pParent, POINT pt) const +{ + if( pParent == NULL ) pParent = GetRoot(); + ASSERT(pParent); + return pParent->FindControl(__FindControlFromPoint, &pt, UIFIND_VISIBLE | UIFIND_HITTEST | UIFIND_TOP_FIRST); +} + +CControlUI* CPaintManagerUI::FindSubControlByName(CControlUI* pParent, LPCTSTR pstrName) const +{ + if( pParent == NULL ) pParent = GetRoot(); + ASSERT(pParent); + return pParent->FindControl(__FindControlFromName, (LPVOID)pstrName, UIFIND_ALL); +} + +CControlUI* CPaintManagerUI::FindSubControlByClass(CControlUI* pParent, LPCTSTR pstrClass, int iIndex) +{ + if( pParent == NULL ) pParent = GetRoot(); + ASSERT(pParent); + m_aFoundControls.Resize(iIndex + 1); + return pParent->FindControl(__FindControlFromClass, (LPVOID)pstrClass, UIFIND_ALL); +} + +CStdPtrArray* CPaintManagerUI::FindSubControlsByClass(CControlUI* pParent, LPCTSTR pstrClass) +{ + if( pParent == NULL ) pParent = GetRoot(); + ASSERT(pParent); + m_aFoundControls.Empty(); + pParent->FindControl(__FindControlsFromClass, (LPVOID)pstrClass, UIFIND_ALL); + return &m_aFoundControls; +} + +CStdPtrArray* CPaintManagerUI::GetSubControlsByClass() +{ + return &m_aFoundControls; +} + +CControlUI* CALLBACK CPaintManagerUI::__FindControlFromNameHash(CControlUI* pThis, LPVOID pData) +{ + CPaintManagerUI* pManager = static_cast(pData); + const CDuiString& sName = pThis->GetName(); + if( sName.IsEmpty() ) return NULL; + // Add this control to the hash list + pManager->m_mNameHash.Set(sName, pThis); + return NULL; // Attempt to add all controls +} + +CControlUI* CALLBACK CPaintManagerUI::__FindControlFromCount(CControlUI* /*pThis*/, LPVOID pData) +{ + int* pnCount = static_cast(pData); + (*pnCount)++; + return NULL; // Count all controls +} + +CControlUI* CALLBACK CPaintManagerUI::__FindControlFromPoint(CControlUI* pThis, LPVOID pData) +{ + LPPOINT pPoint = static_cast(pData); + return ::PtInRect(&pThis->GetPos(), *pPoint) ? pThis : NULL; +} + +CControlUI* CALLBACK CPaintManagerUI::__FindControlFromTab(CControlUI* pThis, LPVOID pData) +{ + FINDTABINFO* pInfo = static_cast(pData); + if( pInfo->pFocus == pThis ) { + if( pInfo->bForward ) pInfo->bNextIsIt = true; + return pInfo->bForward ? NULL : pInfo->pLast; + } + if( (pThis->GetControlFlags() & UIFLAG_TABSTOP) == 0 ) return NULL; + pInfo->pLast = pThis; + if( pInfo->bNextIsIt ) return pThis; + if( pInfo->pFocus == NULL ) return pThis; + return NULL; // Examine all controls +} + +CControlUI* CALLBACK CPaintManagerUI::__FindControlFromShortcut(CControlUI* pThis, LPVOID pData) +{ + if( !pThis->IsVisible() ) return NULL; + FINDSHORTCUT* pFS = static_cast(pData); + if( pFS->ch == toupper(pThis->GetShortcut()) ) pFS->bPickNext = true; + if( _tcsstr(pThis->GetClass(), _T("LabelUI")) != NULL ) return NULL; // Labels never get focus! + return pFS->bPickNext ? pThis : NULL; +} + +CControlUI* CALLBACK CPaintManagerUI::__FindControlFromUpdate(CControlUI* pThis, LPVOID pData) +{ + return pThis->IsUpdateNeeded() ? pThis : NULL; +} + +CControlUI* CALLBACK CPaintManagerUI::__FindControlFromName(CControlUI* pThis, LPVOID pData) +{ + LPCTSTR pstrName = static_cast(pData); + const CDuiString& sName = pThis->GetName(); + if( sName.IsEmpty() ) return NULL; + return (_tcsicmp(sName, pstrName) == 0) ? pThis : NULL; +} + +CControlUI* CALLBACK CPaintManagerUI::__FindControlFromClass(CControlUI* pThis, LPVOID pData) +{ + LPCTSTR pstrType = static_cast(pData); + LPCTSTR pType = pThis->GetClass(); + CStdPtrArray* pFoundControls = pThis->GetManager()->GetSubControlsByClass(); + if( _tcscmp(pstrType, _T("*")) == 0 || _tcscmp(pstrType, pType) == 0 ) { + int iIndex = -1; + while( pFoundControls->GetAt(++iIndex) != NULL ) ; + if( iIndex < pFoundControls->GetSize() ) pFoundControls->SetAt(iIndex, pThis); + } + if( pFoundControls->GetAt(pFoundControls->GetSize() - 1) != NULL ) return pThis; + return NULL; +} + +CControlUI* CALLBACK CPaintManagerUI::__FindControlsFromClass(CControlUI* pThis, LPVOID pData) +{ + LPCTSTR pstrType = static_cast(pData); + LPCTSTR pType = pThis->GetClass(); + if( _tcscmp(pstrType, _T("*")) == 0 || _tcscmp(pstrType, pType) == 0 ) + pThis->GetManager()->GetSubControlsByClass()->Add((LPVOID)pThis); + return NULL; +} + +bool CPaintManagerUI::TranslateAccelerator(LPMSG pMsg) +{ + for (int i = 0; i < m_aTranslateAccelerator.GetSize(); i++) + { + LRESULT lResult = static_cast(m_aTranslateAccelerator[i])->TranslateAccelerator(pMsg); + if( lResult == S_OK ) return true; + } + return false; +} + +bool CPaintManagerUI::TranslateMessage(const LPMSG pMsg) +{ + // Pretranslate Message takes care of system-wide messages, such as + // tabbing and shortcut key-combos. We'll look for all messages for + // each window and any child control attached. + UINT uStyle = GetWindowStyle(pMsg->hwnd); + UINT uChildRes = uStyle & WS_CHILD; + LRESULT lRes = 0; + if (uChildRes != 0) + { + HWND hWndParent = ::GetParent(pMsg->hwnd); + + for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) + { + CPaintManagerUI* pT = static_cast(m_aPreMessages[i]); + HWND hTempParent = hWndParent; + while(hTempParent) + { + if(pMsg->hwnd == pT->GetPaintWindow() || hTempParent == pT->GetPaintWindow()) + { + if (pT->TranslateAccelerator(pMsg)) + return true; + + pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes); + } + hTempParent = GetParent(hTempParent); + } + } + } + else + { + for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) + { + CPaintManagerUI* pT = static_cast(m_aPreMessages[i]); + if(pMsg->hwnd == pT->GetPaintWindow()) + { + if (pT->TranslateAccelerator(pMsg)) + return true; + + if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) + return true; + + return false; + } + } + } + return false; +} + +bool CPaintManagerUI::AddTranslateAccelerator(ITranslateAccelerator *pTranslateAccelerator) +{ + ASSERT(m_aTranslateAccelerator.Find(pTranslateAccelerator) < 0); + return m_aTranslateAccelerator.Add(pTranslateAccelerator); +} + +bool CPaintManagerUI::RemoveTranslateAccelerator(ITranslateAccelerator *pTranslateAccelerator) +{ + for (int i = 0; i < m_aTranslateAccelerator.GetSize(); i++) + { + if (static_cast(m_aTranslateAccelerator[i]) == pTranslateAccelerator) + { + return m_aTranslateAccelerator.Remove(i); + } + } + return false; +} + +void CPaintManagerUI::UsedVirtualWnd(bool bUsed) +{ + m_bUsedVirtualWnd = bUsed; +} + +} // namespace DuiLib diff --git a/DuiLib/Core/UIManager.h b/DuiLib/Core/UIManager.h new file mode 100644 index 00000000..965ae490 --- /dev/null +++ b/DuiLib/Core/UIManager.h @@ -0,0 +1,429 @@ +#ifndef __UIMANAGER_H__ +#define __UIMANAGER_H__ + +#pragma once + +namespace DuiLib { +///////////////////////////////////////////////////////////////////////////////////// +// + +class CControlUI; +class CRichEditUI; + +///////////////////////////////////////////////////////////////////////////////////// +// + +typedef enum EVENTTYPE_UI +{ + UIEVENT__FIRST = 1, + UIEVENT__KEYBEGIN, + UIEVENT_KEYDOWN, + UIEVENT_KEYUP, + UIEVENT_CHAR, + UIEVENT_SYSKEY, + UIEVENT__KEYEND, + UIEVENT__MOUSEBEGIN, + UIEVENT_MOUSEMOVE, + UIEVENT_MOUSELEAVE, + UIEVENT_MOUSEENTER, + UIEVENT_MOUSEHOVER, + UIEVENT_BUTTONDOWN, + UIEVENT_BUTTONUP, + UIEVENT_RBUTTONDOWN, + UIEVENT_DBLCLICK, + UIEVENT_CONTEXTMENU, + UIEVENT_SCROLLWHEEL, + UIEVENT__MOUSEEND, + UIEVENT_KILLFOCUS, + UIEVENT_SETFOCUS, + UIEVENT_WINDOWSIZE, + UIEVENT_SETCURSOR, + UIEVENT_TIMER, + UIEVENT_NOTIFY, + UIEVENT_COMMAND, + UIEVENT__LAST, +}; + +///////////////////////////////////////////////////////////////////////////////////// +// + +// Flags for CControlUI::GetControlFlags() +#define UIFLAG_TABSTOP 0x00000001 +#define UIFLAG_SETCURSOR 0x00000002 +#define UIFLAG_WANTRETURN 0x00000004 + +// Flags for FindControl() +#define UIFIND_ALL 0x00000000 +#define UIFIND_VISIBLE 0x00000001 +#define UIFIND_ENABLED 0x00000002 +#define UIFIND_HITTEST 0x00000004 +#define UIFIND_TOP_FIRST 0x00000008 +#define UIFIND_ME_FIRST 0x80000000 + +// Flags for the CDialogLayout stretching +#define UISTRETCH_NEWGROUP 0x00000001 +#define UISTRETCH_NEWLINE 0x00000002 +#define UISTRETCH_MOVE_X 0x00000004 +#define UISTRETCH_MOVE_Y 0x00000008 +#define UISTRETCH_SIZE_X 0x00000010 +#define UISTRETCH_SIZE_Y 0x00000020 + +// Flags used for controlling the paint +#define UISTATE_FOCUSED 0x00000001 +#define UISTATE_SELECTED 0x00000002 +#define UISTATE_DISABLED 0x00000004 +#define UISTATE_HOT 0x00000008 +#define UISTATE_PUSHED 0x00000010 +#define UISTATE_READONLY 0x00000020 +#define UISTATE_CAPTURED 0x00000040 + + + +///////////////////////////////////////////////////////////////////////////////////// +// + +typedef struct tagTFontInfo +{ + HFONT hFont; + CDuiString sFontName; + int iSize; + bool bBold; + bool bUnderline; + bool bItalic; + TEXTMETRIC tm; +} TFontInfo; + +typedef struct tagTImageInfo +{ + HBITMAP hBitmap; + int nX; + int nY; + bool alphaChannel; + CDuiString sResType; + DWORD dwMask; +} TImageInfo; + +// Structure for notifications from the system +// to the control implementation. +typedef struct tagTEventUI +{ + int Type; + CControlUI* pSender; + DWORD dwTimestamp; + POINT ptMouse; + TCHAR chKey; + WORD wKeyState; + WPARAM wParam; + LPARAM lParam; +} TEventUI; + +// Structure for relative position to the parent +typedef struct tagTRelativePosUI +{ + bool bRelative; + SIZE szParent; + int nMoveXPercent; + int nMoveYPercent; + int nZoomXPercent; + int nZoomYPercent; +}TRelativePosUI; + +// Listener interface +class INotifyUI +{ +public: + virtual void Notify(TNotifyUI& msg) = 0; +}; + +// MessageFilter interface +class IMessageFilterUI +{ +public: + virtual LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled) = 0; +}; + +class ITranslateAccelerator +{ +public: + virtual LRESULT TranslateAccelerator(MSG *pMsg) = 0; +}; + + +///////////////////////////////////////////////////////////////////////////////////// +// +typedef CControlUI* (*LPCREATECONTROL)(LPCTSTR pstrType); + + +class UILIB_API CPaintManagerUI +{ +public: + CPaintManagerUI(); + ~CPaintManagerUI(); + +public: + void Init(HWND hWnd); + void NeedUpdate(); + void Invalidate(RECT& rcItem); + + HDC GetPaintDC() const; + HWND GetPaintWindow() const; + HWND GetTooltipWindow() const; + + POINT GetMousePos() const; + SIZE GetClientSize() const; + SIZE GetInitSize(); + void SetInitSize(int cx, int cy); + RECT& GetSizeBox(); + void SetSizeBox(RECT& rcSizeBox); + RECT& GetCaptionRect(); + void SetCaptionRect(RECT& rcCaption); + SIZE GetRoundCorner() const; + void SetRoundCorner(int cx, int cy); + SIZE GetMinInfo() const; + void SetMinInfo(int cx, int cy); + SIZE GetMaxInfo() const; + void SetMaxInfo(int cx, int cy); + int GetTransparent() const; + void SetTransparent(int nOpacity); + void SetBackgroundTransparent(bool bTrans); + + bool IsBackgroundTransparent(); + bool ShowCaret(bool bShow); + bool SetCaretPos(CRichEditUI* obj, int x, int y); + CRichEditUI* GetCurrentCaretObject(); + bool CreateCaret(HBITMAP hBmp, int nWidth, int nHeight); + void DrawCaret(HDC hDC, const RECT& rcPaint); + CShadowUI* GetShadow(); + void SetUseGdiplusText(bool bUse); + bool IsUseGdiplusText() const; + + static HINSTANCE GetInstance(); + static CDuiString GetInstancePath(); + static CDuiString GetCurrentPath(); + static HINSTANCE GetResourceDll(); + static const CDuiString& GetResourcePath(); + static const CDuiString& GetResourceZip(); + static bool IsCachedResourceZip(); + static HANDLE GetResourceZipHandle(); + static void SetInstance(HINSTANCE hInst); + static void SetCurrentPath(LPCTSTR pStrPath); + static void SetResourceDll(HINSTANCE hInst); + static void SetResourcePath(LPCTSTR pStrPath); + static void SetResourceZip(LPVOID pVoid, unsigned int len); + static void SetResourceZip(LPCTSTR pstrZip, bool bCachedResourceZip = false); + static void GetHSL(short* H, short* S, short* L); + static void SetHSL(bool bUseHSL, short H, short S, short L); // H:0~360, S:0~200, L:0~200 + static void ReloadSkin(); + static bool LoadPlugin(LPCTSTR pstrModuleName); + static CStdPtrArray* GetPlugins(); + + bool UseParentResource(CPaintManagerUI* pm); + CPaintManagerUI* GetParentResource() const; + + DWORD GetDefaultDisabledColor() const; + void SetDefaultDisabledColor(DWORD dwColor); + DWORD GetDefaultFontColor() const; + void SetDefaultFontColor(DWORD dwColor); + DWORD GetDefaultLinkFontColor() const; + void SetDefaultLinkFontColor(DWORD dwColor); + DWORD GetDefaultLinkHoverFontColor() const; + void SetDefaultLinkHoverFontColor(DWORD dwColor); + DWORD GetDefaultSelectedBkColor() const; + void SetDefaultSelectedBkColor(DWORD dwColor); + TFontInfo* GetDefaultFontInfo(); + void SetDefaultFont(LPCTSTR pStrFontName, int nSize, bool bBold, bool bUnderline, bool bItalic); + DWORD GetCustomFontCount() const; + HFONT AddFont(LPCTSTR pStrFontName, int nSize, bool bBold, bool bUnderline, bool bItalic); + HFONT AddFontAt(int index, LPCTSTR pStrFontName, int nSize, bool bBold, bool bUnderline, bool bItalic); + HFONT GetFont(int index); + HFONT GetFont(LPCTSTR pStrFontName, int nSize, bool bBold, bool bUnderline, bool bItalic); + bool FindFont(HFONT hFont); + bool FindFont(LPCTSTR pStrFontName, int nSize, bool bBold, bool bUnderline, bool bItalic); + int GetFontIndex(HFONT hFont); + int GetFontIndex(LPCTSTR pStrFontName, int nSize, bool bBold, bool bUnderline, bool bItalic); + bool RemoveFont(HFONT hFont); + bool RemoveFontAt(int index); + void RemoveAllFonts(); + TFontInfo* GetFontInfo(int index); + TFontInfo* GetFontInfo(HFONT hFont); + + const TImageInfo* GetImage(LPCTSTR bitmap); + const TImageInfo* GetImageEx(LPCTSTR bitmap, LPCTSTR type = NULL, DWORD mask = 0); + const TImageInfo* AddImage(LPCTSTR bitmap, LPCTSTR type = NULL, DWORD mask = 0); + const TImageInfo* AddImage(LPCTSTR bitmap, HBITMAP hBitmap, int iWidth, int iHeight, bool bAlpha); + bool RemoveImage(LPCTSTR bitmap); + void RemoveAllImages(); + void ReloadAllImages(); + + void AddDefaultAttributeList(LPCTSTR pStrControlName, LPCTSTR pStrControlAttrList); + LPCTSTR GetDefaultAttributeList(LPCTSTR pStrControlName) const; + bool RemoveDefaultAttributeList(LPCTSTR pStrControlName); + const CStdStringPtrMap& GetDefaultAttribultes() const; + void RemoveAllDefaultAttributeList(); + + bool AttachDialog(CControlUI* pControl); + bool InitControls(CControlUI* pControl, CControlUI* pParent = NULL); + void ReapObjects(CControlUI* pControl); + + bool AddOptionGroup(LPCTSTR pStrGroupName, CControlUI* pControl); + CStdPtrArray* GetOptionGroup(LPCTSTR pStrGroupName); + void RemoveOptionGroup(LPCTSTR pStrGroupName, CControlUI* pControl); + void RemoveAllOptionGroups(); + + CControlUI* GetFocus() const; + void SetFocus(CControlUI* pControl); + void SetFocusNeeded(CControlUI* pControl); + + bool SetNextTabControl(bool bForward = true); + + bool SetTimer(CControlUI* pControl, UINT nTimerID, UINT uElapse); + bool KillTimer(CControlUI* pControl, UINT nTimerID); + void KillTimer(CControlUI* pControl); + void RemoveAllTimers(); + + void SetCapture(); + void ReleaseCapture(); + bool IsCaptured(); + + bool AddNotifier(INotifyUI* pControl); + bool RemoveNotifier(INotifyUI* pControl); + void SendNotify(TNotifyUI& Msg, bool bAsync = false); + void SendNotify(CControlUI* pControl, LPCTSTR pstrMessage, WPARAM wParam = 0, LPARAM lParam = 0, bool bAsync = false); + + bool AddPreMessageFilter(IMessageFilterUI* pFilter); + bool RemovePreMessageFilter(IMessageFilterUI* pFilter); + + bool AddMessageFilter(IMessageFilterUI* pFilter); + bool RemoveMessageFilter(IMessageFilterUI* pFilter); + + int GetPostPaintCount() const; + bool AddPostPaint(CControlUI* pControl); + bool RemovePostPaint(CControlUI* pControl); + bool SetPostPaintIndex(CControlUI* pControl, int iIndex); + + void AddDelayedCleanup(CControlUI* pControl); + + bool AddTranslateAccelerator(ITranslateAccelerator *pTranslateAccelerator); + bool RemoveTranslateAccelerator(ITranslateAccelerator *pTranslateAccelerator); + bool TranslateAccelerator(LPMSG pMsg); + + CControlUI* GetRoot() const; + CControlUI* FindControl(POINT pt) const; + CControlUI* FindControl(LPCTSTR pstrName) const; + CControlUI* FindSubControlByPoint(CControlUI* pParent, POINT pt) const; + CControlUI* FindSubControlByName(CControlUI* pParent, LPCTSTR pstrName) const; + CControlUI* FindSubControlByClass(CControlUI* pParent, LPCTSTR pstrClass, int iIndex = 0); + CStdPtrArray* FindSubControlsByClass(CControlUI* pParent, LPCTSTR pstrClass); + CStdPtrArray* GetSubControlsByClass(); + + static void MessageLoop(); + static bool TranslateMessage(const LPMSG pMsg); + static void Term(); + + bool MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes); + bool PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes); + void UsedVirtualWnd(bool bUsed); + +private: + static CControlUI* CALLBACK __FindControlFromNameHash(CControlUI* pThis, LPVOID pData); + static CControlUI* CALLBACK __FindControlFromCount(CControlUI* pThis, LPVOID pData); + static CControlUI* CALLBACK __FindControlFromPoint(CControlUI* pThis, LPVOID pData); + static CControlUI* CALLBACK __FindControlFromTab(CControlUI* pThis, LPVOID pData); + static CControlUI* CALLBACK __FindControlFromShortcut(CControlUI* pThis, LPVOID pData); + static CControlUI* CALLBACK __FindControlFromUpdate(CControlUI* pThis, LPVOID pData); + static CControlUI* CALLBACK __FindControlFromName(CControlUI* pThis, LPVOID pData); + static CControlUI* CALLBACK __FindControlFromClass(CControlUI* pThis, LPVOID pData); + static CControlUI* CALLBACK __FindControlsFromClass(CControlUI* pThis, LPVOID pData); + +private: + HWND m_hWndPaint; //ӵĴľ + int m_nOpacity; //͸ + HDC m_hDcPaint; //DC + HDC m_hDcOffscreen; + HBITMAP m_hbmpOffscreen; + LPBYTE m_pBmpOffscreenBits; + RECT m_rcInvalidate; // ͸δУˢ + bool m_bIsRestore; + + HWND m_hwndTooltip; + TOOLINFO m_ToolTip; + + RECT m_rtCaret; + bool m_bCaretActive; + bool m_bCaretShowing; + CRichEditUI* m_currentCaretObject; + + CShadowUI m_shadow; + bool m_bUseGdiplusText; + // + // + ULONG_PTR m_gdiplusToken; + Gdiplus::GdiplusStartupInput *m_pGdiplusStartupInput; + // + CControlUI* m_pRoot; + CControlUI* m_pFocus; + CControlUI* m_pEventHover; + CControlUI* m_pEventClick; + CControlUI* m_pEventKey; + // + POINT m_ptLastMousePos; + SIZE m_szMinWindow; + SIZE m_szMaxWindow; + SIZE m_szInitWindowSize; + RECT m_rcSizeBox; + SIZE m_szRoundCorner; + RECT m_rcCaption; + UINT m_uTimerID; + bool m_bFirstLayout; + bool m_bUpdateNeeded; + bool m_bFocusNeeded; + bool m_bOffscreenPaint; + bool m_bAlphaBackground; + bool m_bMouseTracking; + bool m_bMouseCapture; + bool m_bUsedVirtualWnd; + + // + CStdPtrArray m_aNotifiers; + CStdPtrArray m_aTimers; + CStdPtrArray m_aPreMessageFilters; + CStdPtrArray m_aMessageFilters; + CStdPtrArray m_aPostPaintControls; + CStdPtrArray m_aDelayedCleanup; + CStdPtrArray m_aAsyncNotify; + CStdPtrArray m_aFoundControls; + CStdStringPtrMap m_mNameHash; + CStdStringPtrMap m_mOptionGroup; + // + CPaintManagerUI* m_pParentResourcePM; + DWORD m_dwDefaultDisabledColor; + DWORD m_dwDefaultFontColor; + DWORD m_dwDefaultLinkFontColor; + DWORD m_dwDefaultLinkHoverFontColor; + DWORD m_dwDefaultSelectedBkColor; + TFontInfo m_DefaultFontInfo; + CStdPtrArray m_aCustomFonts; + + CStdStringPtrMap m_mImageHash; + CStdStringPtrMap m_DefaultAttrHash; + // + static HINSTANCE m_hInstance; + static HINSTANCE m_hResourceInstance; + static CDuiString m_pStrResourcePath; + static CDuiString m_pStrResourceZip; + static bool m_bCachedResourceZip; + static HANDLE m_hResourceZip; + static short m_H; + static short m_S; + static short m_L; + static CStdPtrArray m_aPreMessages; + static CStdPtrArray m_aPlugins; + +public: + static CDuiString m_pStrDefaultFontName; + CStdPtrArray m_aTranslateAccelerator; +}; + +} // namespace DuiLib + +#endif // __UIMANAGER_H__ diff --git a/DuiLib/Core/UIMarkup.cpp b/DuiLib/Core/UIMarkup.cpp new file mode 100644 index 00000000..95c178e8 --- /dev/null +++ b/DuiLib/Core/UIMarkup.cpp @@ -0,0 +1,665 @@ +#include "StdAfx.h" + +#ifndef TRACE +#define TRACE +#endif + +/////////////////////////////////////////////////////////////////////////////////////// +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened +typedef DWORD ZRESULT; +typedef struct +{ + int index; // index of this file within the zip + char name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; +typedef struct +{ + int index; // index of this file within the zip + TCHAR name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; +#define OpenZip OpenZipU +#define CloseZip(hz) CloseZipU(hz) +extern HZIP OpenZipU(void *z,unsigned int len,DWORD flags); +extern ZRESULT CloseZipU(HZIP hz); +#ifdef _UNICODE +#define ZIPENTRY ZIPENTRYW +#define GetZipItem GetZipItemW +#define FindZipItem FindZipItemW +#else +#define GetZipItem GetZipItemA +#define FindZipItem FindZipItemA +#endif +extern ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze); +extern ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *ze); +extern ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze); +extern ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *ze); +extern ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags); +/////////////////////////////////////////////////////////////////////////////////////// + +namespace DuiLib { + +/////////////////////////////////////////////////////////////////////////////////////// +// +// +// + +CMarkupNode::CMarkupNode() : m_pOwner(NULL) +{ +} + +CMarkupNode::CMarkupNode(CMarkup* pOwner, int iPos) : m_pOwner(pOwner), m_iPos(iPos), m_nAttributes(0) +{ +} + +CMarkupNode CMarkupNode::GetSibling() +{ + if( m_pOwner == NULL ) return CMarkupNode(); + ULONG iPos = m_pOwner->m_pElements[m_iPos].iNext; + if( iPos == 0 ) return CMarkupNode(); + return CMarkupNode(m_pOwner, iPos); +} + +bool CMarkupNode::HasSiblings() const +{ + if( m_pOwner == NULL ) return false; + ULONG iPos = m_pOwner->m_pElements[m_iPos].iNext; + return iPos > 0; +} + +CMarkupNode CMarkupNode::GetChild() +{ + if( m_pOwner == NULL ) return CMarkupNode(); + ULONG iPos = m_pOwner->m_pElements[m_iPos].iChild; + if( iPos == 0 ) return CMarkupNode(); + return CMarkupNode(m_pOwner, iPos); +} + +CMarkupNode CMarkupNode::GetChild(LPCTSTR pstrName) +{ + if( m_pOwner == NULL ) return CMarkupNode(); + ULONG iPos = m_pOwner->m_pElements[m_iPos].iChild; + while( iPos != 0 ) { + if( _tcscmp(m_pOwner->m_pstrXML + m_pOwner->m_pElements[iPos].iStart, pstrName) == 0 ) { + return CMarkupNode(m_pOwner, iPos); + } + iPos = m_pOwner->m_pElements[iPos].iNext; + } + return CMarkupNode(); +} + +bool CMarkupNode::HasChildren() const +{ + if( m_pOwner == NULL ) return false; + return m_pOwner->m_pElements[m_iPos].iChild != 0; +} + +CMarkupNode CMarkupNode::GetParent() +{ + if( m_pOwner == NULL ) return CMarkupNode(); + ULONG iPos = m_pOwner->m_pElements[m_iPos].iParent; + if( iPos == 0 ) return CMarkupNode(); + return CMarkupNode(m_pOwner, iPos); +} + +bool CMarkupNode::IsValid() const +{ + return m_pOwner != NULL; +} + +LPCTSTR CMarkupNode::GetName() const +{ + if( m_pOwner == NULL ) return NULL; + return m_pOwner->m_pstrXML + m_pOwner->m_pElements[m_iPos].iStart; +} + +LPCTSTR CMarkupNode::GetValue() const +{ + if( m_pOwner == NULL ) return NULL; + return m_pOwner->m_pstrXML + m_pOwner->m_pElements[m_iPos].iData; +} + +LPCTSTR CMarkupNode::GetAttributeName(int iIndex) +{ + if( m_pOwner == NULL ) return NULL; + if( m_nAttributes == 0 ) _MapAttributes(); + if( iIndex < 0 || iIndex >= m_nAttributes ) return _T(""); + return m_pOwner->m_pstrXML + m_aAttributes[iIndex].iName; +} + +LPCTSTR CMarkupNode::GetAttributeValue(int iIndex) +{ + if( m_pOwner == NULL ) return NULL; + if( m_nAttributes == 0 ) _MapAttributes(); + if( iIndex < 0 || iIndex >= m_nAttributes ) return _T(""); + return m_pOwner->m_pstrXML + m_aAttributes[iIndex].iValue; +} + +LPCTSTR CMarkupNode::GetAttributeValue(LPCTSTR pstrName) +{ + if( m_pOwner == NULL ) return NULL; + if( m_nAttributes == 0 ) _MapAttributes(); + for( int i = 0; i < m_nAttributes; i++ ) { + if( _tcscmp(m_pOwner->m_pstrXML + m_aAttributes[i].iName, pstrName) == 0 ) return m_pOwner->m_pstrXML + m_aAttributes[i].iValue; + } + return _T(""); +} + +bool CMarkupNode::GetAttributeValue(int iIndex, LPTSTR pstrValue, SIZE_T cchMax) +{ + if( m_pOwner == NULL ) return false; + if( m_nAttributes == 0 ) _MapAttributes(); + if( iIndex < 0 || iIndex >= m_nAttributes ) return false; + _tcsncpy(pstrValue, m_pOwner->m_pstrXML + m_aAttributes[iIndex].iValue, cchMax); + return true; +} + +bool CMarkupNode::GetAttributeValue(LPCTSTR pstrName, LPTSTR pstrValue, SIZE_T cchMax) +{ + if( m_pOwner == NULL ) return false; + if( m_nAttributes == 0 ) _MapAttributes(); + for( int i = 0; i < m_nAttributes; i++ ) { + if( _tcscmp(m_pOwner->m_pstrXML + m_aAttributes[i].iName, pstrName) == 0 ) { + _tcsncpy(pstrValue, m_pOwner->m_pstrXML + m_aAttributes[i].iValue, cchMax); + return true; + } + } + return false; +} + +int CMarkupNode::GetAttributeCount() +{ + if( m_pOwner == NULL ) return 0; + if( m_nAttributes == 0 ) _MapAttributes(); + return m_nAttributes; +} + +bool CMarkupNode::HasAttributes() +{ + if( m_pOwner == NULL ) return false; + if( m_nAttributes == 0 ) _MapAttributes(); + return m_nAttributes > 0; +} + +bool CMarkupNode::HasAttribute(LPCTSTR pstrName) +{ + if( m_pOwner == NULL ) return false; + if( m_nAttributes == 0 ) _MapAttributes(); + for( int i = 0; i < m_nAttributes; i++ ) { + if( _tcscmp(m_pOwner->m_pstrXML + m_aAttributes[i].iName, pstrName) == 0 ) return true; + } + return false; +} + +void CMarkupNode::_MapAttributes() +{ + m_nAttributes = 0; + LPCTSTR pstr = m_pOwner->m_pstrXML + m_pOwner->m_pElements[m_iPos].iStart; + LPCTSTR pstrEnd = m_pOwner->m_pstrXML + m_pOwner->m_pElements[m_iPos].iData; + pstr += _tcslen(pstr) + 1; + while( pstr < pstrEnd ) { + m_pOwner->_SkipWhitespace(pstr); + m_aAttributes[m_nAttributes].iName = pstr - m_pOwner->m_pstrXML; + pstr += _tcslen(pstr) + 1; + m_pOwner->_SkipWhitespace(pstr); + if( *pstr++ != _T('\"') ) return; // if( *pstr != _T('\"') ) { pstr = ::CharNext(pstr); return; } + + m_aAttributes[m_nAttributes++].iValue = pstr - m_pOwner->m_pstrXML; + if( m_nAttributes >= MAX_XML_ATTRIBUTES ) return; + pstr += _tcslen(pstr) + 1; + } +} + + +/////////////////////////////////////////////////////////////////////////////////////// +// +// +// + +CMarkup::CMarkup(LPCTSTR pstrXML) +{ + m_pstrXML = NULL; + m_pElements = NULL; + m_nElements = 0; + m_bPreserveWhitespace = true; + if( pstrXML != NULL ) Load(pstrXML); +} + +CMarkup::~CMarkup() +{ + Release(); +} + +bool CMarkup::IsValid() const +{ + return m_pElements != NULL; +} + +void CMarkup::SetPreserveWhitespace(bool bPreserve) +{ + m_bPreserveWhitespace = bPreserve; +} + +bool CMarkup::Load(LPCTSTR pstrXML) +{ + Release(); + SIZE_T cchLen = _tcslen(pstrXML) + 1; + m_pstrXML = static_cast(malloc(cchLen * sizeof(TCHAR))); + ::CopyMemory(m_pstrXML, pstrXML, cchLen * sizeof(TCHAR)); + bool bRes = _Parse(); + if( !bRes ) Release(); + return bRes; +} + +bool CMarkup::LoadFromMem(BYTE* pByte, DWORD dwSize, int encoding) +{ +#ifdef _UNICODE + if (encoding == XMLFILE_ENCODING_UTF8) + { + if( dwSize >= 3 && pByte[0] == 0xEF && pByte[1] == 0xBB && pByte[2] == 0xBF ) + { + pByte += 3; dwSize -= 3; + } + DWORD nWide = ::MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)pByte, dwSize, NULL, 0 ); + + m_pstrXML = static_cast(malloc((nWide + 1)*sizeof(TCHAR))); + ::MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)pByte, dwSize, m_pstrXML, nWide ); + m_pstrXML[nWide] = _T('\0'); + } + else if (encoding == XMLFILE_ENCODING_ASNI) + { + DWORD nWide = ::MultiByteToWideChar( CP_ACP, 0, (LPCSTR)pByte, dwSize, NULL, 0 ); + + m_pstrXML = static_cast(malloc((nWide + 1)*sizeof(TCHAR))); + ::MultiByteToWideChar( CP_ACP, 0, (LPCSTR)pByte, dwSize, m_pstrXML, nWide ); + m_pstrXML[nWide] = _T('\0'); + } + else + { + if ( dwSize >= 2 && ( ( pByte[0] == 0xFE && pByte[1] == 0xFF ) || ( pByte[0] == 0xFF && pByte[1] == 0xFE ) ) ) + { + dwSize = dwSize / 2 - 1; + + if ( pByte[0] == 0xFE && pByte[1] == 0xFF ) + { + pByte += 2; + + for ( DWORD nSwap = 0 ; nSwap < dwSize ; nSwap ++ ) + { + register CHAR nTemp = pByte[ ( nSwap << 1 ) + 0 ]; + pByte[ ( nSwap << 1 ) + 0 ] = pByte[ ( nSwap << 1 ) + 1 ]; + pByte[ ( nSwap << 1 ) + 1 ] = nTemp; + } + } + else + { + pByte += 2; + } + + m_pstrXML = static_cast(malloc((dwSize + 1)*sizeof(TCHAR))); + ::CopyMemory( m_pstrXML, pByte, dwSize * sizeof(TCHAR) ); + m_pstrXML[dwSize] = _T('\0'); + + pByte -= 2; + } + } +#else // !_UNICODE + if (encoding == XMLFILE_ENCODING_UTF8) + { + if( dwSize >= 3 && pByte[0] == 0xEF && pByte[1] == 0xBB && pByte[2] == 0xBF ) + { + pByte += 3; dwSize -= 3; + } + DWORD nWide = ::MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)pByte, dwSize, NULL, 0 ); + + LPWSTR w_str = static_cast(malloc((nWide + 1)*sizeof(WCHAR))); + ::MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)pByte, dwSize, w_str, nWide ); + w_str[nWide] = L'\0'; + + DWORD wide = ::WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)w_str, nWide, NULL, 0, NULL, NULL); + + m_pstrXML = static_cast(malloc((wide + 1)*sizeof(TCHAR))); + ::WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)w_str, nWide, m_pstrXML, wide, NULL, NULL); + m_pstrXML[wide] = _T('\0'); + + free(w_str); + } + else if (encoding == XMLFILE_ENCODING_UNICODE) + { + if ( dwSize >= 2 && ( ( pByte[0] == 0xFE && pByte[1] == 0xFF ) || ( pByte[0] == 0xFF && pByte[1] == 0xFE ) ) ) + { + dwSize = dwSize / 2 - 1; + + if ( pByte[0] == 0xFE && pByte[1] == 0xFF ) + { + pByte += 2; + + for ( DWORD nSwap = 0 ; nSwap < dwSize ; nSwap ++ ) + { + register CHAR nTemp = pByte[ ( nSwap << 1 ) + 0 ]; + pByte[ ( nSwap << 1 ) + 0 ] = pByte[ ( nSwap << 1 ) + 1 ]; + pByte[ ( nSwap << 1 ) + 1 ] = nTemp; + } + } + else + { + pByte += 2; + } + + DWORD nWide = ::WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pByte, dwSize, NULL, 0, NULL, NULL); + m_pstrXML = static_cast(malloc((nWide + 1)*sizeof(TCHAR))); + ::WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)pByte, dwSize, m_pstrXML, nWide, NULL, NULL); + m_pstrXML[nWide] = _T('\0'); + + pByte -= 2; + } + } + else + { + m_pstrXML = static_cast(malloc((dwSize + 1)*sizeof(TCHAR))); + ::CopyMemory( m_pstrXML, pByte, dwSize * sizeof(TCHAR) ); + m_pstrXML[dwSize] = _T('\0'); + } +#endif // _UNICODE + + bool bRes = _Parse(); + if( !bRes ) Release(); + return bRes; +} + +bool CMarkup::LoadFromFile(LPCTSTR pstrFilename, int encoding) +{ + Release(); + CDuiString sFile = CPaintManagerUI::GetResourcePath(); + if( CPaintManagerUI::GetResourceZip().IsEmpty() ) { + sFile += pstrFilename; + HANDLE hFile = ::CreateFile(sFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if( hFile == INVALID_HANDLE_VALUE ) return _Failed(_T("Error opening file")); + DWORD dwSize = ::GetFileSize(hFile, NULL); + if( dwSize == 0 ) return _Failed(_T("File is empty")); + if ( dwSize > 4096*1024 ) return _Failed(_T("File too large")); + + DWORD dwRead = 0; + BYTE* pByte = new BYTE[ dwSize ]; + ::ReadFile( hFile, pByte, dwSize, &dwRead, NULL ); + ::CloseHandle( hFile ); + + if( dwRead != dwSize ) { + delete[] pByte; + Release(); + return _Failed(_T("Could not read file")); + } + bool ret = LoadFromMem(pByte, dwSize, encoding); + delete[] pByte; + + return ret; + } + else { + sFile += CPaintManagerUI::GetResourceZip(); + HZIP hz = NULL; + if( CPaintManagerUI::IsCachedResourceZip() ) hz = (HZIP)CPaintManagerUI::GetResourceZipHandle(); + else hz = OpenZip((void*)sFile.GetData(), 0, 2); + if( hz == NULL ) return _Failed(_T("Error opening zip file")); + ZIPENTRY ze; + int i; + if( FindZipItem(hz, pstrFilename, true, &i, &ze) != 0 ) return _Failed(_T("Could not find ziped file")); + DWORD dwSize = ze.unc_size; + if( dwSize == 0 ) return _Failed(_T("File is empty")); + if ( dwSize > 4096*1024 ) return _Failed(_T("File too large")); + BYTE* pByte = new BYTE[ dwSize ]; + int res = UnzipItem(hz, i, pByte, dwSize, 3); + if( res != 0x00000000 && res != 0x00000600) { + delete[] pByte; + if( !CPaintManagerUI::IsCachedResourceZip() ) CloseZip(hz); + return _Failed(_T("Could not unzip file")); + } + if( !CPaintManagerUI::IsCachedResourceZip() ) CloseZip(hz); + bool ret = LoadFromMem(pByte, dwSize, encoding); + delete[] pByte; + + return ret; + } +} + +void CMarkup::Release() +{ + if( m_pstrXML != NULL ) free(m_pstrXML); + if( m_pElements != NULL ) free(m_pElements); + m_pstrXML = NULL; + m_pElements = NULL; + m_nElements; +} + +void CMarkup::GetLastErrorMessage(LPTSTR pstrMessage, SIZE_T cchMax) const +{ + _tcsncpy(pstrMessage, m_szErrorMsg, cchMax); +} + +void CMarkup::GetLastErrorLocation(LPTSTR pstrSource, SIZE_T cchMax) const +{ + _tcsncpy(pstrSource, m_szErrorXML, cchMax); +} + +CMarkupNode CMarkup::GetRoot() +{ + if( m_nElements == 0 ) return CMarkupNode(); + return CMarkupNode(this, 1); +} + +bool CMarkup::_Parse() +{ + _ReserveElement(); // Reserve index 0 for errors + ::ZeroMemory(m_szErrorMsg, sizeof(m_szErrorMsg)); + ::ZeroMemory(m_szErrorXML, sizeof(m_szErrorXML)); + LPTSTR pstrXML = m_pstrXML; + return _Parse(pstrXML, 0); +} + +bool CMarkup::_Parse(LPTSTR& pstrText, ULONG iParent) +{ + _SkipWhitespace(pstrText); + ULONG iPrevious = 0; + for( ; ; ) + { + if( *pstrText == _T('\0') && iParent <= 1 ) return true; + _SkipWhitespace(pstrText); + if( *pstrText != _T('<') ) return _Failed(_T("Expected start tag"), pstrText); + if( pstrText[1] == _T('/') ) return true; + *pstrText++ = _T('\0'); + _SkipWhitespace(pstrText); + // Skip comment or processing directive + if( *pstrText == _T('!') || *pstrText == _T('?') ) { + TCHAR ch = *pstrText; + if( *pstrText == _T('!') ) ch = _T('-'); + while( *pstrText != _T('\0') && !(*pstrText == ch && *(pstrText + 1) == _T('>')) ) pstrText = ::CharNext(pstrText); + if( *pstrText != _T('\0') ) pstrText += 2; + _SkipWhitespace(pstrText); + continue; + } + _SkipWhitespace(pstrText); + // Fill out element structure + XMLELEMENT* pEl = _ReserveElement(); + ULONG iPos = pEl - m_pElements; + pEl->iStart = pstrText - m_pstrXML; + pEl->iParent = iParent; + pEl->iNext = pEl->iChild = 0; + if( iPrevious != 0 ) m_pElements[iPrevious].iNext = iPos; + else if( iParent > 0 ) m_pElements[iParent].iChild = iPos; + iPrevious = iPos; + // Parse name + LPCTSTR pstrName = pstrText; + _SkipIdentifier(pstrText); + LPTSTR pstrNameEnd = pstrText; + if( *pstrText == _T('\0') ) return _Failed(_T("Error parsing element name"), pstrText); + // Parse attributes + if( !_ParseAttributes(pstrText) ) return false; + _SkipWhitespace(pstrText); + if( pstrText[0] == _T('/') && pstrText[1] == _T('>') ) + { + pEl->iData = pstrText - m_pstrXML; + *pstrText = _T('\0'); + pstrText += 2; + } + else + { + if( *pstrText != _T('>') ) return _Failed(_T("Expected start-tag closing"), pstrText); + // Parse node data + pEl->iData = ++pstrText - m_pstrXML; + LPTSTR pstrDest = pstrText; + if( !_ParseData(pstrText, pstrDest, _T('<')) ) return false; + // Determine type of next element + if( *pstrText == _T('\0') && iParent <= 1 ) return true; + if( *pstrText != _T('<') ) return _Failed(_T("Expected end-tag start"), pstrText); + if( pstrText[0] == _T('<') && pstrText[1] != _T('/') ) + { + if( !_Parse(pstrText, iPos) ) return false; + } + if( pstrText[0] == _T('<') && pstrText[1] == _T('/') ) + { + *pstrDest = _T('\0'); + *pstrText = _T('\0'); + pstrText += 2; + _SkipWhitespace(pstrText); + SIZE_T cchName = pstrNameEnd - pstrName; + if( _tcsncmp(pstrText, pstrName, cchName) != 0 ) return _Failed(_T("Unmatched closing tag"), pstrText); + pstrText += cchName; + _SkipWhitespace(pstrText); + if( *pstrText++ != _T('>') ) return _Failed(_T("Unmatched closing tag"), pstrText); + } + } + *pstrNameEnd = _T('\0'); + _SkipWhitespace(pstrText); + } +} + +CMarkup::XMLELEMENT* CMarkup::_ReserveElement() +{ + if( m_nElements == 0 ) m_nReservedElements = 0; + if( m_nElements >= m_nReservedElements ) { + m_nReservedElements += (m_nReservedElements / 2) + 500; + m_pElements = static_cast(realloc(m_pElements, m_nReservedElements * sizeof(XMLELEMENT))); + } + return &m_pElements[m_nElements++]; +} + +void CMarkup::_SkipWhitespace(LPCTSTR& pstr) const +{ + while( *pstr > _T('\0') && *pstr <= _T(' ') ) pstr = ::CharNext(pstr); +} + +void CMarkup::_SkipWhitespace(LPTSTR& pstr) const +{ + while( *pstr > _T('\0') && *pstr <= _T(' ') ) pstr = ::CharNext(pstr); +} + +void CMarkup::_SkipIdentifier(LPCTSTR& pstr) const +{ + // ֻӢģû + while( *pstr != _T('\0') && (*pstr == _T('_') || *pstr == _T(':') || _istalnum(*pstr)) ) pstr = ::CharNext(pstr); +} + +void CMarkup::_SkipIdentifier(LPTSTR& pstr) const +{ + // ֻӢģû + while( *pstr != _T('\0') && (*pstr == _T('_') || *pstr == _T(':') || _istalnum(*pstr)) ) pstr = ::CharNext(pstr); +} + +bool CMarkup::_ParseAttributes(LPTSTR& pstrText) +{ + if( *pstrText == _T('>') ) return true; + *pstrText++ = _T('\0'); + _SkipWhitespace(pstrText); + while( *pstrText != _T('\0') && *pstrText != _T('>') && *pstrText != _T('/') ) { + _SkipIdentifier(pstrText); + LPTSTR pstrIdentifierEnd = pstrText; + _SkipWhitespace(pstrText); + if( *pstrText != _T('=') ) return _Failed(_T("Error while parsing attributes"), pstrText); + *pstrText++ = _T(' '); + *pstrIdentifierEnd = _T('\0'); + _SkipWhitespace(pstrText); + if( *pstrText++ != _T('\"') ) return _Failed(_T("Expected attribute value"), pstrText); + LPTSTR pstrDest = pstrText; + if( !_ParseData(pstrText, pstrDest, _T('\"')) ) return false; + if( *pstrText == _T('\0') ) return _Failed(_T("Error while parsing attribute string"), pstrText); + *pstrDest = _T('\0'); + if( pstrText != pstrDest ) *pstrText = _T(' '); + pstrText++; + _SkipWhitespace(pstrText); + } + return true; +} + +bool CMarkup::_ParseData(LPTSTR& pstrText, LPTSTR& pstrDest, char cEnd) +{ + while( *pstrText != _T('\0') && *pstrText != cEnd ) { + if( *pstrText == _T('&') ) { + while( *pstrText == _T('&') ) { + _ParseMetaChar(++pstrText, pstrDest); + } + if (*pstrText == cEnd) + break; + } + + if( *pstrText == _T(' ') ) { + *pstrDest++ = *pstrText++; + if( !m_bPreserveWhitespace ) _SkipWhitespace(pstrText); + } + else { + LPTSTR pstrTemp = ::CharNext(pstrText); + while( pstrText < pstrTemp) { + *pstrDest++ = *pstrText++; + } + } + } + // Make sure that MapAttributes() works correctly when it parses + // over a value that has been transformed. + LPTSTR pstrFill = pstrDest + 1; + while( pstrFill < pstrText ) *pstrFill++ = _T(' '); + return true; +} + +void CMarkup::_ParseMetaChar(LPTSTR& pstrText, LPTSTR& pstrDest) +{ + if( pstrText[0] == _T('a') && pstrText[1] == _T('m') && pstrText[2] == _T('p') && pstrText[3] == _T(';') ) { + *pstrDest++ = _T('&'); + pstrText += 4; + } + else if( pstrText[0] == _T('l') && pstrText[1] == _T('t') && pstrText[2] == _T(';') ) { + *pstrDest++ = _T('<'); + pstrText += 3; + } + else if( pstrText[0] == _T('g') && pstrText[1] == _T('t') && pstrText[2] == _T(';') ) { + *pstrDest++ = _T('>'); + pstrText += 3; + } + else if( pstrText[0] == _T('q') && pstrText[1] == _T('u') && pstrText[2] == _T('o') && pstrText[3] == _T('t') && pstrText[4] == _T(';') ) { + *pstrDest++ = _T('\"'); + pstrText += 5; + } + else if( pstrText[0] == _T('a') && pstrText[1] == _T('p') && pstrText[2] == _T('o') && pstrText[3] == _T('s') && pstrText[4] == _T(';') ) { + *pstrDest++ = _T('\''); + pstrText += 5; + } + else { + *pstrDest++ = _T('&'); + } +} + +bool CMarkup::_Failed(LPCTSTR pstrError, LPCTSTR pstrLocation) +{ + // Register last error + TRACE(_T("XML Error: %s"), pstrError); + if( pstrLocation != NULL ) TRACE(pstrLocation); + _tcsncpy(m_szErrorMsg, pstrError, (sizeof(m_szErrorMsg) / sizeof(m_szErrorMsg[0])) - 1); + _tcsncpy(m_szErrorXML, pstrLocation != NULL ? pstrLocation : _T(""), lengthof(m_szErrorXML) - 1); + return false; // Always return 'false' +} + +} // namespace DuiLib diff --git a/DuiLib/Core/UIMarkup.h b/DuiLib/Core/UIMarkup.h new file mode 100644 index 00000000..3936b9ac --- /dev/null +++ b/DuiLib/Core/UIMarkup.h @@ -0,0 +1,119 @@ +#ifndef __UIMARKUP_H__ +#define __UIMARKUP_H__ + +#pragma once + +namespace DuiLib { + +enum +{ + XMLFILE_ENCODING_UTF8 = 0, + XMLFILE_ENCODING_UNICODE = 1, + XMLFILE_ENCODING_ASNI = 2, +}; + +class CMarkup; +class CMarkupNode; + + +class UILIB_API CMarkup +{ + friend class CMarkupNode; +public: + CMarkup(LPCTSTR pstrXML = NULL); + ~CMarkup(); + + bool Load(LPCTSTR pstrXML); + bool LoadFromMem(BYTE* pByte, DWORD dwSize, int encoding = XMLFILE_ENCODING_UTF8); + bool LoadFromFile(LPCTSTR pstrFilename, int encoding = XMLFILE_ENCODING_UTF8); + void Release(); + bool IsValid() const; + + void SetPreserveWhitespace(bool bPreserve = true); + void GetLastErrorMessage(LPTSTR pstrMessage, SIZE_T cchMax) const; + void GetLastErrorLocation(LPTSTR pstrSource, SIZE_T cchMax) const; + + CMarkupNode GetRoot(); + +private: + typedef struct tagXMLELEMENT + { + ULONG iStart; + ULONG iChild; + ULONG iNext; + ULONG iParent; + ULONG iData; + } XMLELEMENT; + + LPTSTR m_pstrXML; + XMLELEMENT* m_pElements; + ULONG m_nElements; + ULONG m_nReservedElements; + TCHAR m_szErrorMsg[100]; + TCHAR m_szErrorXML[50]; + bool m_bPreserveWhitespace; + +private: + bool _Parse(); + bool _Parse(LPTSTR& pstrText, ULONG iParent); + XMLELEMENT* _ReserveElement(); + inline void _SkipWhitespace(LPTSTR& pstr) const; + inline void _SkipWhitespace(LPCTSTR& pstr) const; + inline void _SkipIdentifier(LPTSTR& pstr) const; + inline void _SkipIdentifier(LPCTSTR& pstr) const; + bool _ParseData(LPTSTR& pstrText, LPTSTR& pstrData, char cEnd); + void _ParseMetaChar(LPTSTR& pstrText, LPTSTR& pstrDest); + bool _ParseAttributes(LPTSTR& pstrText); + bool _Failed(LPCTSTR pstrError, LPCTSTR pstrLocation = NULL); +}; + + +class UILIB_API CMarkupNode +{ + friend class CMarkup; +private: + CMarkupNode(); + CMarkupNode(CMarkup* pOwner, int iPos); + +public: + bool IsValid() const; + + CMarkupNode GetParent(); + CMarkupNode GetSibling(); + CMarkupNode GetChild(); + CMarkupNode GetChild(LPCTSTR pstrName); + + bool HasSiblings() const; + bool HasChildren() const; + LPCTSTR GetName() const; + LPCTSTR GetValue() const; + + bool HasAttributes(); + bool HasAttribute(LPCTSTR pstrName); + int GetAttributeCount(); + LPCTSTR GetAttributeName(int iIndex); + LPCTSTR GetAttributeValue(int iIndex); + LPCTSTR GetAttributeValue(LPCTSTR pstrName); + bool GetAttributeValue(int iIndex, LPTSTR pstrValue, SIZE_T cchMax); + bool GetAttributeValue(LPCTSTR pstrName, LPTSTR pstrValue, SIZE_T cchMax); + +private: + void _MapAttributes(); + + enum { MAX_XML_ATTRIBUTES = 64 }; + + typedef struct + { + ULONG iName; + ULONG iValue; + } XMLATTRIBUTE; + + int m_iPos; + int m_nAttributes; + XMLATTRIBUTE m_aAttributes[MAX_XML_ATTRIBUTES]; + CMarkup* m_pOwner; +}; + +} // namespace DuiLib + +#endif // __UIMARKUP_H__ diff --git a/DuiLib/Core/UIRender.cpp b/DuiLib/Core/UIRender.cpp new file mode 100644 index 00000000..82a7c2fc --- /dev/null +++ b/DuiLib/Core/UIRender.cpp @@ -0,0 +1,2061 @@ +#include "StdAfx.h" + +/////////////////////////////////////////////////////////////////////////////////////// +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened +typedef DWORD ZRESULT; +typedef struct +{ + int index; // index of this file within the zip + char name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; +typedef struct +{ + int index; // index of this file within the zip + TCHAR name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; +#define OpenZip OpenZipU +#define CloseZip(hz) CloseZipU(hz) +extern HZIP OpenZipU(void *z,unsigned int len,DWORD flags); +extern ZRESULT CloseZipU(HZIP hz); +#ifdef _UNICODE +#define ZIPENTRY ZIPENTRYW +#define GetZipItem GetZipItemW +#define FindZipItem FindZipItemW +#else +#define GetZipItem GetZipItemA +#define FindZipItem FindZipItemA +#endif +extern ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze); +extern ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *ze); +extern ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze); +extern ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *ze); +extern ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags); +/////////////////////////////////////////////////////////////////////////////////////// + +extern "C" +{ + extern unsigned char *stbi_load_from_memory(unsigned char const *buffer, int len, int *x, int *y, \ + int *comp, int req_comp); + extern void stbi_image_free(void *retval_from_stbi_load); + +}; + +namespace DuiLib { + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +CRenderClip::~CRenderClip() +{ + ASSERT(::GetObjectType(hDC)==OBJ_DC || ::GetObjectType(hDC)==OBJ_MEMDC); + ASSERT(::GetObjectType(hRgn)==OBJ_REGION); + ASSERT(::GetObjectType(hOldRgn)==OBJ_REGION); + ::SelectClipRgn(hDC, hOldRgn); + ::DeleteObject(hOldRgn); + ::DeleteObject(hRgn); +} + +void CRenderClip::GenerateClip(HDC hDC, RECT rc, CRenderClip& clip) +{ + RECT rcClip = { 0 }; + ::GetClipBox(hDC, &rcClip); + clip.hOldRgn = ::CreateRectRgnIndirect(&rcClip); + clip.hRgn = ::CreateRectRgnIndirect(&rc); + ::ExtSelectClipRgn(hDC, clip.hRgn, RGN_AND); + clip.hDC = hDC; + clip.rcItem = rc; +} + +void CRenderClip::GenerateRoundClip(HDC hDC, RECT rc, RECT rcItem, int width, int height, CRenderClip& clip) +{ + RECT rcClip = { 0 }; + ::GetClipBox(hDC, &rcClip); + clip.hOldRgn = ::CreateRectRgnIndirect(&rcClip); + clip.hRgn = ::CreateRectRgnIndirect(&rc); + HRGN hRgnItem = ::CreateRoundRectRgn(rcItem.left, rcItem.top, rcItem.right + 1, rcItem.bottom + 1, width, height); + ::CombineRgn(clip.hRgn, clip.hRgn, hRgnItem, RGN_AND); + ::ExtSelectClipRgn(hDC, clip.hRgn, RGN_AND); + clip.hDC = hDC; + clip.rcItem = rc; + ::DeleteObject(hRgnItem); +} + +void CRenderClip::UseOldClipBegin(HDC hDC, CRenderClip& clip) +{ + ::SelectClipRgn(hDC, clip.hOldRgn); +} + +void CRenderClip::UseOldClipEnd(HDC hDC, CRenderClip& clip) +{ + ::SelectClipRgn(hDC, clip.hRgn); +} + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +static const float OneThird = 1.0f / 3; + +static void RGBtoHSL(DWORD ARGB, float* H, float* S, float* L) { + const float + R = (float)GetRValue(ARGB), + G = (float)GetGValue(ARGB), + B = (float)GetBValue(ARGB), + nR = (R<0?0:(R>255?255:R))/255, + nG = (G<0?0:(G>255?255:G))/255, + nB = (B<0?0:(B>255?255:B))/255, + m = min(min(nR,nG),nB), + M = max(max(nR,nG),nB); + *L = (m + M)/2; + if (M==m) *H = *S = 0; + else { + const float + f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)), + i = (nR==m)?3.0f:((nG==m)?5.0f:1.0f); + *H = (i-f/(M-m)); + if (*H>=6) *H-=6; + *H*=60; + *S = (2*(*L)<=1)?((M-m)/(M+m)):((M-m)/(2-M-m)); + } +} + +static void HSLtoRGB(DWORD* ARGB, float H, float S, float L) { + const float + q = 2*L<1?L*(1+S):(L+S-L*S), + p = 2*L-q, + h = H/360, + tr = h + OneThird, + tg = h, + tb = h - OneThird, + ntr = tr<0?tr+1:(tr>1?tr-1:tr), + ntg = tg<0?tg+1:(tg>1?tg-1:tg), + ntb = tb<0?tb+1:(tb>1?tb-1:tb), + R = 255*(6*ntr<1?p+(q-p)*6*ntr:(2*ntr<1?q:(3*ntr<2?p+(q-p)*6*(2.0f*OneThird-ntr):p))), + G = 255*(6*ntg<1?p+(q-p)*6*ntg:(2*ntg<1?q:(3*ntg<2?p+(q-p)*6*(2.0f*OneThird-ntg):p))), + B = 255*(6*ntb<1?p+(q-p)*6*ntb:(2*ntb<1?q:(3*ntb<2?p+(q-p)*6*(2.0f*OneThird-ntb):p))); + *ARGB &= 0xFF000000; + *ARGB |= RGB( (BYTE)(R<0?0:(R>255?255:R)), (BYTE)(G<0?0:(G>255?255:G)), (BYTE)(B<0?0:(B>255?255:B)) ); +} + +static COLORREF PixelAlpha(COLORREF clrSrc, double src_darken, COLORREF clrDest, double dest_darken) +{ + return RGB (GetRValue (clrSrc) * src_darken + GetRValue (clrDest) * dest_darken, + GetGValue (clrSrc) * src_darken + GetGValue (clrDest) * dest_darken, + GetBValue (clrSrc) * src_darken + GetBValue (clrDest) * dest_darken); + +} + +static BOOL WINAPI AlphaBitBlt(HDC hDC, int nDestX, int nDestY, int dwWidth, int dwHeight, HDC hSrcDC, \ + int nSrcX, int nSrcY, int wSrc, int hSrc, BLENDFUNCTION ftn) +{ + HDC hTempDC = ::CreateCompatibleDC(hDC); + if (NULL == hTempDC) + return FALSE; + + //Creates Source DIB + LPBITMAPINFO lpbiSrc = NULL; + // Fill in the BITMAPINFOHEADER + lpbiSrc = (LPBITMAPINFO) new BYTE[sizeof(BITMAPINFOHEADER)]; + if (lpbiSrc == NULL) + { + ::DeleteDC(hTempDC); + return FALSE; + } + lpbiSrc->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + lpbiSrc->bmiHeader.biWidth = dwWidth; + lpbiSrc->bmiHeader.biHeight = dwHeight; + lpbiSrc->bmiHeader.biPlanes = 1; + lpbiSrc->bmiHeader.biBitCount = 32; + lpbiSrc->bmiHeader.biCompression = BI_RGB; + lpbiSrc->bmiHeader.biSizeImage = dwWidth * dwHeight; + lpbiSrc->bmiHeader.biXPelsPerMeter = 0; + lpbiSrc->bmiHeader.biYPelsPerMeter = 0; + lpbiSrc->bmiHeader.biClrUsed = 0; + lpbiSrc->bmiHeader.biClrImportant = 0; + + COLORREF* pSrcBits = NULL; + HBITMAP hSrcDib = CreateDIBSection ( + hSrcDC, lpbiSrc, DIB_RGB_COLORS, (void **)&pSrcBits, + NULL, NULL); + + if ((NULL == hSrcDib) || (NULL == pSrcBits)) + { + delete [] lpbiSrc; + ::DeleteDC(hTempDC); + return FALSE; + } + + HBITMAP hOldTempBmp = (HBITMAP)::SelectObject (hTempDC, hSrcDib); + ::StretchBlt(hTempDC, 0, 0, dwWidth, dwHeight, hSrcDC, nSrcX, nSrcY, wSrc, hSrc, SRCCOPY); + ::SelectObject (hTempDC, hOldTempBmp); + + //Creates Destination DIB + LPBITMAPINFO lpbiDest = NULL; + // Fill in the BITMAPINFOHEADER + lpbiDest = (LPBITMAPINFO) new BYTE[sizeof(BITMAPINFOHEADER)]; + if (lpbiDest == NULL) + { + delete [] lpbiSrc; + ::DeleteObject(hSrcDib); + ::DeleteDC(hTempDC); + return FALSE; + } + + lpbiDest->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + lpbiDest->bmiHeader.biWidth = dwWidth; + lpbiDest->bmiHeader.biHeight = dwHeight; + lpbiDest->bmiHeader.biPlanes = 1; + lpbiDest->bmiHeader.biBitCount = 32; + lpbiDest->bmiHeader.biCompression = BI_RGB; + lpbiDest->bmiHeader.biSizeImage = dwWidth * dwHeight; + lpbiDest->bmiHeader.biXPelsPerMeter = 0; + lpbiDest->bmiHeader.biYPelsPerMeter = 0; + lpbiDest->bmiHeader.biClrUsed = 0; + lpbiDest->bmiHeader.biClrImportant = 0; + + COLORREF* pDestBits = NULL; + HBITMAP hDestDib = CreateDIBSection ( + hDC, lpbiDest, DIB_RGB_COLORS, (void **)&pDestBits, + NULL, NULL); + + if ((NULL == hDestDib) || (NULL == pDestBits)) + { + delete [] lpbiSrc; + ::DeleteObject(hSrcDib); + ::DeleteDC(hTempDC); + return FALSE; + } + + ::SelectObject (hTempDC, hDestDib); + ::BitBlt (hTempDC, 0, 0, dwWidth, dwHeight, hDC, nDestX, nDestY, SRCCOPY); + ::SelectObject (hTempDC, hOldTempBmp); + + double src_darken; + BYTE nAlpha; + + for (int pixel = 0; pixel < dwWidth * dwHeight; pixel++, pSrcBits++, pDestBits++) + { + nAlpha = LOBYTE(*pSrcBits >> 24); + src_darken = (double) (nAlpha * ftn.SourceConstantAlpha) / 255.0 / 255.0; + if( src_darken < 0.0 ) src_darken = 0.0; + *pDestBits = PixelAlpha(*pSrcBits, src_darken, *pDestBits, 1.0 - src_darken); + } //for + + ::SelectObject (hTempDC, hDestDib); + ::BitBlt (hDC, nDestX, nDestY, dwWidth, dwHeight, hTempDC, 0, 0, SRCCOPY); + ::SelectObject (hTempDC, hOldTempBmp); + + delete [] lpbiDest; + ::DeleteObject(hDestDib); + + delete [] lpbiSrc; + ::DeleteObject(hSrcDib); + + ::DeleteDC(hTempDC); + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////////////// +// +// + +DWORD CRenderEngine::AdjustColor(DWORD dwColor, short H, short S, short L) +{ + if( H == 180 && S == 100 && L == 100 ) return dwColor; + float fH, fS, fL; + float S1 = S / 100.0f; + float L1 = L / 100.0f; + RGBtoHSL(dwColor, &fH, &fS, &fL); + fH += (H - 180); + fH = fH > 0 ? fH : fH + 360; + fS *= S1; + fL *= L1; + HSLtoRGB(&dwColor, fH, fS, fL); + return dwColor; +} + +TImageInfo* CRenderEngine::LoadImage(STRINGorID bitmap, LPCTSTR type, DWORD mask) +{ + LPBYTE pData = NULL; + DWORD dwSize = 0; + + do + { + if( type == NULL ) { + CDuiString sFile = CPaintManagerUI::GetResourcePath(); + if( CPaintManagerUI::GetResourceZip().IsEmpty() ) { + sFile += bitmap.m_lpstr; + HANDLE hFile = ::CreateFile(sFile.GetData(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, \ + FILE_ATTRIBUTE_NORMAL, NULL); + if( hFile == INVALID_HANDLE_VALUE ) break; + dwSize = ::GetFileSize(hFile, NULL); + if( dwSize == 0 ) break; + + DWORD dwRead = 0; + pData = new BYTE[ dwSize ]; + ::ReadFile( hFile, pData, dwSize, &dwRead, NULL ); + ::CloseHandle( hFile ); + + if( dwRead != dwSize ) { + delete[] pData; + pData = NULL; + break; + } + } + else { + sFile += CPaintManagerUI::GetResourceZip(); + HZIP hz = NULL; + if( CPaintManagerUI::IsCachedResourceZip() ) hz = (HZIP)CPaintManagerUI::GetResourceZipHandle(); + else hz = OpenZip((void*)sFile.GetData(), 0, 2); + if( hz == NULL ) break; + ZIPENTRY ze; + int i; + if( FindZipItem(hz, bitmap.m_lpstr, true, &i, &ze) != 0 ) break; + dwSize = ze.unc_size; + if( dwSize == 0 ) break; + pData = new BYTE[ dwSize ]; + int res = UnzipItem(hz, i, pData, dwSize, 3); + if( res != 0x00000000 && res != 0x00000600) { + delete[] pData; + pData = NULL; + if( !CPaintManagerUI::IsCachedResourceZip() ) CloseZip(hz); + break; + } + if( !CPaintManagerUI::IsCachedResourceZip() ) CloseZip(hz); + } + } + else { + HRSRC hResource = ::FindResource(CPaintManagerUI::GetResourceDll(), bitmap.m_lpstr, type); + if( hResource == NULL ) break; + HGLOBAL hGlobal = ::LoadResource(CPaintManagerUI::GetResourceDll(), hResource); + if( hGlobal == NULL ) { + FreeResource(hResource); + break; + } + + dwSize = ::SizeofResource(CPaintManagerUI::GetResourceDll(), hResource); + if( dwSize == 0 ) break; + pData = new BYTE[ dwSize ]; + ::CopyMemory(pData, (LPBYTE)::LockResource(hGlobal), dwSize); + ::FreeResource(hResource); + } + } while (0); + + while (!pData) + { + //ͼƬ, ֱȥȡbitmap.m_lpstrָ· + HANDLE hFile = ::CreateFile(bitmap.m_lpstr, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, \ + FILE_ATTRIBUTE_NORMAL, NULL); + if( hFile == INVALID_HANDLE_VALUE ) break; + dwSize = ::GetFileSize(hFile, NULL); + if( dwSize == 0 ) break; + + DWORD dwRead = 0; + pData = new BYTE[ dwSize ]; + ::ReadFile( hFile, pData, dwSize, &dwRead, NULL ); + ::CloseHandle( hFile ); + + if( dwRead != dwSize ) { + delete[] pData; + pData = NULL; + } + break; + } + if (!pData) + { + //::MessageBox(0, _T("ȡͼƬʧܣ"), _T("ץBUG"), MB_OK); + return NULL; + } + + LPBYTE pImage = NULL; + int x,y,n; + pImage = stbi_load_from_memory(pData, dwSize, &x, &y, &n, 4); + delete[] pData; + if( !pImage ) { + //::MessageBox(0, _T("ͼƬʧ"), _T("ץBUG"), MB_OK); + return NULL; + } + + BITMAPINFO bmi; + ::ZeroMemory(&bmi, sizeof(BITMAPINFO)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = x; + bmi.bmiHeader.biHeight = -y; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = x * y * 4; + + bool bAlphaChannel = false; + LPBYTE pDest = NULL; + HBITMAP hBitmap = ::CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**)&pDest, NULL, 0); + if( !hBitmap ) { + //::MessageBox(0, _T("CreateDIBSectionʧ"), _T("ץBUG"), MB_OK); + return NULL; + } + + for( int i = 0; i < x * y; i++ ) + { + pDest[i*4 + 3] = pImage[i*4 + 3]; + if( pDest[i*4 + 3] < 255 ) + { + pDest[i*4] = (BYTE)(DWORD(pImage[i*4 + 2])*pImage[i*4 + 3]/255); + pDest[i*4 + 1] = (BYTE)(DWORD(pImage[i*4 + 1])*pImage[i*4 + 3]/255); + pDest[i*4 + 2] = (BYTE)(DWORD(pImage[i*4])*pImage[i*4 + 3]/255); + bAlphaChannel = true; + } + else + { + pDest[i*4] = pImage[i*4 + 2]; + pDest[i*4 + 1] = pImage[i*4 + 1]; + pDest[i*4 + 2] = pImage[i*4]; + } + + if( *(DWORD*)(&pDest[i*4]) == mask ) { + pDest[i*4] = (BYTE)0; + pDest[i*4 + 1] = (BYTE)0; + pDest[i*4 + 2] = (BYTE)0; + pDest[i*4 + 3] = (BYTE)0; + bAlphaChannel = true; + } + } + + stbi_image_free(pImage); + + TImageInfo* data = new TImageInfo; + data->hBitmap = hBitmap; + data->nX = x; + data->nY = y; + data->alphaChannel = bAlphaChannel; + return data; +} + +void CRenderEngine::FreeImage(const TImageInfo* bitmap) +{ + if (bitmap->hBitmap) { + ::DeleteObject(bitmap->hBitmap) ; + } + delete bitmap ; +} + +void CRenderEngine::DrawImage(HDC hDC, HBITMAP hBitmap, const RECT& rc, const RECT& rcPaint, + const RECT& rcBmpPart, const RECT& rcCorners, bool alphaChannel, + BYTE uFade, bool hole, bool xtiled, bool ytiled) +{ + ASSERT(::GetObjectType(hDC)==OBJ_DC || ::GetObjectType(hDC)==OBJ_MEMDC); + + typedef BOOL (WINAPI *LPALPHABLEND)(HDC, int, int, int, int,HDC, int, int, int, int, BLENDFUNCTION); + static LPALPHABLEND lpAlphaBlend = (LPALPHABLEND) ::GetProcAddress(::GetModuleHandle(_T("msimg32.dll")), "AlphaBlend"); + + if( lpAlphaBlend == NULL ) lpAlphaBlend = AlphaBitBlt; + if( hBitmap == NULL ) return; + + HDC hCloneDC = ::CreateCompatibleDC(hDC); + HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(hCloneDC, hBitmap); + ::SetStretchBltMode(hDC, HALFTONE); + + RECT rcTemp = {0}; + RECT rcDest = {0}; + if( lpAlphaBlend && (alphaChannel || uFade < 255) ) { + BLENDFUNCTION bf = { AC_SRC_OVER, 0, uFade, AC_SRC_ALPHA }; + // middle + if( !hole ) { + rcDest.left = rc.left + rcCorners.left; + rcDest.top = rc.top + rcCorners.top; + rcDest.right = rc.right - rc.left - rcCorners.left - rcCorners.right; + rcDest.bottom = rc.bottom - rc.top - rcCorners.top - rcCorners.bottom; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + if( !xtiled && !ytiled ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + lpAlphaBlend(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.left + rcCorners.left, rcBmpPart.top + rcCorners.top, \ + rcBmpPart.right - rcBmpPart.left - rcCorners.left - rcCorners.right, \ + rcBmpPart.bottom - rcBmpPart.top - rcCorners.top - rcCorners.bottom, bf); + } + else if( xtiled && ytiled ) { + LONG lWidth = rcBmpPart.right - rcBmpPart.left - rcCorners.left - rcCorners.right; + LONG lHeight = rcBmpPart.bottom - rcBmpPart.top - rcCorners.top - rcCorners.bottom; + int iTimesX = (rcDest.right - rcDest.left + lWidth - 1) / lWidth; + int iTimesY = (rcDest.bottom - rcDest.top + lHeight - 1) / lHeight; + for( int j = 0; j < iTimesY; ++j ) { + LONG lDestTop = rcDest.top + lHeight * j; + LONG lDestBottom = rcDest.top + lHeight * (j + 1); + LONG lDrawHeight = lHeight; + if( lDestBottom > rcDest.bottom ) { + lDrawHeight -= lDestBottom - rcDest.bottom; + lDestBottom = rcDest.bottom; + } + for( int i = 0; i < iTimesX; ++i ) { + LONG lDestLeft = rcDest.left + lWidth * i; + LONG lDestRight = rcDest.left + lWidth * (i + 1); + LONG lDrawWidth = lWidth; + if( lDestRight > rcDest.right ) { + lDrawWidth -= lDestRight - rcDest.right; + lDestRight = rcDest.right; + } + lpAlphaBlend(hDC, rcDest.left + lWidth * i, rcDest.top + lHeight * j, + lDestRight - lDestLeft, lDestBottom - lDestTop, hCloneDC, + rcBmpPart.left + rcCorners.left, rcBmpPart.top + rcCorners.top, lDrawWidth, lDrawHeight, bf); + } + } + } + else if( xtiled ) { + LONG lWidth = rcBmpPart.right - rcBmpPart.left - rcCorners.left - rcCorners.right; + int iTimes = (rcDest.right - rcDest.left + lWidth - 1) / lWidth; + for( int i = 0; i < iTimes; ++i ) { + LONG lDestLeft = rcDest.left + lWidth * i; + LONG lDestRight = rcDest.left + lWidth * (i + 1); + LONG lDrawWidth = lWidth; + if( lDestRight > rcDest.right ) { + lDrawWidth -= lDestRight - rcDest.right; + lDestRight = rcDest.right; + } + lpAlphaBlend(hDC, lDestLeft, rcDest.top, lDestRight - lDestLeft, rcDest.bottom, + hCloneDC, rcBmpPart.left + rcCorners.left, rcBmpPart.top + rcCorners.top, \ + lDrawWidth, rcBmpPart.bottom - rcBmpPart.top - rcCorners.top - rcCorners.bottom, bf); + } + } + else { // ytiled + LONG lHeight = rcBmpPart.bottom - rcBmpPart.top - rcCorners.top - rcCorners.bottom; + int iTimes = (rcDest.bottom - rcDest.top + lHeight - 1) / lHeight; + for( int i = 0; i < iTimes; ++i ) { + LONG lDestTop = rcDest.top + lHeight * i; + LONG lDestBottom = rcDest.top + lHeight * (i + 1); + LONG lDrawHeight = lHeight; + if( lDestBottom > rcDest.bottom ) { + lDrawHeight -= lDestBottom - rcDest.bottom; + lDestBottom = rcDest.bottom; + } + lpAlphaBlend(hDC, rcDest.left, rcDest.top + lHeight * i, rcDest.right, lDestBottom - lDestTop, + hCloneDC, rcBmpPart.left + rcCorners.left, rcBmpPart.top + rcCorners.top, \ + rcBmpPart.right - rcBmpPart.left - rcCorners.left - rcCorners.right, lDrawHeight, bf); + } + } + } + } + + // left-top + if( rcCorners.left > 0 && rcCorners.top > 0 ) { + rcDest.left = rc.left; + rcDest.top = rc.top; + rcDest.right = rcCorners.left; + rcDest.bottom = rcCorners.top; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + lpAlphaBlend(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.left, rcBmpPart.top, rcCorners.left, rcCorners.top, bf); + } + } + // top + if( rcCorners.top > 0 ) { + rcDest.left = rc.left + rcCorners.left; + rcDest.top = rc.top; + rcDest.right = rc.right - rc.left - rcCorners.left - rcCorners.right; + rcDest.bottom = rcCorners.top; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + lpAlphaBlend(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.left + rcCorners.left, rcBmpPart.top, rcBmpPart.right - rcBmpPart.left - \ + rcCorners.left - rcCorners.right, rcCorners.top, bf); + } + } + // right-top + if( rcCorners.right > 0 && rcCorners.top > 0 ) { + rcDest.left = rc.right - rcCorners.right; + rcDest.top = rc.top; + rcDest.right = rcCorners.right; + rcDest.bottom = rcCorners.top; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + lpAlphaBlend(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.right - rcCorners.right, rcBmpPart.top, rcCorners.right, rcCorners.top, bf); + } + } + // left + if( rcCorners.left > 0 ) { + rcDest.left = rc.left; + rcDest.top = rc.top + rcCorners.top; + rcDest.right = rcCorners.left; + rcDest.bottom = rc.bottom - rc.top - rcCorners.top - rcCorners.bottom; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + lpAlphaBlend(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.left, rcBmpPart.top + rcCorners.top, rcCorners.left, rcBmpPart.bottom - \ + rcBmpPart.top - rcCorners.top - rcCorners.bottom, bf); + } + } + // right + if( rcCorners.right > 0 ) { + rcDest.left = rc.right - rcCorners.right; + rcDest.top = rc.top + rcCorners.top; + rcDest.right = rcCorners.right; + rcDest.bottom = rc.bottom - rc.top - rcCorners.top - rcCorners.bottom; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + lpAlphaBlend(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.right - rcCorners.right, rcBmpPart.top + rcCorners.top, rcCorners.right, \ + rcBmpPart.bottom - rcBmpPart.top - rcCorners.top - rcCorners.bottom, bf); + } + } + // left-bottom + if( rcCorners.left > 0 && rcCorners.bottom > 0 ) { + rcDest.left = rc.left; + rcDest.top = rc.bottom - rcCorners.bottom; + rcDest.right = rcCorners.left; + rcDest.bottom = rcCorners.bottom; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + lpAlphaBlend(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.left, rcBmpPart.bottom - rcCorners.bottom, rcCorners.left, rcCorners.bottom, bf); + } + } + // bottom + if( rcCorners.bottom > 0 ) { + rcDest.left = rc.left + rcCorners.left; + rcDest.top = rc.bottom - rcCorners.bottom; + rcDest.right = rc.right - rc.left - rcCorners.left - rcCorners.right; + rcDest.bottom = rcCorners.bottom; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + lpAlphaBlend(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.left + rcCorners.left, rcBmpPart.bottom - rcCorners.bottom, \ + rcBmpPart.right - rcBmpPart.left - rcCorners.left - rcCorners.right, rcCorners.bottom, bf); + } + } + // right-bottom + if( rcCorners.right > 0 && rcCorners.bottom > 0 ) { + rcDest.left = rc.right - rcCorners.right; + rcDest.top = rc.bottom - rcCorners.bottom; + rcDest.right = rcCorners.right; + rcDest.bottom = rcCorners.bottom; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + lpAlphaBlend(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.right - rcCorners.right, rcBmpPart.bottom - rcCorners.bottom, rcCorners.right, \ + rcCorners.bottom, bf); + } + } + } + else + { + if (rc.right - rc.left == rcBmpPart.right - rcBmpPart.left \ + && rc.bottom - rc.top == rcBmpPart.bottom - rcBmpPart.top \ + && rcCorners.left == 0 && rcCorners.right == 0 && rcCorners.top == 0 && rcCorners.bottom == 0) + { + if( ::IntersectRect(&rcTemp, &rcPaint, &rc) ) { + ::BitBlt(hDC, rcTemp.left, rcTemp.top, rcTemp.right - rcTemp.left, rcTemp.bottom - rcTemp.top, \ + hCloneDC, rcBmpPart.left + rcTemp.left - rc.left, rcBmpPart.top + rcTemp.top - rc.top, SRCCOPY); + } + } + else + { + // middle + if( !hole ) { + rcDest.left = rc.left + rcCorners.left; + rcDest.top = rc.top + rcCorners.top; + rcDest.right = rc.right - rc.left - rcCorners.left - rcCorners.right; + rcDest.bottom = rc.bottom - rc.top - rcCorners.top - rcCorners.bottom; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + if( !xtiled && !ytiled ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + ::StretchBlt(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.left + rcCorners.left, rcBmpPart.top + rcCorners.top, \ + rcBmpPart.right - rcBmpPart.left - rcCorners.left - rcCorners.right, \ + rcBmpPart.bottom - rcBmpPart.top - rcCorners.top - rcCorners.bottom, SRCCOPY); + } + else if( xtiled && ytiled ) { + LONG lWidth = rcBmpPart.right - rcBmpPart.left - rcCorners.left - rcCorners.right; + LONG lHeight = rcBmpPart.bottom - rcBmpPart.top - rcCorners.top - rcCorners.bottom; + int iTimesX = (rcDest.right - rcDest.left + lWidth - 1) / lWidth; + int iTimesY = (rcDest.bottom - rcDest.top + lHeight - 1) / lHeight; + for( int j = 0; j < iTimesY; ++j ) { + LONG lDestTop = rcDest.top + lHeight * j; + LONG lDestBottom = rcDest.top + lHeight * (j + 1); + LONG lDrawHeight = lHeight; + if( lDestBottom > rcDest.bottom ) { + lDrawHeight -= lDestBottom - rcDest.bottom; + lDestBottom = rcDest.bottom; + } + for( int i = 0; i < iTimesX; ++i ) { + LONG lDestLeft = rcDest.left + lWidth * i; + LONG lDestRight = rcDest.left + lWidth * (i + 1); + LONG lDrawWidth = lWidth; + if( lDestRight > rcDest.right ) { + lDrawWidth -= lDestRight - rcDest.right; + lDestRight = rcDest.right; + } + ::BitBlt(hDC, rcDest.left + lWidth * i, rcDest.top + lHeight * j, \ + lDestRight - lDestLeft, lDestBottom - lDestTop, hCloneDC, \ + rcBmpPart.left + rcCorners.left, rcBmpPart.top + rcCorners.top, SRCCOPY); + } + } + } + else if( xtiled ) { + LONG lWidth = rcBmpPart.right - rcBmpPart.left - rcCorners.left - rcCorners.right; + int iTimes = (rcDest.right - rcDest.left + lWidth - 1) / lWidth; + for( int i = 0; i < iTimes; ++i ) { + LONG lDestLeft = rcDest.left + lWidth * i; + LONG lDestRight = rcDest.left + lWidth * (i + 1); + LONG lDrawWidth = lWidth; + if( lDestRight > rcDest.right ) { + lDrawWidth -= lDestRight - rcDest.right; + lDestRight = rcDest.right; + } + ::StretchBlt(hDC, lDestLeft, rcDest.top, lDestRight - lDestLeft, rcDest.bottom, + hCloneDC, rcBmpPart.left + rcCorners.left, rcBmpPart.top + rcCorners.top, \ + lDrawWidth, rcBmpPart.bottom - rcBmpPart.top - rcCorners.top - rcCorners.bottom, SRCCOPY); + } + } + else { // ytiled + LONG lHeight = rcBmpPart.bottom - rcBmpPart.top - rcCorners.top - rcCorners.bottom; + int iTimes = (rcDest.bottom - rcDest.top + lHeight - 1) / lHeight; + for( int i = 0; i < iTimes; ++i ) { + LONG lDestTop = rcDest.top + lHeight * i; + LONG lDestBottom = rcDest.top + lHeight * (i + 1); + LONG lDrawHeight = lHeight; + if( lDestBottom > rcDest.bottom ) { + lDrawHeight -= lDestBottom - rcDest.bottom; + lDestBottom = rcDest.bottom; + } + ::StretchBlt(hDC, rcDest.left, rcDest.top + lHeight * i, rcDest.right, lDestBottom - lDestTop, + hCloneDC, rcBmpPart.left + rcCorners.left, rcBmpPart.top + rcCorners.top, \ + rcBmpPart.right - rcBmpPart.left - rcCorners.left - rcCorners.right, lDrawHeight, SRCCOPY); + } + } + } + } + + // left-top + if( rcCorners.left > 0 && rcCorners.top > 0 ) { + rcDest.left = rc.left; + rcDest.top = rc.top; + rcDest.right = rcCorners.left; + rcDest.bottom = rcCorners.top; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + ::StretchBlt(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.left, rcBmpPart.top, rcCorners.left, rcCorners.top, SRCCOPY); + } + } + // top + if( rcCorners.top > 0 ) { + rcDest.left = rc.left + rcCorners.left; + rcDest.top = rc.top; + rcDest.right = rc.right - rc.left - rcCorners.left - rcCorners.right; + rcDest.bottom = rcCorners.top; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + ::StretchBlt(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.left + rcCorners.left, rcBmpPart.top, rcBmpPart.right - rcBmpPart.left - \ + rcCorners.left - rcCorners.right, rcCorners.top, SRCCOPY); + } + } + // right-top + if( rcCorners.right > 0 && rcCorners.top > 0 ) { + rcDest.left = rc.right - rcCorners.right; + rcDest.top = rc.top; + rcDest.right = rcCorners.right; + rcDest.bottom = rcCorners.top; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + ::StretchBlt(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.right - rcCorners.right, rcBmpPart.top, rcCorners.right, rcCorners.top, SRCCOPY); + } + } + // left + if( rcCorners.left > 0 ) { + rcDest.left = rc.left; + rcDest.top = rc.top + rcCorners.top; + rcDest.right = rcCorners.left; + rcDest.bottom = rc.bottom - rc.top - rcCorners.top - rcCorners.bottom; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + ::StretchBlt(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.left, rcBmpPart.top + rcCorners.top, rcCorners.left, rcBmpPart.bottom - \ + rcBmpPart.top - rcCorners.top - rcCorners.bottom, SRCCOPY); + } + } + // right + if( rcCorners.right > 0 ) { + rcDest.left = rc.right - rcCorners.right; + rcDest.top = rc.top + rcCorners.top; + rcDest.right = rcCorners.right; + rcDest.bottom = rc.bottom - rc.top - rcCorners.top - rcCorners.bottom; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + ::StretchBlt(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.right - rcCorners.right, rcBmpPart.top + rcCorners.top, rcCorners.right, \ + rcBmpPart.bottom - rcBmpPart.top - rcCorners.top - rcCorners.bottom, SRCCOPY); + } + } + // left-bottom + if( rcCorners.left > 0 && rcCorners.bottom > 0 ) { + rcDest.left = rc.left; + rcDest.top = rc.bottom - rcCorners.bottom; + rcDest.right = rcCorners.left; + rcDest.bottom = rcCorners.bottom; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + ::StretchBlt(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.left, rcBmpPart.bottom - rcCorners.bottom, rcCorners.left, rcCorners.bottom, SRCCOPY); + } + } + // bottom + if( rcCorners.bottom > 0 ) { + rcDest.left = rc.left + rcCorners.left; + rcDest.top = rc.bottom - rcCorners.bottom; + rcDest.right = rc.right - rc.left - rcCorners.left - rcCorners.right; + rcDest.bottom = rcCorners.bottom; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + ::StretchBlt(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.left + rcCorners.left, rcBmpPart.bottom - rcCorners.bottom, \ + rcBmpPart.right - rcBmpPart.left - rcCorners.left - rcCorners.right, rcCorners.bottom, SRCCOPY); + } + } + // right-bottom + if( rcCorners.right > 0 && rcCorners.bottom > 0 ) { + rcDest.left = rc.right - rcCorners.right; + rcDest.top = rc.bottom - rcCorners.bottom; + rcDest.right = rcCorners.right; + rcDest.bottom = rcCorners.bottom; + rcDest.right += rcDest.left; + rcDest.bottom += rcDest.top; + if( ::IntersectRect(&rcTemp, &rcPaint, &rcDest) ) { + rcDest.right -= rcDest.left; + rcDest.bottom -= rcDest.top; + ::StretchBlt(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom, hCloneDC, \ + rcBmpPart.right - rcCorners.right, rcBmpPart.bottom - rcCorners.bottom, rcCorners.right, \ + rcCorners.bottom, SRCCOPY); + } + } + } + } + + ::SelectObject(hCloneDC, hOldBitmap); + ::DeleteDC(hCloneDC); +} + + +bool DrawImage(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, const CDuiString& sImageName, \ + const CDuiString& sImageResType, RECT rcItem, RECT rcBmpPart, RECT rcCorner, DWORD dwMask, BYTE bFade, \ + bool bHole, bool bTiledX, bool bTiledY) +{ + if (sImageName.IsEmpty()) { + return false; + } + const TImageInfo* data = NULL; + if( sImageResType.IsEmpty() ) { + data = pManager->GetImageEx((LPCTSTR)sImageName, NULL, dwMask); + } + else { + data = pManager->GetImageEx((LPCTSTR)sImageName, (LPCTSTR)sImageResType, dwMask); + } + if( !data ) return false; + + if( rcBmpPart.left == 0 && rcBmpPart.right == 0 && rcBmpPart.top == 0 && rcBmpPart.bottom == 0 ) { + rcBmpPart.right = data->nX; + rcBmpPart.bottom = data->nY; + } + if (rcBmpPart.right > data->nX) rcBmpPart.right = data->nX; + if (rcBmpPart.bottom > data->nY) rcBmpPart.bottom = data->nY; + + RECT rcTemp; + if( !::IntersectRect(&rcTemp, &rcItem, &rc) ) return true; + if( !::IntersectRect(&rcTemp, &rcItem, &rcPaint) ) return true; + + CRenderEngine::DrawImage(hDC, data->hBitmap, rcItem, rcPaint, rcBmpPart, rcCorner, pManager->IsBackgroundTransparent() ? true : data->alphaChannel, bFade, bHole, bTiledX, bTiledY); + + return true; +} + +bool CRenderEngine::DrawImageString(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, + LPCTSTR pStrImage, LPCTSTR pStrModify) +{ + if ((pManager == NULL) || (hDC == NULL)) return false; + + // 1aaa.jpg + // 2file='aaa.jpg' res='' restype='0' dest='0,0,0,0' source='0,0,0,0' corner='0,0,0,0' + // mask='#FF0000' fade='255' hole='false' xtiled='false' ytiled='false' + + CDuiString sImageName = pStrImage; + CDuiString sImageResType; + RECT rcItem = rc; + RECT rcBmpPart = {0}; + RECT rcCorner = {0}; + DWORD dwMask = 0; + BYTE bFade = 0xFF; + bool bHole = false; + bool bTiledX = false; + bool bTiledY = false; + + int image_count = 0; + + CDuiString sItem; + CDuiString sValue; + LPTSTR pstr = NULL; + + for( int i = 0; i < 2; ++i,image_count = 0 ) { + if( i == 1) + pStrImage = pStrModify; + + if( !pStrImage ) continue; + + while( *pStrImage != _T('\0') ) { + sItem.Empty(); + sValue.Empty(); + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + while( *pStrImage != _T('\0') && *pStrImage != _T('=') && *pStrImage > _T(' ') ) { + LPTSTR pstrTemp = ::CharNext(pStrImage); + while( pStrImage < pstrTemp) { + sItem += *pStrImage++; + } + } + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + if( *pStrImage++ != _T('=') ) break; + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + if( *pStrImage++ != _T('\'') ) break; + while( *pStrImage != _T('\0') && *pStrImage != _T('\'') ) { + LPTSTR pstrTemp = ::CharNext(pStrImage); + while( pStrImage < pstrTemp) { + sValue += *pStrImage++; + } + } + if( *pStrImage++ != _T('\'') ) break; + if( !sValue.IsEmpty() ) { + if( sItem == _T("file") || sItem == _T("res") ) { + if( image_count > 0 ) + DuiLib::DrawImage(hDC, pManager, rc, rcPaint, sImageName, sImageResType, + rcItem, rcBmpPart, rcCorner, dwMask, bFade, bHole, bTiledX, bTiledY); + + sImageName = sValue; + if( sItem == _T("file") ) + ++image_count; + } + else if( sItem == _T("restype") ) { + if( image_count > 0 ) + DuiLib::DrawImage(hDC, pManager, rc, rcPaint, sImageName, sImageResType, + rcItem, rcBmpPart, rcCorner, dwMask, bFade, bHole, bTiledX, bTiledY); + + sImageResType = sValue; + ++image_count; + } + else if( sItem == _T("dest") ) { + rcItem.left = rc.left + _tcstol(sValue.GetData(), &pstr, 10); ASSERT(pstr); + rcItem.top = rc.top + _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcItem.right = rc.left + _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + if (rcItem.right > rc.right) rcItem.right = rc.right; + rcItem.bottom = rc.top + _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + if (rcItem.bottom > rc.bottom) rcItem.bottom = rc.bottom; + } + else if( sItem == _T("source") ) { + rcBmpPart.left = _tcstol(sValue.GetData(), &pstr, 10); ASSERT(pstr); + rcBmpPart.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcBmpPart.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcBmpPart.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + } + else if( sItem == _T("corner") ) { + rcCorner.left = _tcstol(sValue.GetData(), &pstr, 10); ASSERT(pstr); + rcCorner.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcCorner.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + rcCorner.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + } + else if( sItem == _T("mask") ) { + if( sValue[0] == _T('#')) dwMask = _tcstoul(sValue.GetData() + 1, &pstr, 16); + else dwMask = _tcstoul(sValue.GetData(), &pstr, 16); + } + else if( sItem == _T("fade") ) { + bFade = (BYTE)_tcstoul(sValue.GetData(), &pstr, 10); + } + else if( sItem == _T("hole") ) { + bHole = (_tcscmp(sValue.GetData(), _T("true")) == 0); + } + else if( sItem == _T("xtiled") ) { + bTiledX = (_tcscmp(sValue.GetData(), _T("true")) == 0); + } + else if( sItem == _T("ytiled") ) { + bTiledY = (_tcscmp(sValue.GetData(), _T("true")) == 0); + } + } + if( *pStrImage++ != _T(' ') ) break; + } + } + + DuiLib::DrawImage(hDC, pManager, rc, rcPaint, sImageName, sImageResType, + rcItem, rcBmpPart, rcCorner, dwMask, bFade, bHole, bTiledX, bTiledY); + + return true; +} + +void CRenderEngine::DrawColor(HDC hDC, const RECT& rc, DWORD color) +{ + if( color <= 0x00FFFFFF ) return; + + Gdiplus::Graphics graphics( hDC ); + Gdiplus::SolidBrush brush(Gdiplus::Color((LOBYTE((color)>>24)), GetBValue(color), GetGValue(color), GetRValue(color))); + + graphics.FillRectangle(&brush, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top); + +// ԭĴ룬ıɫ͸ͨ͸ģʽͼƬֻ⣬ԸΪgdi+䱳ɫ +// if( color <= 0x00FFFFFF ) return; +// if( color >= 0xFF000000 ) +// { +// ::SetBkColor(hDC, RGB(GetBValue(color), GetGValue(color), GetRValue(color))); +// ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL); +// } +// else +// { +// // Create a new 32bpp bitmap with room for an alpha channel +// BITMAPINFO bmi = { 0 }; +// bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); +// bmi.bmiHeader.biWidth = 1; +// bmi.bmiHeader.biHeight = 1; +// bmi.bmiHeader.biPlanes = 1; +// bmi.bmiHeader.biBitCount = 32; +// bmi.bmiHeader.biCompression = BI_RGB; +// bmi.bmiHeader.biSizeImage = 1 * 1 * sizeof(DWORD); +// LPDWORD pDest = NULL; +// HBITMAP hBitmap = ::CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, (LPVOID*) &pDest, NULL, 0); +// if( !hBitmap ) return; +// +// *pDest = color; +// +// RECT rcBmpPart = {0, 0, 1, 1}; +// RECT rcCorners = {0}; +// DrawImage(hDC, hBitmap, rc, rc, rcBmpPart, rcCorners, true, 255); +// ::DeleteObject(hBitmap); +// } + +} + +void CRenderEngine::DrawGradient(HDC hDC, const RECT& rc, DWORD dwFirst, DWORD dwSecond, bool bVertical, int nSteps) +{ + typedef BOOL (WINAPI *LPALPHABLEND)(HDC, int, int, int, int,HDC, int, int, int, int, BLENDFUNCTION); + static LPALPHABLEND lpAlphaBlend = (LPALPHABLEND) ::GetProcAddress(::GetModuleHandle(_T("msimg32.dll")), "AlphaBlend"); + if( lpAlphaBlend == NULL ) lpAlphaBlend = AlphaBitBlt; + typedef BOOL (WINAPI *PGradientFill)(HDC, PTRIVERTEX, ULONG, PVOID, ULONG, ULONG); + static PGradientFill lpGradientFill = (PGradientFill) ::GetProcAddress(::GetModuleHandle(_T("msimg32.dll")), "GradientFill"); + + BYTE bAlpha = (BYTE)(((dwFirst >> 24) + (dwSecond >> 24)) >> 1); + if( bAlpha == 0 ) return; + int cx = rc.right - rc.left; + int cy = rc.bottom - rc.top; + RECT rcPaint = rc; + HDC hPaintDC = hDC; + HBITMAP hPaintBitmap = NULL; + HBITMAP hOldPaintBitmap = NULL; + if( bAlpha < 255 ) { + rcPaint.left = rcPaint.top = 0; + rcPaint.right = cx; + rcPaint.bottom = cy; + hPaintDC = ::CreateCompatibleDC(hDC); + hPaintBitmap = ::CreateCompatibleBitmap(hDC, cx, cy); + ASSERT(hPaintDC); + ASSERT(hPaintBitmap); + hOldPaintBitmap = (HBITMAP) ::SelectObject(hPaintDC, hPaintBitmap); + } + if( lpGradientFill != NULL ) + { + TRIVERTEX triv[2] = + { + { rcPaint.left, rcPaint.top, GetBValue(dwFirst) << 8, GetGValue(dwFirst) << 8, GetRValue(dwFirst) << 8, 0xFF00 }, + { rcPaint.right, rcPaint.bottom, GetBValue(dwSecond) << 8, GetGValue(dwSecond) << 8, GetRValue(dwSecond) << 8, 0xFF00 } + }; + GRADIENT_RECT grc = { 0, 1 }; + lpGradientFill(hPaintDC, triv, 2, &grc, 1, bVertical ? GRADIENT_FILL_RECT_V : GRADIENT_FILL_RECT_H); + } + else + { + // Determine how many shades + int nShift = 1; + if( nSteps >= 64 ) nShift = 6; + else if( nSteps >= 32 ) nShift = 5; + else if( nSteps >= 16 ) nShift = 4; + else if( nSteps >= 8 ) nShift = 3; + else if( nSteps >= 4 ) nShift = 2; + int nLines = 1 << nShift; + for( int i = 0; i < nLines; i++ ) { + // Do a little alpha blending + BYTE bR = (BYTE) ((GetBValue(dwSecond) * (nLines - i) + GetBValue(dwFirst) * i) >> nShift); + BYTE bG = (BYTE) ((GetGValue(dwSecond) * (nLines - i) + GetGValue(dwFirst) * i) >> nShift); + BYTE bB = (BYTE) ((GetRValue(dwSecond) * (nLines - i) + GetRValue(dwFirst) * i) >> nShift); + // ... then paint with the resulting color + HBRUSH hBrush = ::CreateSolidBrush(RGB(bR,bG,bB)); + RECT r2 = rcPaint; + if( bVertical ) { + r2.bottom = rc.bottom - ((i * (rc.bottom - rc.top)) >> nShift); + r2.top = rc.bottom - (((i + 1) * (rc.bottom - rc.top)) >> nShift); + if( (r2.bottom - r2.top) > 0 ) ::FillRect(hDC, &r2, hBrush); + } + else { + r2.left = rc.right - (((i + 1) * (rc.right - rc.left)) >> nShift); + r2.right = rc.right - ((i * (rc.right - rc.left)) >> nShift); + if( (r2.right - r2.left) > 0 ) ::FillRect(hPaintDC, &r2, hBrush); + } + ::DeleteObject(hBrush); + } + } + if( bAlpha < 255 ) { + BLENDFUNCTION bf = { AC_SRC_OVER, 0, bAlpha, AC_SRC_ALPHA }; + lpAlphaBlend(hDC, rc.left, rc.top, cx, cy, hPaintDC, 0, 0, cx, cy, bf); + ::SelectObject(hPaintDC, hOldPaintBitmap); + ::DeleteObject(hPaintBitmap); + ::DeleteDC(hPaintDC); + } +} + +void CRenderEngine::DrawLine( HDC hDC, const RECT& rc, int nSize, DWORD dwPenColor,int nStyle /*= PS_SOLID*/ ) +{ + ASSERT(::GetObjectType(hDC)==OBJ_DC || ::GetObjectType(hDC)==OBJ_MEMDC); + + LOGPEN lg; + lg.lopnColor = RGB(GetBValue(dwPenColor), GetGValue(dwPenColor), GetRValue(dwPenColor)); + lg.lopnStyle = nStyle; + lg.lopnWidth.x = nSize; + HPEN hPen = CreatePenIndirect(&lg); + HPEN hOldPen = (HPEN)::SelectObject(hDC, hPen); + POINT ptTemp = { 0 }; + ::MoveToEx(hDC, rc.left, rc.top, &ptTemp); + ::LineTo(hDC, rc.right, rc.bottom); + ::SelectObject(hDC, hOldPen); + ::DeleteObject(hPen); +} + +void CRenderEngine::DrawRect(HDC hDC, const RECT& rc, int nSize, DWORD dwPenColor) +{ + ASSERT(::GetObjectType(hDC)==OBJ_DC || ::GetObjectType(hDC)==OBJ_MEMDC); + HPEN hPen = ::CreatePen(PS_SOLID | PS_INSIDEFRAME, nSize, RGB(GetBValue(dwPenColor), GetGValue(dwPenColor), GetRValue(dwPenColor))); + HPEN hOldPen = (HPEN)::SelectObject(hDC, hPen); + ::SelectObject(hDC, ::GetStockObject(HOLLOW_BRUSH)); + ::Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom); + ::SelectObject(hDC, hOldPen); + ::DeleteObject(hPen); +} + +void CRenderEngine::DrawRoundRect(HDC hDC, const RECT& rc, int nSize, int width, int height, DWORD dwPenColor) +{ + ASSERT(::GetObjectType(hDC)==OBJ_DC || ::GetObjectType(hDC)==OBJ_MEMDC); + HPEN hPen = ::CreatePen(PS_SOLID | PS_INSIDEFRAME, nSize, RGB(GetBValue(dwPenColor), GetGValue(dwPenColor), GetRValue(dwPenColor))); + HPEN hOldPen = (HPEN)::SelectObject(hDC, hPen); + ::SelectObject(hDC, ::GetStockObject(HOLLOW_BRUSH)); + ::RoundRect(hDC, rc.left, rc.top, rc.right, rc.bottom, width, height); + ::SelectObject(hDC, hOldPen); + ::DeleteObject(hPen); +} + +void CRenderEngine::DrawText(HDC hDC, CPaintManagerUI* pManager, RECT& rc, LPCTSTR pstrText, DWORD dwTextColor, int iFont, UINT uStyle) +{ + ASSERT(::GetObjectType(hDC)==OBJ_DC || ::GetObjectType(hDC)==OBJ_MEMDC); + if( pstrText == NULL || pManager == NULL ) return; + + if ( pManager->IsBackgroundTransparent() || pManager->IsUseGdiplusText()) + { + Gdiplus::Graphics graphics( hDC ); + graphics.SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAlias); + + Gdiplus::Font font(hDC, pManager->GetFont(iFont)); + Gdiplus::RectF rectF((Gdiplus::REAL)rc.left, (Gdiplus::REAL)rc.top, (Gdiplus::REAL)(rc.right - rc.left), (Gdiplus::REAL)(rc.bottom - rc.top)); + Gdiplus::SolidBrush brush(Gdiplus::Color(254, GetBValue(dwTextColor), GetGValue(dwTextColor), GetRValue(dwTextColor))); + + Gdiplus::StringFormat stringFormat = Gdiplus::StringFormat::GenericTypographic(); + + if ((uStyle & DT_END_ELLIPSIS) != 0) { + stringFormat.SetTrimming(Gdiplus::StringTrimmingEllipsisCharacter); + } + + int formatFlags = 0; + if ((uStyle & DT_NOCLIP) != 0) { + formatFlags |= Gdiplus::StringFormatFlagsNoClip; + } + if ((uStyle & DT_SINGLELINE) != 0) { + formatFlags |= Gdiplus::StringFormatFlagsNoWrap; + } + + stringFormat.SetFormatFlags(formatFlags); + + if ((uStyle & DT_LEFT) != 0) { + stringFormat.SetAlignment(Gdiplus::StringAlignmentNear); + } + else if ((uStyle & DT_CENTER) != 0) { + stringFormat.SetAlignment(Gdiplus::StringAlignmentCenter); + } + else if ((uStyle & DT_RIGHT) != 0) { + stringFormat.SetAlignment(Gdiplus::StringAlignmentFar); + } + else { + stringFormat.SetAlignment(Gdiplus::StringAlignmentNear); + } + stringFormat.GenericTypographic(); + if ((uStyle & DT_TOP) != 0) { + stringFormat.SetLineAlignment(Gdiplus::StringAlignmentNear); + } + else if ((uStyle & DT_VCENTER) != 0) { + stringFormat.SetLineAlignment(Gdiplus::StringAlignmentCenter); + } + else if ((uStyle & DT_BOTTOM) != 0) { + stringFormat.SetLineAlignment(Gdiplus::StringAlignmentFar); + } + else { + stringFormat.SetLineAlignment(Gdiplus::StringAlignmentNear); + } + + if ((uStyle & DT_CALCRECT) != 0) + { + Gdiplus::RectF bounds; + graphics.MeasureString(pstrText, -1, &font, rectF, &stringFormat, &bounds); + + // MeasureStringڼһ + rc.bottom = rc.top + (long)bounds.Height + 1; + rc.right = rc.left + (long)bounds.Width + 1; + } + else + { + graphics.DrawString(pstrText, -1, &font, rectF, &stringFormat, &brush); + } + + } + else + { + ::SetBkMode(hDC, TRANSPARENT); + ::SetTextColor(hDC, RGB(GetBValue(dwTextColor), GetGValue(dwTextColor), GetRValue(dwTextColor))); + HFONT hOldFont = (HFONT)::SelectObject(hDC, pManager->GetFont(iFont)); + ::DrawText(hDC, pstrText, -1, &rc, uStyle | DT_NOPREFIX); + ::SelectObject(hDC, hOldFont); + + } + +} + +void CRenderEngine::DrawHtmlText(HDC hDC, CPaintManagerUI* pManager, RECT& rc, LPCTSTR pstrText, DWORD dwTextColor, RECT* prcLinks, CDuiString* sLinks, int& nLinkRects, UINT uStyle) +{ + // ǵxml༭ʹ<>Ų㣬ʹ{}Ŵ + // ֱ֧ǩǶףtextǽǶӦñģtext + // The string formatter supports a kind of "mini-html" that consists of various short tags: + // + // Bold: text + // Color: text where x = RGB in hex + // Font: text where x = font id + // Italic: text + // Image: where x = image name and y = imagelist num and z(optional) = imagelist id + // Link: text where x(optional) = link content, normal like app:notepad or http:www.xxx.com + // NewLine + // Paragraph:

text

where x = extra pixels indent in p + // Raw Text: text + // Selected: text + // Underline: text + // X Indent: where i = hor indent in pixels + // Y Indent: where i = ver indent in pixels + + ASSERT(::GetObjectType(hDC)==OBJ_DC || ::GetObjectType(hDC)==OBJ_MEMDC); + if( pstrText == NULL || pManager == NULL ) return; + if( ::IsRectEmpty(&rc) ) return; + + bool bDraw = (uStyle & DT_CALCRECT) == 0; + + CStdPtrArray aFontArray(10); + CStdPtrArray aColorArray(10); + CStdPtrArray aPIndentArray(10); + + RECT rcClip = { 0 }; + ::GetClipBox(hDC, &rcClip); + HRGN hOldRgn = ::CreateRectRgnIndirect(&rcClip); + HRGN hRgn = ::CreateRectRgnIndirect(&rc); + if( bDraw ) ::ExtSelectClipRgn(hDC, hRgn, RGN_AND); + + TEXTMETRIC* pTm = &pManager->GetDefaultFontInfo()->tm; + HFONT hOldFont = (HFONT) ::SelectObject(hDC, pManager->GetDefaultFontInfo()->hFont); + ::SetBkMode(hDC, TRANSPARENT); + ::SetTextColor(hDC, RGB(GetBValue(dwTextColor), GetGValue(dwTextColor), GetRValue(dwTextColor))); + DWORD dwBkColor = pManager->GetDefaultSelectedBkColor(); + ::SetBkColor(hDC, RGB(GetBValue(dwBkColor), GetGValue(dwBkColor), GetRValue(dwBkColor))); + + // If the drawstyle include a alignment, we'll need to first determine the text-size so + // we can draw it at the correct position... + if( ((uStyle & DT_CENTER) != 0 || (uStyle & DT_RIGHT) != 0 || (uStyle & DT_VCENTER) != 0 || (uStyle & DT_BOTTOM) != 0) && (uStyle & DT_CALCRECT) == 0 ) { + RECT rcText = { 0, 0, 9999, 100 }; + int nLinks = 0; + DrawHtmlText(hDC, pManager, rcText, pstrText, dwTextColor, NULL, NULL, nLinks, uStyle | DT_CALCRECT); + if( (uStyle & DT_SINGLELINE) != 0 ){ + if( (uStyle & DT_CENTER) != 0 ) { + rc.left = rc.left + ((rc.right - rc.left) / 2) - ((rcText.right - rcText.left) / 2); + rc.right = rc.left + (rcText.right - rcText.left); + } + if( (uStyle & DT_RIGHT) != 0 ) { + rc.left = rc.right - (rcText.right - rcText.left); + } + } + if( (uStyle & DT_VCENTER) != 0 ) { + rc.top = rc.top + ((rc.bottom - rc.top) / 2) - ((rcText.bottom - rcText.top) / 2); + rc.bottom = rc.top + (rcText.bottom - rcText.top); + } + if( (uStyle & DT_BOTTOM) != 0 ) { + rc.top = rc.bottom - (rcText.bottom - rcText.top); + } + } + + bool bHoverLink = false; + CDuiString sHoverLink; + POINT ptMouse = pManager->GetMousePos(); + for( int i = 0; !bHoverLink && i < nLinkRects; i++ ) { + if( ::PtInRect(prcLinks + i, ptMouse) ) { + sHoverLink = *(CDuiString*)(sLinks + i); + bHoverLink = true; + } + } + + POINT pt = { rc.left, rc.top }; + int iLinkIndex = 0; + int cyLine = pTm->tmHeight + pTm->tmExternalLeading + (int)aPIndentArray.GetAt(aPIndentArray.GetSize() - 1); + int cyMinHeight = 0; + int cxMaxWidth = 0; + POINT ptLinkStart = { 0 }; + bool bLineEnd = false; + bool bInRaw = false; + bool bInLink = false; + bool bInSelected = false; + int iLineLinkIndex = 0; + + // Űϰͼĵײ룬ÿлƶҪȼ߶ȣٻ + CStdPtrArray aLineFontArray; + CStdPtrArray aLineColorArray; + CStdPtrArray aLinePIndentArray; + LPCTSTR pstrLineBegin = pstrText; + bool bLineInRaw = false; + bool bLineInLink = false; + bool bLineInSelected = false; + int cyLineHeight = 0; + bool bLineDraw = false; // еĵڶ׶Σ + while( *pstrText != _T('\0') ) { + if( pt.x >= rc.right || *pstrText == _T('\n') || bLineEnd ) { + if( *pstrText == _T('\n') ) pstrText++; + if( bLineEnd ) bLineEnd = false; + if( !bLineDraw ) { + if( bInLink && iLinkIndex < nLinkRects ) { + ::SetRect(&prcLinks[iLinkIndex++], ptLinkStart.x, ptLinkStart.y, MIN(pt.x, rc.right), pt.y + cyLine); + CDuiString *pStr1 = (CDuiString*)(sLinks + iLinkIndex - 1); + CDuiString *pStr2 = (CDuiString*)(sLinks + iLinkIndex); + *pStr2 = *pStr1; + } + for( int i = iLineLinkIndex; i < iLinkIndex; i++ ) { + prcLinks[i].bottom = pt.y + cyLine; + } + if( bDraw ) { + bInLink = bLineInLink; + iLinkIndex = iLineLinkIndex; + } + } + else { + if( bInLink && iLinkIndex < nLinkRects ) iLinkIndex++; + bLineInLink = bInLink; + iLineLinkIndex = iLinkIndex; + } + if( (uStyle & DT_SINGLELINE) != 0 && (!bDraw || bLineDraw) ) break; + if( bDraw ) bLineDraw = !bLineDraw; // ! + pt.x = rc.left; + if( !bLineDraw ) pt.y += cyLine; + if( pt.y > rc.bottom && bDraw ) break; + ptLinkStart = pt; + cyLine = pTm->tmHeight + pTm->tmExternalLeading + (int)aPIndentArray.GetAt(aPIndentArray.GetSize() - 1); + if( pt.x >= rc.right ) break; + } + else if( !bInRaw && ( *pstrText == _T('<') || *pstrText == _T('{') ) + && ( pstrText[1] >= _T('a') && pstrText[1] <= _T('z') ) + && ( pstrText[2] == _T(' ') || pstrText[2] == _T('>') || pstrText[2] == _T('}') ) ) { + pstrText++; + LPCTSTR pstrNextStart = NULL; + switch( *pstrText ) { + case _T('a'): // Link + { + pstrText++; + while( *pstrText > _T('\0') && *pstrText <= _T(' ') ) pstrText = ::CharNext(pstrText); + if( iLinkIndex < nLinkRects && !bLineDraw ) { + CDuiString *pStr = (CDuiString*)(sLinks + iLinkIndex); + pStr->Empty(); + while( *pstrText != _T('\0') && *pstrText != _T('>') && *pstrText != _T('}') ) { + LPCTSTR pstrTemp = ::CharNext(pstrText); + while( pstrText < pstrTemp) { + *pStr += *pstrText++; + } + } + } + + DWORD clrColor = pManager->GetDefaultLinkFontColor(); + if( bHoverLink && iLinkIndex < nLinkRects ) { + CDuiString *pStr = (CDuiString*)(sLinks + iLinkIndex); + if( sHoverLink == *pStr ) clrColor = pManager->GetDefaultLinkHoverFontColor(); + } + //else if( prcLinks == NULL ) { + // if( ::PtInRect(&rc, ptMouse) ) + // clrColor = pManager->GetDefaultLinkHoverFontColor(); + //} + aColorArray.Add((LPVOID)clrColor); + ::SetTextColor(hDC, RGB(GetBValue(clrColor), GetGValue(clrColor), GetRValue(clrColor))); + TFontInfo* pFontInfo = pManager->GetDefaultFontInfo(); + if( aFontArray.GetSize() > 0 ) pFontInfo = (TFontInfo*)aFontArray.GetAt(aFontArray.GetSize() - 1); + if( pFontInfo->bUnderline == false ) { + HFONT hFont = pManager->GetFont(pFontInfo->sFontName, pFontInfo->iSize, pFontInfo->bBold, true, pFontInfo->bItalic); + if( hFont == NULL ) hFont = pManager->AddFont(pFontInfo->sFontName, pFontInfo->iSize, pFontInfo->bBold, true, pFontInfo->bItalic); + pFontInfo = pManager->GetFontInfo(hFont); + aFontArray.Add(pFontInfo); + pTm = &pFontInfo->tm; + ::SelectObject(hDC, pFontInfo->hFont); + cyLine = MAX(cyLine, pTm->tmHeight + pTm->tmExternalLeading + (int)aPIndentArray.GetAt(aPIndentArray.GetSize() - 1)); + } + ptLinkStart = pt; + bInLink = true; + } + break; + case _T('b'): // Bold + { + pstrText++; + TFontInfo* pFontInfo = pManager->GetDefaultFontInfo(); + if( aFontArray.GetSize() > 0 ) pFontInfo = (TFontInfo*)aFontArray.GetAt(aFontArray.GetSize() - 1); + if( pFontInfo->bBold == false ) { + HFONT hFont = pManager->GetFont(pFontInfo->sFontName, pFontInfo->iSize, true, pFontInfo->bUnderline, pFontInfo->bItalic); + if( hFont == NULL ) hFont = pManager->AddFont(pFontInfo->sFontName, pFontInfo->iSize, true, pFontInfo->bUnderline, pFontInfo->bItalic); + pFontInfo = pManager->GetFontInfo(hFont); + aFontArray.Add(pFontInfo); + pTm = &pFontInfo->tm; + ::SelectObject(hDC, pFontInfo->hFont); + cyLine = MAX(cyLine, pTm->tmHeight + pTm->tmExternalLeading + (int)aPIndentArray.GetAt(aPIndentArray.GetSize() - 1)); + } + } + break; + case _T('c'): // Color + { + pstrText++; + while( *pstrText > _T('\0') && *pstrText <= _T(' ') ) pstrText = ::CharNext(pstrText); + if( *pstrText == _T('#')) pstrText++; + DWORD clrColor = _tcstol(pstrText, const_cast(&pstrText), 16); + aColorArray.Add((LPVOID)clrColor); + ::SetTextColor(hDC, RGB(GetBValue(clrColor), GetGValue(clrColor), GetRValue(clrColor))); + } + break; + case _T('f'): // Font + { + pstrText++; + while( *pstrText > _T('\0') && *pstrText <= _T(' ') ) pstrText = ::CharNext(pstrText); + LPCTSTR pstrTemp = pstrText; + int iFont = (int) _tcstol(pstrText, const_cast(&pstrText), 10); + //if( isdigit(*pstrText) ) { // debug汾쳣 + if( pstrTemp != pstrText ) { + TFontInfo* pFontInfo = pManager->GetFontInfo(iFont); + aFontArray.Add(pFontInfo); + pTm = &pFontInfo->tm; + ::SelectObject(hDC, pFontInfo->hFont); + } + else { + CDuiString sFontName; + int iFontSize = 10; + CDuiString sFontAttr; + bool bBold = false; + bool bUnderline = false; + bool bItalic = false; + while( *pstrText != _T('\0') && *pstrText != _T('>') && *pstrText != _T('}') && *pstrText != _T(' ') ) { + pstrTemp = ::CharNext(pstrText); + while( pstrText < pstrTemp) { + sFontName += *pstrText++; + } + } + while( *pstrText > _T('\0') && *pstrText <= _T(' ') ) pstrText = ::CharNext(pstrText); + if( isdigit(*pstrText) ) { + iFontSize = (int) _tcstol(pstrText, const_cast(&pstrText), 10); + } + while( *pstrText > _T('\0') && *pstrText <= _T(' ') ) pstrText = ::CharNext(pstrText); + while( *pstrText != _T('\0') && *pstrText != _T('>') && *pstrText != _T('}') ) { + pstrTemp = ::CharNext(pstrText); + while( pstrText < pstrTemp) { + sFontAttr += *pstrText++; + } + } + sFontAttr.MakeLower(); + if( sFontAttr.Find(_T("bold")) >= 0 ) bBold = true; + if( sFontAttr.Find(_T("underline")) >= 0 ) bUnderline = true; + if( sFontAttr.Find(_T("italic")) >= 0 ) bItalic = true; + HFONT hFont = pManager->GetFont(sFontName, iFontSize, bBold, bUnderline, bItalic); + if( hFont == NULL ) hFont = pManager->AddFont(sFontName, iFontSize, bBold, bUnderline, bItalic); + TFontInfo* pFontInfo = pManager->GetFontInfo(hFont); + aFontArray.Add(pFontInfo); + pTm = &pFontInfo->tm; + ::SelectObject(hDC, pFontInfo->hFont); + } + cyLine = MAX(cyLine, pTm->tmHeight + pTm->tmExternalLeading + (int)aPIndentArray.GetAt(aPIndentArray.GetSize() - 1)); + } + break; + case _T('i'): // Italic or Image + { + pstrNextStart = pstrText - 1; + pstrText++; + CDuiString sImageString = pstrText; + int iWidth = 0; + int iHeight = 0; + while( *pstrText > _T('\0') && *pstrText <= _T(' ') ) pstrText = ::CharNext(pstrText); + const TImageInfo* pImageInfo = NULL; + CDuiString sName; + while( *pstrText != _T('\0') && *pstrText != _T('>') && *pstrText != _T('}') && *pstrText != _T(' ') ) { + LPCTSTR pstrTemp = ::CharNext(pstrText); + while( pstrText < pstrTemp) { + sName += *pstrText++; + } + } + if( sName.IsEmpty() ) { // Italic + pstrNextStart = NULL; + TFontInfo* pFontInfo = pManager->GetDefaultFontInfo(); + if( aFontArray.GetSize() > 0 ) pFontInfo = (TFontInfo*)aFontArray.GetAt(aFontArray.GetSize() - 1); + if( pFontInfo->bItalic == false ) { + HFONT hFont = pManager->GetFont(pFontInfo->sFontName, pFontInfo->iSize, pFontInfo->bBold, pFontInfo->bUnderline, true); + if( hFont == NULL ) hFont = pManager->AddFont(pFontInfo->sFontName, pFontInfo->iSize, pFontInfo->bBold, pFontInfo->bUnderline, true); + pFontInfo = pManager->GetFontInfo(hFont); + aFontArray.Add(pFontInfo); + pTm = &pFontInfo->tm; + ::SelectObject(hDC, pFontInfo->hFont); + cyLine = MAX(cyLine, pTm->tmHeight + pTm->tmExternalLeading + (int)aPIndentArray.GetAt(aPIndentArray.GetSize() - 1)); + } + } + else { + while( *pstrText > _T('\0') && *pstrText <= _T(' ') ) pstrText = ::CharNext(pstrText); + int iImageListNum = (int) _tcstol(pstrText, const_cast(&pstrText), 10); + if( iImageListNum <= 0 ) iImageListNum = 1; + while( *pstrText > _T('\0') && *pstrText <= _T(' ') ) pstrText = ::CharNext(pstrText); + int iImageListIndex = (int) _tcstol(pstrText, const_cast(&pstrText), 10); + if( iImageListIndex < 0 || iImageListIndex >= iImageListNum ) iImageListIndex = 0; + + if( _tcsstr(sImageString.GetData(), _T("file=\'")) != NULL || _tcsstr(sImageString.GetData(), _T("res=\'")) != NULL ) { + CDuiString sImageResType; + CDuiString sImageName; + LPCTSTR pStrImage = sImageString.GetData(); + CDuiString sItem; + CDuiString sValue; + while( *pStrImage != _T('\0') ) { + sItem.Empty(); + sValue.Empty(); + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + while( *pStrImage != _T('\0') && *pStrImage != _T('=') && *pStrImage > _T(' ') ) { + LPTSTR pstrTemp = ::CharNext(pStrImage); + while( pStrImage < pstrTemp) { + sItem += *pStrImage++; + } + } + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + if( *pStrImage++ != _T('=') ) break; + while( *pStrImage > _T('\0') && *pStrImage <= _T(' ') ) pStrImage = ::CharNext(pStrImage); + if( *pStrImage++ != _T('\'') ) break; + while( *pStrImage != _T('\0') && *pStrImage != _T('\'') ) { + LPTSTR pstrTemp = ::CharNext(pStrImage); + while( pStrImage < pstrTemp) { + sValue += *pStrImage++; + } + } + if( *pStrImage++ != _T('\'') ) break; + if( !sValue.IsEmpty() ) { + if( sItem == _T("file") || sItem == _T("res") ) { + sImageName = sValue; + } + else if( sItem == _T("restype") ) { + sImageResType = sValue; + } + } + if( *pStrImage++ != _T(' ') ) break; + } + + pImageInfo = pManager->GetImageEx((LPCTSTR)sImageName, sImageResType); + } + else + pImageInfo = pManager->GetImageEx((LPCTSTR)sName); + + if( pImageInfo ) { + iWidth = pImageInfo->nX; + iHeight = pImageInfo->nY; + if( iImageListNum > 1 ) iWidth /= iImageListNum; + + if( pt.x + iWidth > rc.right && pt.x > rc.left && (uStyle & DT_SINGLELINE) == 0 ) { + bLineEnd = true; + } + else { + pstrNextStart = NULL; + if( bDraw && bLineDraw ) { + CDuiRect rcImage(pt.x, pt.y + cyLineHeight - iHeight, pt.x + iWidth, pt.y + cyLineHeight); + if( iHeight < cyLineHeight ) { + rcImage.bottom -= (cyLineHeight - iHeight) / 2; + rcImage.top = rcImage.bottom - iHeight; + } + CDuiRect rcBmpPart(0, 0, iWidth, iHeight); + rcBmpPart.left = iWidth * iImageListIndex; + rcBmpPart.right = iWidth * (iImageListIndex + 1); + CDuiRect rcCorner(0, 0, 0, 0); + DrawImage(hDC, pImageInfo->hBitmap, rcImage, rcImage, rcBmpPart, rcCorner, \ + pImageInfo->alphaChannel, 255); + } + + cyLine = MAX(iHeight, cyLine); + pt.x += iWidth; + cyMinHeight = pt.y + iHeight; + cxMaxWidth = MAX(cxMaxWidth, pt.x); + } + } + else pstrNextStart = NULL; + } + } + break; + case _T('n'): // Newline + { + pstrText++; + if( (uStyle & DT_SINGLELINE) != 0 ) break; + bLineEnd = true; + } + break; + case _T('p'): // Paragraph + { + pstrText++; + if( pt.x > rc.left ) bLineEnd = true; + while( *pstrText > _T('\0') && *pstrText <= _T(' ') ) pstrText = ::CharNext(pstrText); + int cyLineExtra = (int)_tcstol(pstrText, const_cast(&pstrText), 10); + aPIndentArray.Add((LPVOID)cyLineExtra); + cyLine = MAX(cyLine, pTm->tmHeight + pTm->tmExternalLeading + cyLineExtra); + } + break; + case _T('r'): // Raw Text + { + pstrText++; + bInRaw = true; + } + break; + case _T('s'): // Selected text background color + { + pstrText++; + bInSelected = !bInSelected; + if( bDraw && bLineDraw ) { + if( bInSelected ) ::SetBkMode(hDC, OPAQUE); + else ::SetBkMode(hDC, TRANSPARENT); + } + } + break; + case _T('u'): // Underline text + { + pstrText++; + TFontInfo* pFontInfo = pManager->GetDefaultFontInfo(); + if( aFontArray.GetSize() > 0 ) pFontInfo = (TFontInfo*)aFontArray.GetAt(aFontArray.GetSize() - 1); + if( pFontInfo->bUnderline == false ) { + HFONT hFont = pManager->GetFont(pFontInfo->sFontName, pFontInfo->iSize, pFontInfo->bBold, true, pFontInfo->bItalic); + if( hFont == NULL ) hFont = pManager->AddFont(pFontInfo->sFontName, pFontInfo->iSize, pFontInfo->bBold, true, pFontInfo->bItalic); + pFontInfo = pManager->GetFontInfo(hFont); + aFontArray.Add(pFontInfo); + pTm = &pFontInfo->tm; + ::SelectObject(hDC, pFontInfo->hFont); + cyLine = MAX(cyLine, pTm->tmHeight + pTm->tmExternalLeading + (int)aPIndentArray.GetAt(aPIndentArray.GetSize() - 1)); + } + } + break; + case _T('x'): // X Indent + { + pstrText++; + while( *pstrText > _T('\0') && *pstrText <= _T(' ') ) pstrText = ::CharNext(pstrText); + int iWidth = (int) _tcstol(pstrText, const_cast(&pstrText), 10); + pt.x += iWidth; + cxMaxWidth = MAX(cxMaxWidth, pt.x); + } + break; + case _T('y'): // Y Indent + { + pstrText++; + while( *pstrText > _T('\0') && *pstrText <= _T(' ') ) pstrText = ::CharNext(pstrText); + cyLine = (int) _tcstol(pstrText, const_cast(&pstrText), 10); + } + break; + } + if( pstrNextStart != NULL ) pstrText = pstrNextStart; + else { + while( *pstrText != _T('\0') && *pstrText != _T('>') && *pstrText != _T('}') ) pstrText = ::CharNext(pstrText); + pstrText = ::CharNext(pstrText); + } + } + else if( !bInRaw && ( *pstrText == _T('<') || *pstrText == _T('{') ) && pstrText[1] == _T('/') ) + { + pstrText++; + pstrText++; + switch( *pstrText ) + { + case _T('c'): + { + pstrText++; + aColorArray.Remove(aColorArray.GetSize() - 1); + DWORD clrColor = dwTextColor; + if( aColorArray.GetSize() > 0 ) clrColor = (int)aColorArray.GetAt(aColorArray.GetSize() - 1); + ::SetTextColor(hDC, RGB(GetBValue(clrColor), GetGValue(clrColor), GetRValue(clrColor))); + } + break; + case _T('p'): + pstrText++; + if( pt.x > rc.left ) bLineEnd = true; + aPIndentArray.Remove(aPIndentArray.GetSize() - 1); + cyLine = MAX(cyLine, pTm->tmHeight + pTm->tmExternalLeading + (int)aPIndentArray.GetAt(aPIndentArray.GetSize() - 1)); + break; + case _T('s'): + { + pstrText++; + bInSelected = !bInSelected; + if( bDraw && bLineDraw ) { + if( bInSelected ) ::SetBkMode(hDC, OPAQUE); + else ::SetBkMode(hDC, TRANSPARENT); + } + } + break; + case _T('a'): + { + if( iLinkIndex < nLinkRects ) { + if( !bLineDraw ) ::SetRect(&prcLinks[iLinkIndex], ptLinkStart.x, ptLinkStart.y, MIN(pt.x, rc.right), pt.y + pTm->tmHeight + pTm->tmExternalLeading); + iLinkIndex++; + } + aColorArray.Remove(aColorArray.GetSize() - 1); + DWORD clrColor = dwTextColor; + if( aColorArray.GetSize() > 0 ) clrColor = (int)aColorArray.GetAt(aColorArray.GetSize() - 1); + ::SetTextColor(hDC, RGB(GetBValue(clrColor), GetGValue(clrColor), GetRValue(clrColor))); + bInLink = false; + } + case _T('b'): + case _T('f'): + case _T('i'): + case _T('u'): + { + pstrText++; + aFontArray.Remove(aFontArray.GetSize() - 1); + TFontInfo* pFontInfo = (TFontInfo*)aFontArray.GetAt(aFontArray.GetSize() - 1); + if( pFontInfo == NULL ) pFontInfo = pManager->GetDefaultFontInfo(); + if( pTm->tmItalic && pFontInfo->bItalic == false ) { + ABC abc; + ::GetCharABCWidths(hDC, _T(' '), _T(' '), &abc); + pt.x += abc.abcC / 2; // һбŵ, ȷӦhttp://support.microsoft.com/kb/244798/en-us + } + pTm = &pFontInfo->tm; + ::SelectObject(hDC, pFontInfo->hFont); + cyLine = MAX(cyLine, pTm->tmHeight + pTm->tmExternalLeading + (int)aPIndentArray.GetAt(aPIndentArray.GetSize() - 1)); + } + break; + } + while( *pstrText != _T('\0') && *pstrText != _T('>') && *pstrText != _T('}') ) pstrText = ::CharNext(pstrText); + pstrText = ::CharNext(pstrText); + } + else if( !bInRaw && *pstrText == _T('<') && pstrText[2] == _T('>') && (pstrText[1] == _T('{') || pstrText[1] == _T('}')) ) + { + SIZE szSpace = { 0 }; + ::GetTextExtentPoint32(hDC, &pstrText[1], 1, &szSpace); + if( bDraw && bLineDraw ) ::TextOut(hDC, pt.x, pt.y + cyLineHeight - pTm->tmHeight - pTm->tmExternalLeading, &pstrText[1], 1); + pt.x += szSpace.cx; + cxMaxWidth = MAX(cxMaxWidth, pt.x); + pstrText++;pstrText++;pstrText++; + } + else if( !bInRaw && *pstrText == _T('{') && pstrText[2] == _T('}') && (pstrText[1] == _T('<') || pstrText[1] == _T('>')) ) + { + SIZE szSpace = { 0 }; + ::GetTextExtentPoint32(hDC, &pstrText[1], 1, &szSpace); + if( bDraw && bLineDraw ) ::TextOut(hDC, pt.x, pt.y + cyLineHeight - pTm->tmHeight - pTm->tmExternalLeading, &pstrText[1], 1); + pt.x += szSpace.cx; + cxMaxWidth = MAX(cxMaxWidth, pt.x); + pstrText++;pstrText++;pstrText++; + } + else if( !bInRaw && *pstrText == _T(' ') ) + { + SIZE szSpace = { 0 }; + ::GetTextExtentPoint32(hDC, _T(" "), 1, &szSpace); + // Still need to paint the space because the font might have + // underline formatting. + if( bDraw && bLineDraw ) ::TextOut(hDC, pt.x, pt.y + cyLineHeight - pTm->tmHeight - pTm->tmExternalLeading, _T(" "), 1); + pt.x += szSpace.cx; + cxMaxWidth = MAX(cxMaxWidth, pt.x); + pstrText++; + } + else + { + POINT ptPos = pt; + int cchChars = 0; + int cchSize = 0; + int cchLastGoodWord = 0; + int cchLastGoodSize = 0; + LPCTSTR p = pstrText; + LPCTSTR pstrNext; + SIZE szText = { 0 }; + if( !bInRaw && *p == _T('<') || *p == _T('{') ) p++, cchChars++, cchSize++; + while( *p != _T('\0') && *p != _T('\n') ) { + // This part makes sure that we're word-wrapping if needed or providing support + // for DT_END_ELLIPSIS. Unfortunately the GetTextExtentPoint32() call is pretty + // slow when repeated so often. + // TODO: Rewrite and use GetTextExtentExPoint() instead! + if( bInRaw ) { + if( ( *p == _T('<') || *p == _T('{') ) && p[1] == _T('/') + && p[2] == _T('r') && ( p[3] == _T('>') || p[3] == _T('}') ) ) { + p += 4; + bInRaw = false; + break; + } + } + else { + if( *p == _T('<') || *p == _T('{') ) break; + } + pstrNext = ::CharNext(p); + cchChars++; + cchSize += (int)(pstrNext - p); + szText.cx = cchChars * pTm->tmMaxCharWidth; + if( pt.x + szText.cx >= rc.right ) { + ::GetTextExtentPoint32(hDC, pstrText, cchSize, &szText); + } + if( pt.x + szText.cx > rc.right ) { + if( pt.x + szText.cx > rc.right && pt.x != rc.left) { + cchChars--; + cchSize -= (int)(pstrNext - p); + } + if( (uStyle & DT_WORDBREAK) != 0 && cchLastGoodWord > 0 ) { + cchChars = cchLastGoodWord; + cchSize = cchLastGoodSize; + } + if( (uStyle & DT_END_ELLIPSIS) != 0 && cchChars > 0 ) { + cchChars -= 1; + LPCTSTR pstrPrev = ::CharPrev(pstrText, p); + if( cchChars > 0 ) { + cchChars -= 1; + pstrPrev = ::CharPrev(pstrText, pstrPrev); + cchSize -= (int)(p - pstrPrev); + } + else + cchSize -= (int)(p - pstrPrev); + pt.x = rc.right; + } + bLineEnd = true; + cxMaxWidth = MAX(cxMaxWidth, pt.x); + break; + } + if (!( ( p[0] >= _T('a') && p[0] <= _T('z') ) || ( p[0] >= _T('A') && p[0] <= _T('Z') ) )) { + cchLastGoodWord = cchChars; + cchLastGoodSize = cchSize; + } + if( *p == _T(' ') ) { + cchLastGoodWord = cchChars; + cchLastGoodSize = cchSize; + } + p = ::CharNext(p); + } + + ::GetTextExtentPoint32(hDC, pstrText, cchSize, &szText); + if( bDraw && bLineDraw ) { + if( (uStyle & DT_SINGLELINE) == 0 && (uStyle & DT_CENTER) != 0 ) { + ptPos.x += (rc.right - rc.left - szText.cx)/2; + } + else if( (uStyle & DT_SINGLELINE) == 0 && (uStyle & DT_RIGHT) != 0) { + ptPos.x += (rc.right - rc.left - szText.cx); + } + ::TextOut(hDC, ptPos.x, ptPos.y + cyLineHeight - pTm->tmHeight - pTm->tmExternalLeading, pstrText, cchSize); + if( pt.x >= rc.right && (uStyle & DT_END_ELLIPSIS) != 0 ) + ::TextOut(hDC, ptPos.x + szText.cx, ptPos.y, _T("..."), 3); + } + pt.x += szText.cx; + cxMaxWidth = MAX(cxMaxWidth, pt.x); + pstrText += cchSize; + } + + if( pt.x >= rc.right || *pstrText == _T('\n') || *pstrText == _T('\0') ) bLineEnd = true; + if( bDraw && bLineEnd ) { + if( !bLineDraw ) { + aFontArray.Resize(aLineFontArray.GetSize()); + ::CopyMemory(aFontArray.GetData(), aLineFontArray.GetData(), aLineFontArray.GetSize() * sizeof(LPVOID)); + aColorArray.Resize(aLineColorArray.GetSize()); + ::CopyMemory(aColorArray.GetData(), aLineColorArray.GetData(), aLineColorArray.GetSize() * sizeof(LPVOID)); + aPIndentArray.Resize(aLinePIndentArray.GetSize()); + ::CopyMemory(aPIndentArray.GetData(), aLinePIndentArray.GetData(), aLinePIndentArray.GetSize() * sizeof(LPVOID)); + + cyLineHeight = cyLine; + pstrText = pstrLineBegin; + bInRaw = bLineInRaw; + bInSelected = bLineInSelected; + + DWORD clrColor = dwTextColor; + if( aColorArray.GetSize() > 0 ) clrColor = (int)aColorArray.GetAt(aColorArray.GetSize() - 1); + ::SetTextColor(hDC, RGB(GetBValue(clrColor), GetGValue(clrColor), GetRValue(clrColor))); + TFontInfo* pFontInfo = (TFontInfo*)aFontArray.GetAt(aFontArray.GetSize() - 1); + if( pFontInfo == NULL ) pFontInfo = pManager->GetDefaultFontInfo(); + pTm = &pFontInfo->tm; + ::SelectObject(hDC, pFontInfo->hFont); + if( bInSelected ) ::SetBkMode(hDC, OPAQUE); + } + else { + aLineFontArray.Resize(aFontArray.GetSize()); + ::CopyMemory(aLineFontArray.GetData(), aFontArray.GetData(), aFontArray.GetSize() * sizeof(LPVOID)); + aLineColorArray.Resize(aColorArray.GetSize()); + ::CopyMemory(aLineColorArray.GetData(), aColorArray.GetData(), aColorArray.GetSize() * sizeof(LPVOID)); + aLinePIndentArray.Resize(aPIndentArray.GetSize()); + ::CopyMemory(aLinePIndentArray.GetData(), aPIndentArray.GetData(), aPIndentArray.GetSize() * sizeof(LPVOID)); + pstrLineBegin = pstrText; + bLineInSelected = bInSelected; + bLineInRaw = bInRaw; + } + } + + ASSERT(iLinkIndex<=nLinkRects); + } + + nLinkRects = iLinkIndex; + + // Return size of text when requested + if( (uStyle & DT_CALCRECT) != 0 ) { + rc.bottom = MAX(cyMinHeight, pt.y + cyLine); + rc.right = MIN(rc.right, cxMaxWidth); + } + + if( bDraw ) ::SelectClipRgn(hDC, hOldRgn); + ::DeleteObject(hOldRgn); + ::DeleteObject(hRgn); + + ::SelectObject(hDC, hOldFont); +} + +HBITMAP CRenderEngine::GenerateBitmap(CPaintManagerUI* pManager, CControlUI* pControl, RECT rc) +{ + int cx = rc.right - rc.left; + int cy = rc.bottom - rc.top; + + HDC hPaintDC = ::CreateCompatibleDC(pManager->GetPaintDC()); + HBITMAP hPaintBitmap = ::CreateCompatibleBitmap(pManager->GetPaintDC(), rc.right, rc.bottom); + ASSERT(hPaintDC); + ASSERT(hPaintBitmap); + HBITMAP hOldPaintBitmap = (HBITMAP) ::SelectObject(hPaintDC, hPaintBitmap); + pControl->DoPaint(hPaintDC, rc); + + BITMAPINFO bmi = { 0 }; + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = cx; + bmi.bmiHeader.biHeight = cy; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = cx * cy * sizeof(DWORD); + LPDWORD pDest = NULL; + HDC hCloneDC = ::CreateCompatibleDC(pManager->GetPaintDC()); + HBITMAP hBitmap = ::CreateDIBSection(pManager->GetPaintDC(), &bmi, DIB_RGB_COLORS, (LPVOID*) &pDest, NULL, 0); + ASSERT(hCloneDC); + ASSERT(hBitmap); + if( hBitmap != NULL ) + { + HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(hCloneDC, hBitmap); + ::BitBlt(hCloneDC, 0, 0, cx, cy, hPaintDC, rc.left, rc.top, SRCCOPY); + ::SelectObject(hCloneDC, hOldBitmap); + ::DeleteDC(hCloneDC); + ::GdiFlush(); + } + + // Cleanup + ::SelectObject(hPaintDC, hOldPaintBitmap); + ::DeleteObject(hPaintBitmap); + ::DeleteDC(hPaintDC); + + return hBitmap; +} + +SIZE CRenderEngine::GetTextSize( HDC hDC, CPaintManagerUI* pManager , LPCTSTR pstrText, int iFont, UINT uStyle ) +{ + SIZE size = {0,0}; + ASSERT(::GetObjectType(hDC)==OBJ_DC || ::GetObjectType(hDC)==OBJ_MEMDC); + if( pstrText == NULL || pManager == NULL ) return size; + ::SetBkMode(hDC, TRANSPARENT); + HFONT hOldFont = (HFONT)::SelectObject(hDC, pManager->GetFont(iFont)); + GetTextExtentPoint32(hDC, pstrText, _tcslen(pstrText) , &size); + ::SelectObject(hDC, hOldFont); + return size; +} + +void CRenderEngine::CheckAlphaColor(DWORD& dwColor) +{ + //RestoreAlphaColorΪ0x00000000͸GDIƵµ + //GDIв0xFF000000ɫֵڴRGB(0,0,1) + //RGB(0,0,1)RGB(0,0,0)ѷֳ + if((0x00FFFFFF & dwColor) == 0) + { + dwColor += 1; + } +} + +void CRenderEngine::ClearAlphaPixel(LPBYTE pBits, int bitsWidth, PRECT rc) +{ + if(!pBits) + return; + + for(int i = rc->top; i < rc->bottom; ++i) + { + for(int j = rc->left; j < rc->right; ++j) + { + int x = (i*bitsWidth + j) * 4; + *((unsigned int*)&pBits[x]) = 0; + } + } +} + +void CRenderEngine::RestoreAlphaColor(LPBYTE pBits, int bitsWidth, PRECT rc) +{ + for(int i = rc->top; i < rc->bottom; ++i) + { + for(int j = rc->left; j < rc->right; ++j) + { + int x = (i*bitsWidth + j) * 4; + if((pBits[x + 3] == 0)&& (pBits[x + 0] != 0 || pBits[x + 1] != 0|| pBits[x + 2] != 0)) + pBits[x + 3] = 255; + } + } +} +} // namespace DuiLib diff --git a/DuiLib/Core/UIRender.h b/DuiLib/Core/UIRender.h new file mode 100644 index 00000000..0985db05 --- /dev/null +++ b/DuiLib/Core/UIRender.h @@ -0,0 +1,61 @@ +#ifndef __UIRENDER_H__ +#define __UIRENDER_H__ + +#pragma once + +namespace DuiLib { +///////////////////////////////////////////////////////////////////////////////////// +// + +class UILIB_API CRenderClip +{ +public: + ~CRenderClip(); + RECT rcItem; + HDC hDC; + HRGN hRgn; + HRGN hOldRgn; + + static void GenerateClip(HDC hDC, RECT rc, CRenderClip& clip); + static void GenerateRoundClip(HDC hDC, RECT rc, RECT rcItem, int width, int height, CRenderClip& clip); + static void UseOldClipBegin(HDC hDC, CRenderClip& clip); + static void UseOldClipEnd(HDC hDC, CRenderClip& clip); +}; + +///////////////////////////////////////////////////////////////////////////////////// +// + +class UILIB_API CRenderEngine +{ +public: + static DWORD AdjustColor(DWORD dwColor, short H, short S, short L); + static TImageInfo* LoadImage(STRINGorID bitmap, LPCTSTR type = NULL, DWORD mask = 0); + static void FreeImage(const TImageInfo* bitmap); + static void DrawImage(HDC hDC, HBITMAP hBitmap, const RECT& rc, const RECT& rcPaint, \ + const RECT& rcBmpPart, const RECT& rcCorners, bool alphaChannel, BYTE uFade = 255, + bool hole = false, bool xtiled = false, bool ytiled = false); + static bool DrawImageString(HDC hDC, CPaintManagerUI* pManager, const RECT& rcItem, const RECT& rcPaint, + LPCTSTR pStrImage, LPCTSTR pStrModify = NULL); + static void DrawColor(HDC hDC, const RECT& rc, DWORD color); + static void DrawGradient(HDC hDC, const RECT& rc, DWORD dwFirst, DWORD dwSecond, bool bVertical, int nSteps); + + // ºеɫalphaֵЧ + static void DrawLine(HDC hDC, const RECT& rc, int nSize, DWORD dwPenColor,int nStyle = PS_SOLID); + static void DrawRect(HDC hDC, const RECT& rc, int nSize, DWORD dwPenColor); + static void DrawRoundRect(HDC hDC, const RECT& rc, int width, int height, int nSize, DWORD dwPenColor); + static void DrawText(HDC hDC, CPaintManagerUI* pManager, RECT& rc, LPCTSTR pstrText, \ + DWORD dwTextColor, int iFont, UINT uStyle); + static void DrawHtmlText(HDC hDC, CPaintManagerUI* pManager, RECT& rc, LPCTSTR pstrText, + DWORD dwTextColor, RECT* pLinks, CDuiString* sLinks, int& nLinkRects, UINT uStyle); + static HBITMAP GenerateBitmap(CPaintManagerUI* pManager, CControlUI* pControl, RECT rc); + static SIZE GetTextSize(HDC hDC, CPaintManagerUI* pManager , LPCTSTR pstrText, int iFont, UINT uStyle); + + //alpha utilities + static void CheckAlphaColor(DWORD& dwColor); + static void ClearAlphaPixel(LPBYTE pBits, int bitsWidth, PRECT rc); + static void RestoreAlphaColor(LPBYTE pBits, int bitsWidth, PRECT rc); +}; + +} // namespace DuiLib + +#endif // __UIRENDER_H__ diff --git a/DuiLib/DuiLib.vcxproj b/DuiLib/DuiLib.vcxproj new file mode 100644 index 00000000..5e9899c2 --- /dev/null +++ b/DuiLib/DuiLib.vcxproj @@ -0,0 +1,232 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {E106ACD7-4E53-4AEE-942B-D0DD426DB34E} + DuiLib + + + + DynamicLibrary + Unicode + + + DynamicLibrary + Unicode + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + false + false + false + false + false + false + AllRules.ruleset + + + AllRules.ruleset + + + $(ProjectName)_d + $(SolutionDir)Temp\$(Configuration)\ + $(SolutionDir)Temp\$(Configuration)\ + $(SolutionDir)Bin\ + $(SolutionDir)Bin\ + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/DuiLib.tlb + + + + + Disabled + WIN32;_DEBUG;_WINDOWS;UILIB_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + Use + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + true + $(OutDir)$(TargetName).pdb + 0x11000000 + MachineX86 + $(SolutionDir)Lib\$(ProjectName)_d.lib + + + true + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/DuiLib.tlb + + + + + MinSpace + OnlyExplicitInline + WIN32;NDEBUG;_WINDOWS;UILIB_EXPORTS;%(PreprocessorDefinitions) + true + true + Use + Level3 + true + MultiThreadedDLL + + + NDEBUG;%(PreprocessorDefinitions) + 0x0406 + + + true + 0x11000000 + MachineX86 + $(OutDir)$(TargetName).pdb + $(SolutionDir)Lib\$(ProjectName).lib + + + true + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DuiLib/DuiLib.vcxproj.bak b/DuiLib/DuiLib.vcxproj.bak new file mode 100644 index 00000000..52045cf7 --- /dev/null +++ b/DuiLib/DuiLib.vcxproj.bak @@ -0,0 +1,237 @@ + + + + + UnicodeDebug + Win32 + + + UnicodeRelease + Win32 + + + + {E106ACD7-4E53-4AEE-942B-D0DD426DB34E} + DuiLib + + + + DynamicLibrary + false + Unicode + + + DynamicLibrary + false + Unicode + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + false + false + false + false + false + false + AllRules.ruleset + + + AllRules.ruleset + + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/DuiLib.tlb + + + + + Disabled + WIN32;_DEBUG;_WINDOWS;UILIB_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + Use + stdafx.h + ..\Temp\Dui2010_Debug_u\DuiLib.pch + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + ../dll/DuiLib_ud.dll + true + true + $(OutDir)$(TargetName).pdb + 0x11000000 + ../Lib/DuiLib_ud.lib + MachineX86 + + + true + .\Debug/DuiLib.bsc + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/DuiLib.tlb + + + + + MinSpace + OnlyExplicitInline + WIN32;NDEBUG;_WINDOWS;UILIB_EXPORTS;%(PreprocessorDefinitions) + true + true + Use + ..\Temp\Dui2010_Release_u\DuiLib.pch + Level3 + true + MultiThreadedDLL + + + NDEBUG;%(PreprocessorDefinitions) + 0x0406 + + + ../dll/DuiLib_u.dll + true + 0x11000000 + ../Lib/DuiLib_u.lib + MachineX86 + $(OutDir)$(TargetName).pdb + + + true + .\Release/DuiLib.bsc + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DuiLib/DuiLib.vcxproj.filters b/DuiLib/DuiLib.vcxproj.filters new file mode 100644 index 00000000..26af114d --- /dev/null +++ b/DuiLib/DuiLib.vcxproj.filters @@ -0,0 +1,288 @@ + + + + + {64d71d85-4b00-457e-afb3-41e8143f5f4b} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {4e361078-6cc0-44ee-9671-df894ec1d0e3} + + + {3b45530c-ea84-4dc7-b478-4ff1466a252a} + + + {ede7728d-c1aa-4572-9dec-7688cb614cad} + + + {bb533e20-fb1d-4955-83b5-07b22790e264} + + + {a6233711-960e-41c9-888d-d8037ea47d50} + h;hpp;hxx;hm;inl + + + {234d7b68-8c83-42eb-a285-2365598e66f9} + + + {a844aec6-0bc6-44b5-be57-89d8bd6d16bc} + + + {1a50d606-2791-4fc8-8a6f-fd64b1ebc721} + + + {77af9153-b795-4ed6-bcab-50a6b1c6741c} + + + {d2d4b88c-de59-4ad8-882a-9203538020cd} + ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files\Utils + + + Source Files\Utils + + + Source Files\Utils + + + Source Files\Utils + + + Source Files\Core + + + Source Files\Core + + + Source Files\Core + + + Source Files\Core + + + Source Files\Core + + + Source Files\Core + + + Source Files\Core + + + Source Files\Layout + + + Source Files\Layout + + + Source Files\Layout + + + Source Files\Layout + + + Source Files\Layout + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Control + + + Source Files\Utils + + + Source Files\Control + + + + + Header Files + + + Header Files + + + Header Files\Utils + + + Header Files\Utils + + + Header Files\Utils + + + Header Files\Utils + + + Header Files\Utils + + + Header Files\Utils + + + Header Files\Core + + + Header Files\Core + + + Header Files\Core + + + Header Files\Core + + + Header Files\Core + + + Header Files\Core + + + Header Files\Core + + + Header Files\Core + + + Header Files\Layout + + + Header Files\Layout + + + Header Files\Layout + + + Header Files\Layout + + + Header Files\Layout + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Control + + + Header Files\Utils + + + Header Files\Control + + + Header Files\Utils + + + \ No newline at end of file diff --git a/DuiLib/DuiLib.vcxproj.user b/DuiLib/DuiLib.vcxproj.user new file mode 100644 index 00000000..ace9a86a --- /dev/null +++ b/DuiLib/DuiLib.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/DuiLib/Layout/UIChildLayout.cpp b/DuiLib/Layout/UIChildLayout.cpp new file mode 100644 index 00000000..5d9f2889 --- /dev/null +++ b/DuiLib/Layout/UIChildLayout.cpp @@ -0,0 +1,56 @@ +#include "stdafx.h" +#include "UIChildLayout.h" + +namespace DuiLib +{ + CChildLayoutUI::CChildLayoutUI() + { + + } + + void CChildLayoutUI::Init() + { + if (!m_pstrXMLFile.IsEmpty()) + { + CDialogBuilder builder; + CContainerUI* pChildWindow = static_cast(builder.Create(m_pstrXMLFile.GetData(), (UINT)0, NULL, m_pManager)); + if (pChildWindow) + { + this->Add(pChildWindow); + } + else + { + this->RemoveAll(); + } + } + } + + void CChildLayoutUI::SetAttribute( LPCTSTR pstrName, LPCTSTR pstrValue ) + { + if( _tcscmp(pstrName, _T("xmlfile")) == 0 ) + SetChildLayoutXML(pstrValue); + else + CContainerUI::SetAttribute(pstrName,pstrValue); + } + + void CChildLayoutUI::SetChildLayoutXML( DuiLib::CDuiString pXML ) + { + m_pstrXMLFile=pXML; + } + + DuiLib::CDuiString CChildLayoutUI::GetChildLayoutXML() + { + return m_pstrXMLFile; + } + + LPVOID CChildLayoutUI::GetInterface( LPCTSTR pstrName ) + { + if( _tcscmp(pstrName, DUI_CTR_CHILDLAYOUT) == 0 ) return static_cast(this); + return CControlUI::GetInterface(pstrName); + } + + LPCTSTR CChildLayoutUI::GetClass() const + { + return _T("ChildLayoutUI"); + } +} // namespace DuiLib diff --git a/DuiLib/Layout/UIChildLayout.h b/DuiLib/Layout/UIChildLayout.h new file mode 100644 index 00000000..70e3cddd --- /dev/null +++ b/DuiLib/Layout/UIChildLayout.h @@ -0,0 +1,24 @@ +#ifndef __UICHILDLAYOUT_H__ +#define __UICHILDLAYOUT_H__ + +#pragma once + +namespace DuiLib +{ + class UILIB_API CChildLayoutUI : public CContainerUI + { + public: + CChildLayoutUI(); + + void Init(); + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + void SetChildLayoutXML(CDuiString pXML); + DuiLib::CDuiString GetChildLayoutXML(); + virtual LPVOID GetInterface(LPCTSTR pstrName); + virtual LPCTSTR GetClass() const; + + private: + DuiLib::CDuiString m_pstrXMLFile; + }; +} // namespace DuiLib +#endif // __UICHILDLAYOUT_H__ diff --git a/DuiLib/Layout/UIHorizontalLayout.cpp b/DuiLib/Layout/UIHorizontalLayout.cpp new file mode 100644 index 00000000..619f2f70 --- /dev/null +++ b/DuiLib/Layout/UIHorizontalLayout.cpp @@ -0,0 +1,288 @@ +#include "stdafx.h" +#include "UIHorizontalLayout.h" + +namespace DuiLib +{ + CHorizontalLayoutUI::CHorizontalLayoutUI() : m_iSepWidth(0), m_uButtonState(0), m_bImmMode(false) + { + ptLastMouse.x = ptLastMouse.y = 0; + ::ZeroMemory(&m_rcNewPos, sizeof(m_rcNewPos)); + } + + LPCTSTR CHorizontalLayoutUI::GetClass() const + { + return _T("HorizontalLayoutUI"); + } + + LPVOID CHorizontalLayoutUI::GetInterface(LPCTSTR pstrName) + { + if( _tcscmp(pstrName, DUI_CTR_HORIZONTALLAYOUT) == 0 ) return static_cast(this); + return CContainerUI::GetInterface(pstrName); + } + + UINT CHorizontalLayoutUI::GetControlFlags() const + { + if( IsEnabled() && m_iSepWidth != 0 ) return UIFLAG_SETCURSOR; + else return 0; + } + + void CHorizontalLayoutUI::SetPos(RECT rc) + { + CControlUI::SetPos(rc); + rc = m_rcItem; + + // Adjust for inset + rc.left += m_rcInset.left; + rc.top += m_rcInset.top; + rc.right -= m_rcInset.right; + rc.bottom -= m_rcInset.bottom; + + if( m_items.GetSize() == 0) { + ProcessScrollBar(rc, 0, 0); + return; + } + + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth(); + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight(); + + // Determine the width of elements that are sizeable + SIZE szAvailable = { rc.right - rc.left, rc.bottom - rc.top }; + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) + szAvailable.cx += m_pHorizontalScrollBar->GetScrollRange(); + + int nAdjustables = 0; + int cxFixed = 0; + int nEstimateNum = 0; + for( int it1 = 0; it1 < m_items.GetSize(); it1++ ) { + CControlUI* pControl = static_cast(m_items[it1]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) continue; + SIZE sz = pControl->EstimateSize(szAvailable); + if( sz.cx == 0 ) { + nAdjustables++; + } + else { + if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); + if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); + } + cxFixed += sz.cx + pControl->GetPadding().left + pControl->GetPadding().right; + nEstimateNum++; + } + cxFixed += (nEstimateNum - 1) * m_iChildPadding; + + int cxExpand = 0; + int cxNeeded = 0; + if( nAdjustables > 0 ) cxExpand = MAX(0, (szAvailable.cx - cxFixed) / nAdjustables); + // Position the elements + SIZE szRemaining = szAvailable; + int iPosX = rc.left; + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) { + iPosX -= m_pHorizontalScrollBar->GetScrollPos(); + } + int iAdjustable = 0; + int cxFixedRemaining = cxFixed; + for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) { + CControlUI* pControl = static_cast(m_items[it2]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) { + SetFloatPos(it2); + continue; + } + RECT rcPadding = pControl->GetPadding(); + szRemaining.cx -= rcPadding.left; + SIZE sz = pControl->EstimateSize(szRemaining); + if( sz.cx == 0 ) { + iAdjustable++; + sz.cx = cxExpand; + // Distribute remaining to last element (usually round-off left-overs) +// if( iAdjustable == nAdjustables ) { +// sz.cx = MAX(0, szRemaining.cx - rcPadding.right - cxFixedRemaining); +// } + if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); + if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); + } + else { + if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); + if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); + +// cxFixedRemaining -= sz.cx + rcPadding.left + rcPadding.right ; + } + +// cxFixedRemaining -= m_iChildPadding; + + sz.cy = pControl->GetFixedHeight(); + if( sz.cy == 0 ) sz.cy = rc.bottom - rc.top - rcPadding.top - rcPadding.bottom; + if( sz.cy < 0 ) sz.cy = 0; + if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight(); + if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight(); + + RECT rcCtrl = { iPosX + rcPadding.left, rc.top + rcPadding.top, iPosX + sz.cx + rcPadding.left , rc.top + rcPadding.top + sz.cy}; + pControl->SetPos(rcCtrl); + iPosX += sz.cx + m_iChildPadding + rcPadding.left + rcPadding.right; + cxNeeded += sz.cx + rcPadding.left + rcPadding.right; + szRemaining.cx -= sz.cx + m_iChildPadding + rcPadding.right; + } + cxNeeded += (nEstimateNum - 1) * m_iChildPadding; +//reddrain + if( m_pHorizontalScrollBar != NULL ) { + if( cxNeeded > rc.right - rc.left ) { + if( m_pHorizontalScrollBar->IsVisible() ) { + m_pHorizontalScrollBar->SetScrollRange(cxNeeded - (rc.right - rc.left)); + } + else { + m_pHorizontalScrollBar->SetVisible(true); + m_pHorizontalScrollBar->SetScrollRange(cxNeeded - (rc.right - rc.left)); + m_pHorizontalScrollBar->SetScrollPos(0); + rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight(); + } + } + else { + if( m_pHorizontalScrollBar->IsVisible() ) { + m_pHorizontalScrollBar->SetVisible(false); + m_pHorizontalScrollBar->SetScrollRange(0); + m_pHorizontalScrollBar->SetScrollPos(0); + rc.bottom += m_pHorizontalScrollBar->GetFixedHeight(); + } + } + } + + ProcessScrollBar(rc, cxNeeded, 0); + } + + void CHorizontalLayoutUI::DoPostPaint(HDC hDC, const RECT& rcPaint) + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 && !m_bImmMode ) { + RECT rcSeparator = GetThumbRect(true); + CRenderEngine::DrawColor(hDC, rcSeparator, 0xAA000000); + } + } + + void CHorizontalLayoutUI::SetSepWidth(int iWidth) + { + m_iSepWidth = iWidth; + } + + int CHorizontalLayoutUI::GetSepWidth() const + { + return m_iSepWidth; + } + + void CHorizontalLayoutUI::SetSepImmMode(bool bImmediately) + { + if( m_bImmMode == bImmediately ) return; + if( (m_uButtonState & UISTATE_CAPTURED) != 0 && !m_bImmMode && m_pManager != NULL ) { + m_pManager->RemovePostPaint(this); + } + + m_bImmMode = bImmediately; + } + + bool CHorizontalLayoutUI::IsSepImmMode() const + { + return m_bImmMode; + } + + void CHorizontalLayoutUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if( _tcscmp(pstrName, _T("sepwidth")) == 0 ) SetSepWidth(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("sepimm")) == 0 ) SetSepImmMode(_tcscmp(pstrValue, _T("true")) == 0); + else CContainerUI::SetAttribute(pstrName, pstrValue); + } + + void CHorizontalLayoutUI::DoEvent(TEventUI& event) + { + if( m_iSepWidth != 0 ) { + if( event.Type == UIEVENT_BUTTONDOWN && IsEnabled() ) + { + RECT rcSeparator = GetThumbRect(false); + if( ::PtInRect(&rcSeparator, event.ptMouse) ) { + m_uButtonState |= UISTATE_CAPTURED; + ptLastMouse = event.ptMouse; + m_rcNewPos = m_rcItem; + if( !m_bImmMode && m_pManager ) m_pManager->AddPostPaint(this); + return; + } + } + if( event.Type == UIEVENT_BUTTONUP ) + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { + m_uButtonState &= ~UISTATE_CAPTURED; + m_rcItem = m_rcNewPos; + if( !m_bImmMode && m_pManager ) m_pManager->RemovePostPaint(this); + NeedParentUpdate(); + return; + } + } + if( event.Type == UIEVENT_MOUSEMOVE ) + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { + LONG cx = event.ptMouse.x - ptLastMouse.x; + ptLastMouse = event.ptMouse; + RECT rc = m_rcNewPos; + if( m_iSepWidth >= 0 ) { + if( cx > 0 && event.ptMouse.x < m_rcNewPos.right - m_iSepWidth ) return; + if( cx < 0 && event.ptMouse.x > m_rcNewPos.right ) return; + rc.right += cx; + if( rc.right - rc.left <= GetMinWidth() ) { + if( m_rcNewPos.right - m_rcNewPos.left <= GetMinWidth() ) return; + rc.right = rc.left + GetMinWidth(); + } + if( rc.right - rc.left >= GetMaxWidth() ) { + if( m_rcNewPos.right - m_rcNewPos.left >= GetMaxWidth() ) return; + rc.right = rc.left + GetMaxWidth(); + } + } + else { + if( cx > 0 && event.ptMouse.x < m_rcNewPos.left ) return; + if( cx < 0 && event.ptMouse.x > m_rcNewPos.left - m_iSepWidth ) return; + rc.left += cx; + if( rc.right - rc.left <= GetMinWidth() ) { + if( m_rcNewPos.right - m_rcNewPos.left <= GetMinWidth() ) return; + rc.left = rc.right - GetMinWidth(); + } + if( rc.right - rc.left >= GetMaxWidth() ) { + if( m_rcNewPos.right - m_rcNewPos.left >= GetMaxWidth() ) return; + rc.left = rc.right - GetMaxWidth(); + } + } + + CDuiRect rcInvalidate = GetThumbRect(true); + m_rcNewPos = rc; + m_cxyFixed.cx = m_rcNewPos.right - m_rcNewPos.left; + + if( m_bImmMode ) { + m_rcItem = m_rcNewPos; + NeedParentUpdate(); + } + else { + rcInvalidate.Join(GetThumbRect(true)); + rcInvalidate.Join(GetThumbRect(false)); + if( m_pManager ) m_pManager->Invalidate(rcInvalidate); + } + return; + } + } + if( event.Type == UIEVENT_SETCURSOR ) + { + RECT rcSeparator = GetThumbRect(false); + if( IsEnabled() && ::PtInRect(&rcSeparator, event.ptMouse) ) { + ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZEWE))); + return; + } + } + } + CContainerUI::DoEvent(event); + } + + RECT CHorizontalLayoutUI::GetThumbRect(bool bUseNew) const + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 && bUseNew) { + if( m_iSepWidth >= 0 ) return CDuiRect(m_rcNewPos.right - m_iSepWidth, m_rcNewPos.top, m_rcNewPos.right, m_rcNewPos.bottom); + else return CDuiRect(m_rcNewPos.left, m_rcNewPos.top, m_rcNewPos.left - m_iSepWidth, m_rcNewPos.bottom); + } + else { + if( m_iSepWidth >= 0 ) return CDuiRect(m_rcItem.right - m_iSepWidth, m_rcItem.top, m_rcItem.right, m_rcItem.bottom); + else return CDuiRect(m_rcItem.left, m_rcItem.top, m_rcItem.left - m_iSepWidth, m_rcItem.bottom); + } + } +} diff --git a/DuiLib/Layout/UIHorizontalLayout.h b/DuiLib/Layout/UIHorizontalLayout.h new file mode 100644 index 00000000..067d3f90 --- /dev/null +++ b/DuiLib/Layout/UIHorizontalLayout.h @@ -0,0 +1,37 @@ +#ifndef __UIHORIZONTALLAYOUT_H__ +#define __UIHORIZONTALLAYOUT_H__ + +#pragma once + +namespace DuiLib +{ + class UILIB_API CHorizontalLayoutUI : public CContainerUI + { + public: + CHorizontalLayoutUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + UINT GetControlFlags() const; + + void SetSepWidth(int iWidth); + int GetSepWidth() const; + void SetSepImmMode(bool bImmediately); + bool IsSepImmMode() const; + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + void DoEvent(TEventUI& event); + + void SetPos(RECT rc); + void DoPostPaint(HDC hDC, const RECT& rcPaint); + + RECT GetThumbRect(bool bUseNew = false) const; + + protected: + int m_iSepWidth; + UINT m_uButtonState; + POINT ptLastMouse; + RECT m_rcNewPos; + bool m_bImmMode; + }; +} +#endif // __UIHORIZONTALLAYOUT_H__ diff --git a/DuiLib/Layout/UITabLayout.cpp b/DuiLib/Layout/UITabLayout.cpp new file mode 100644 index 00000000..9df66265 --- /dev/null +++ b/DuiLib/Layout/UITabLayout.cpp @@ -0,0 +1,184 @@ +#include "stdafx.h" +#include "UITabLayout.h" + +namespace DuiLib +{ + CTabLayoutUI::CTabLayoutUI() : m_iCurSel(-1) + { + } + + LPCTSTR CTabLayoutUI::GetClass() const + { + return _T("TabLayoutUI"); + } + + LPVOID CTabLayoutUI::GetInterface(LPCTSTR pstrName) + { + if( _tcscmp(pstrName, DUI_CTR_TABLAYOUT) == 0 ) return static_cast(this); + return CContainerUI::GetInterface(pstrName); + } + + bool CTabLayoutUI::Add(CControlUI* pControl) + { + bool ret = CContainerUI::Add(pControl); + if( !ret ) return ret; + + if(m_iCurSel == -1 && pControl->IsVisible()) + { + m_iCurSel = GetItemIndex(pControl); + } + else + { + pControl->SetVisible(false); + } + + return ret; + } + + bool CTabLayoutUI::AddAt(CControlUI* pControl, int iIndex) + { + bool ret = CContainerUI::AddAt(pControl, iIndex); + if( !ret ) return ret; + + if(m_iCurSel == -1 && pControl->IsVisible()) + { + m_iCurSel = GetItemIndex(pControl); + } + else if( m_iCurSel != -1 && iIndex <= m_iCurSel ) + { + m_iCurSel += 1; + } + else + { + pControl->SetVisible(false); + } + + return ret; + } + + bool CTabLayoutUI::Remove(CControlUI* pControl) + { + if( pControl == NULL) return false; + + int index = GetItemIndex(pControl); + bool ret = CContainerUI::Remove(pControl); + if( !ret ) return false; + + if( m_iCurSel == index) + { + if( GetCount() > 0 ) + { + m_iCurSel=0; + GetItemAt(m_iCurSel)->SetVisible(true); + } + else + m_iCurSel=-1; + NeedParentUpdate(); + } + else if( m_iCurSel > index ) + { + m_iCurSel -= 1; + } + + return ret; + } + + void CTabLayoutUI::RemoveAll() + { + m_iCurSel = -1; + CContainerUI::RemoveAll(); + NeedParentUpdate(); + } + + int CTabLayoutUI::GetCurSel() const + { + return m_iCurSel; + } + + bool CTabLayoutUI::SelectItem(int iIndex) + { + if( iIndex < 0 || iIndex >= m_items.GetSize() ) return false; + if( iIndex == m_iCurSel ) return true; + + int iOldSel = m_iCurSel; + m_iCurSel = iIndex; + for( int it = 0; it < m_items.GetSize(); it++ ) + { + if( it == iIndex ) { + GetItemAt(it)->SetVisible(true); + GetItemAt(it)->SetFocus(); + SetPos(m_rcItem); + } + else GetItemAt(it)->SetVisible(false); + } + NeedParentUpdate(); + + if( m_pManager != NULL ) { + m_pManager->SetNextTabControl(); + m_pManager->SendNotify(this, DUI_MSGTYPE_TABSELECT, m_iCurSel, iOldSel); + } + return true; + } + + bool CTabLayoutUI::SelectItem( CControlUI* pControl ) + { + int iIndex=GetItemIndex(pControl); + if (iIndex==-1) + return false; + else + return SelectItem(iIndex); + } + + void CTabLayoutUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if( _tcscmp(pstrName, _T("selectedid")) == 0 ) SelectItem(_ttoi(pstrValue)); + return CContainerUI::SetAttribute(pstrName, pstrValue); + } + + void CTabLayoutUI::SetPos(RECT rc) + { + CControlUI::SetPos(rc); + rc = m_rcItem; + + // Adjust for inset + rc.left += m_rcInset.left; + rc.top += m_rcInset.top; + rc.right -= m_rcInset.right; + rc.bottom -= m_rcInset.bottom; + + for( int it = 0; it < m_items.GetSize(); it++ ) { + CControlUI* pControl = static_cast(m_items[it]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) { + SetFloatPos(it); + continue; + } + + if( it != m_iCurSel ) continue; + + RECT rcPadding = pControl->GetPadding(); + rc.left += rcPadding.left; + rc.top += rcPadding.top; + rc.right -= rcPadding.right; + rc.bottom -= rcPadding.bottom; + + SIZE szAvailable = { rc.right - rc.left, rc.bottom - rc.top }; + + SIZE sz = pControl->EstimateSize(szAvailable); + if( sz.cx == 0 ) { + sz.cx = MAX(0, szAvailable.cx); + } + if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); + if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); + + if(sz.cy == 0) { + sz.cy = MAX(0, szAvailable.cy); + } + if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight(); + if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight(); + + RECT rcCtrl = { rc.left, rc.top, rc.left + sz.cx, rc.top + sz.cy}; + pControl->SetPos(rcCtrl); + } + } +} diff --git a/DuiLib/Layout/UITabLayout.h b/DuiLib/Layout/UITabLayout.h new file mode 100644 index 00000000..964006cd --- /dev/null +++ b/DuiLib/Layout/UITabLayout.h @@ -0,0 +1,32 @@ +#ifndef __UITABLAYOUT_H__ +#define __UITABLAYOUT_H__ + +#pragma once + +namespace DuiLib +{ + class UILIB_API CTabLayoutUI : public CContainerUI + { + public: + CTabLayoutUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + + bool Add(CControlUI* pControl); + bool AddAt(CControlUI* pControl, int iIndex); + bool Remove(CControlUI* pControl); + void RemoveAll(); + int GetCurSel() const; + virtual bool SelectItem(int iIndex); + virtual bool SelectItem(CControlUI* pControl); + + void SetPos(RECT rc); + + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + protected: + int m_iCurSel; + }; +} +#endif // __UITABLAYOUT_H__ diff --git a/DuiLib/Layout/UITileLayout.cpp b/DuiLib/Layout/UITileLayout.cpp new file mode 100644 index 00000000..ed514695 --- /dev/null +++ b/DuiLib/Layout/UITileLayout.cpp @@ -0,0 +1,183 @@ +#include "stdafx.h" +#include "UITileLayout.h" + +namespace DuiLib +{ + CTileLayoutUI::CTileLayoutUI() : m_nColumns(1) + { + m_szItem.cx = m_szItem.cy = 0; + } + + LPCTSTR CTileLayoutUI::GetClass() const + { + return _T("TileLayoutUI"); + } + + LPVOID CTileLayoutUI::GetInterface(LPCTSTR pstrName) + { + if( _tcscmp(pstrName, DUI_CTR_TILELAYOUT) == 0 ) return static_cast(this); + return CContainerUI::GetInterface(pstrName); + } + + SIZE CTileLayoutUI::GetItemSize() const + { + return m_szItem; + } + + void CTileLayoutUI::SetItemSize(SIZE szItem) + { + if( m_szItem.cx != szItem.cx || m_szItem.cy != szItem.cy ) { + m_szItem = szItem; + NeedUpdate(); + } + } + + int CTileLayoutUI::GetColumns() const + { + return m_nColumns; + } + + void CTileLayoutUI::SetColumns(int nCols) + { + if( nCols <= 0 ) return; + m_nColumns = nCols; + NeedUpdate(); + } + + void CTileLayoutUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if( _tcscmp(pstrName, _T("itemsize")) == 0 ) { + SIZE szItem = { 0 }; + LPTSTR pstr = NULL; + szItem.cx = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr); + szItem.cy = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr); + SetItemSize(szItem); + } + else if( _tcscmp(pstrName, _T("columns")) == 0 ) SetColumns(_ttoi(pstrValue)); + else CContainerUI::SetAttribute(pstrName, pstrValue); + } + + void CTileLayoutUI::SetPos(RECT rc) + { + CControlUI::SetPos(rc); + rc = m_rcItem; + + // Adjust for inset + rc.left += m_rcInset.left; + rc.top += m_rcInset.top; + rc.right -= m_rcInset.right; + rc.bottom -= m_rcInset.bottom; + + if( m_items.GetSize() == 0) { + ProcessScrollBar(rc, 0, 0); + return; + } + + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth(); + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight(); + + // Position the elements + if( m_szItem.cx > 0 ) m_nColumns = (rc.right - rc.left) / m_szItem.cx; + if( m_nColumns == 0 ) m_nColumns = 1; + + int cyNeeded = 0; + int cxWidth = (rc.right - rc.left) / m_nColumns; + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) + cxWidth = (rc.right - rc.left + m_pHorizontalScrollBar->GetScrollRange() ) / m_nColumns; ; + + int cyHeight = 0; + int iCount = 0; + POINT ptTile = { rc.left, rc.top }; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) { + ptTile.y -= m_pVerticalScrollBar->GetScrollPos(); + } + int iPosX = rc.left; + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) { + iPosX -= m_pHorizontalScrollBar->GetScrollPos(); + ptTile.x = iPosX; + } + for( int it1 = 0; it1 < m_items.GetSize(); it1++ ) { + CControlUI* pControl = static_cast(m_items[it1]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) { + SetFloatPos(it1); + continue; + } + + // Determine size + RECT rcTile = { ptTile.x, ptTile.y, ptTile.x + cxWidth, ptTile.y }; + if( (iCount % m_nColumns) == 0 ) + { + int iIndex = iCount; + for( int it2 = it1; it2 < m_items.GetSize(); it2++ ) { + CControlUI* pLineControl = static_cast(m_items[it2]); + if( !pLineControl->IsVisible() ) continue; + if( pLineControl->IsFloat() ) continue; + + RECT rcPadding = pLineControl->GetPadding(); + SIZE szAvailable = { rcTile.right - rcTile.left - rcPadding.left - rcPadding.right, 9999 }; + if( iIndex == iCount || (iIndex + 1) % m_nColumns == 0 ) { + szAvailable.cx -= m_iChildPadding / 2; + } + else { + szAvailable.cx -= m_iChildPadding; + } + + if( szAvailable.cx < pControl->GetMinWidth() ) szAvailable.cx = pControl->GetMinWidth(); + if( szAvailable.cx > pControl->GetMaxWidth() ) szAvailable.cx = pControl->GetMaxWidth(); + + SIZE szTile = pLineControl->EstimateSize(szAvailable); + if( szTile.cx < pControl->GetMinWidth() ) szTile.cx = pControl->GetMinWidth(); + if( szTile.cx > pControl->GetMaxWidth() ) szTile.cx = pControl->GetMaxWidth(); + if( szTile.cy < pControl->GetMinHeight() ) szTile.cy = pControl->GetMinHeight(); + if( szTile.cy > pControl->GetMaxHeight() ) szTile.cy = pControl->GetMaxHeight(); + + cyHeight = MAX(cyHeight, szTile.cy + rcPadding.top + rcPadding.bottom); + if( (++iIndex % m_nColumns) == 0) break; + } + } + + RECT rcPadding = pControl->GetPadding(); + + rcTile.left += rcPadding.left + m_iChildPadding / 2; + rcTile.right -= rcPadding.right + m_iChildPadding / 2; + if( (iCount % m_nColumns) == 0 ) { + rcTile.left -= m_iChildPadding / 2; + } + + if( ( (iCount + 1) % m_nColumns) == 0 ) { + rcTile.right += m_iChildPadding / 2; + } + + // Set position + rcTile.top = ptTile.y + rcPadding.top; + rcTile.bottom = ptTile.y + cyHeight; + + SIZE szAvailable = { rcTile.right - rcTile.left, rcTile.bottom - rcTile.top }; + SIZE szTile = pControl->EstimateSize(szAvailable); + if( szTile.cx == 0 ) szTile.cx = szAvailable.cx; + if( szTile.cy == 0 ) szTile.cy = szAvailable.cy; + if( szTile.cx < pControl->GetMinWidth() ) szTile.cx = pControl->GetMinWidth(); + if( szTile.cx > pControl->GetMaxWidth() ) szTile.cx = pControl->GetMaxWidth(); + if( szTile.cy < pControl->GetMinHeight() ) szTile.cy = pControl->GetMinHeight(); + if( szTile.cy > pControl->GetMaxHeight() ) szTile.cy = pControl->GetMaxHeight(); + RECT rcPos = {(rcTile.left + rcTile.right - szTile.cx) / 2, (rcTile.top + rcTile.bottom - szTile.cy) / 2, + (rcTile.left + rcTile.right - szTile.cx) / 2 + szTile.cx, (rcTile.top + rcTile.bottom - szTile.cy) / 2 + szTile.cy}; + pControl->SetPos(rcPos); + + if( (++iCount % m_nColumns) == 0 ) { + ptTile.x = iPosX; + ptTile.y += cyHeight + m_iChildPadding; + cyHeight = 0; + } + else { + ptTile.x += cxWidth; + } + cyNeeded = rcTile.bottom - rc.top; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) cyNeeded += m_pVerticalScrollBar->GetScrollPos(); + } + + // Process the scrollbar + ProcessScrollBar(rc, 0, cyNeeded); + } +} diff --git a/DuiLib/Layout/UITileLayout.h b/DuiLib/Layout/UITileLayout.h new file mode 100644 index 00000000..96c82286 --- /dev/null +++ b/DuiLib/Layout/UITileLayout.h @@ -0,0 +1,30 @@ +#ifndef __UITILELAYOUT_H__ +#define __UITILELAYOUT_H__ + +#pragma once + +namespace DuiLib +{ + class UILIB_API CTileLayoutUI : public CContainerUI + { + public: + CTileLayoutUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + + void SetPos(RECT rc); + + SIZE GetItemSize() const; + void SetItemSize(SIZE szItem); + int GetColumns() const; + void SetColumns(int nCols); + + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + + protected: + SIZE m_szItem; + int m_nColumns; + }; +} +#endif // __UITILELAYOUT_H__ diff --git a/DuiLib/Layout/UIVerticalLayout.cpp b/DuiLib/Layout/UIVerticalLayout.cpp new file mode 100644 index 00000000..af52d261 --- /dev/null +++ b/DuiLib/Layout/UIVerticalLayout.cpp @@ -0,0 +1,281 @@ +#include "stdafx.h" +#include "UIVerticalLayout.h" + +namespace DuiLib +{ + CVerticalLayoutUI::CVerticalLayoutUI() : m_iSepHeight(0), m_uButtonState(0), m_bImmMode(false) + { + ptLastMouse.x = ptLastMouse.y = 0; + ::ZeroMemory(&m_rcNewPos, sizeof(m_rcNewPos)); + } + + LPCTSTR CVerticalLayoutUI::GetClass() const + { + return _T("VerticalLayoutUI"); + } + + LPVOID CVerticalLayoutUI::GetInterface(LPCTSTR pstrName) + { + if( _tcscmp(pstrName, DUI_CTR_VERTICALLAYOUT) == 0 ) return static_cast(this); + return CContainerUI::GetInterface(pstrName); + } + + UINT CVerticalLayoutUI::GetControlFlags() const + { + if( IsEnabled() && m_iSepHeight != 0 ) return UIFLAG_SETCURSOR; + else return 0; + } + + void CVerticalLayoutUI::SetPos(RECT rc) + { + CControlUI::SetPos(rc); + rc = m_rcItem; + + // Adjust for inset + rc.left += m_rcInset.left; + rc.top += m_rcInset.top; + rc.right -= m_rcInset.right; + rc.bottom -= m_rcInset.bottom; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth(); + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight(); + + if( m_items.GetSize() == 0) { + ProcessScrollBar(rc, 0, 0); + return; + } + + // Determine the minimum size + SIZE szAvailable = { rc.right - rc.left, rc.bottom - rc.top }; + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) + szAvailable.cx += m_pHorizontalScrollBar->GetScrollRange(); + + int nAdjustables = 0; + int cyFixed = 0; + int nEstimateNum = 0; + for( int it1 = 0; it1 < m_items.GetSize(); it1++ ) { + CControlUI* pControl = static_cast(m_items[it1]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) continue; + SIZE sz = pControl->EstimateSize(szAvailable); + if( sz.cy == 0 ) { + nAdjustables++; + } + else { + if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight(); + if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight(); + } + cyFixed += sz.cy + pControl->GetPadding().top + pControl->GetPadding().bottom; + nEstimateNum++; + } + cyFixed += (nEstimateNum - 1) * m_iChildPadding; + + // Place elements + int cyNeeded = 0; + int cyExpand = 0; + if( nAdjustables > 0 ) cyExpand = MAX(0, (szAvailable.cy - cyFixed) / nAdjustables); + // Position the elements + SIZE szRemaining = szAvailable; + int iPosY = rc.top; + if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) { + iPosY -= m_pVerticalScrollBar->GetScrollPos(); + } + int iPosX = rc.left; + if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) { + iPosX -= m_pHorizontalScrollBar->GetScrollPos(); + } + int iAdjustable = 0; + int cyFixedRemaining = cyFixed; + for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) { + CControlUI* pControl = static_cast(m_items[it2]); + if( !pControl->IsVisible() ) continue; + if( pControl->IsFloat() ) { + SetFloatPos(it2); + continue; + } + + RECT rcPadding = pControl->GetPadding(); + szRemaining.cy -= rcPadding.top; + SIZE sz = pControl->EstimateSize(szRemaining); + if( sz.cy == 0 ) { + iAdjustable++; + sz.cy = cyExpand; + // Distribute remaining to last element (usually round-off left-overs) +// if( iAdjustable == nAdjustables ) { +// sz.cy = MAX(0, szRemaining.cy - rcPadding.bottom - cyFixedRemaining); +// } + if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight(); + if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight(); + } + else { + if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight(); + if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight(); +// cyFixedRemaining -= sz.cy + rcPadding.top + rcPadding.bottom; + } + +// cyFixedRemaining -= m_iChildPadding; + + sz.cx = pControl->GetFixedWidth(); + if( sz.cx == 0 ) sz.cx = szAvailable.cx - rcPadding.left - rcPadding.right; + if( sz.cx < 0 ) sz.cx = 0; + if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); + if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); + + RECT rcCtrl = { iPosX + rcPadding.left, iPosY + rcPadding.top, iPosX + rcPadding.left + sz.cx, iPosY + sz.cy + rcPadding.top }; + pControl->SetPos(rcCtrl); + + iPosY += sz.cy + m_iChildPadding + rcPadding.top + rcPadding.bottom; + cyNeeded += sz.cy + rcPadding.top + rcPadding.bottom; + szRemaining.cy -= sz.cy + m_iChildPadding + rcPadding.bottom; + } + cyNeeded += (nEstimateNum - 1) * m_iChildPadding; + + // Process the scrollbar + ProcessScrollBar(rc, 0, cyNeeded); + } + + void CVerticalLayoutUI::DoPostPaint(HDC hDC, const RECT& rcPaint) + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 && !m_bImmMode ) { + RECT rcSeparator = GetThumbRect(true); + CRenderEngine::DrawColor(hDC, rcSeparator, 0xAA000000); + } + } + + void CVerticalLayoutUI::SetSepHeight(int iHeight) + { + m_iSepHeight = iHeight; + } + + int CVerticalLayoutUI::GetSepHeight() const + { + return m_iSepHeight; + } + + void CVerticalLayoutUI::SetSepImmMode(bool bImmediately) + { + if( m_bImmMode == bImmediately ) return; + if( (m_uButtonState & UISTATE_CAPTURED) != 0 && !m_bImmMode && m_pManager != NULL ) { + m_pManager->RemovePostPaint(this); + } + + m_bImmMode = bImmediately; + } + + bool CVerticalLayoutUI::IsSepImmMode() const + { + return m_bImmMode; + } + + void CVerticalLayoutUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) + { + if( _tcscmp(pstrName, _T("sepheight")) == 0 ) SetSepHeight(_ttoi(pstrValue)); + else if( _tcscmp(pstrName, _T("sepimm")) == 0 ) SetSepImmMode(_tcscmp(pstrValue, _T("true")) == 0); + else CContainerUI::SetAttribute(pstrName, pstrValue); + } + + void CVerticalLayoutUI::DoEvent(TEventUI& event) + { + if( m_iSepHeight != 0 ) { + if( event.Type == UIEVENT_BUTTONDOWN && IsEnabled() ) + { + RECT rcSeparator = GetThumbRect(false); + if( ::PtInRect(&rcSeparator, event.ptMouse) ) { + m_uButtonState |= UISTATE_CAPTURED; + ptLastMouse = event.ptMouse; + m_rcNewPos = m_rcItem; + if( !m_bImmMode && m_pManager ) m_pManager->AddPostPaint(this); + return; + } + } + if( event.Type == UIEVENT_BUTTONUP ) + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { + m_uButtonState &= ~UISTATE_CAPTURED; + m_rcItem = m_rcNewPos; + if( !m_bImmMode && m_pManager ) m_pManager->RemovePostPaint(this); + NeedParentUpdate(); + return; + } + } + if( event.Type == UIEVENT_MOUSEMOVE ) + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { + LONG cy = event.ptMouse.y - ptLastMouse.y; + ptLastMouse = event.ptMouse; + RECT rc = m_rcNewPos; + if( m_iSepHeight >= 0 ) { + if( cy > 0 && event.ptMouse.y < m_rcNewPos.bottom + m_iSepHeight ) return; + if( cy < 0 && event.ptMouse.y > m_rcNewPos.bottom ) return; + rc.bottom += cy; + if( rc.bottom - rc.top <= GetMinHeight() ) { + if( m_rcNewPos.bottom - m_rcNewPos.top <= GetMinHeight() ) return; + rc.bottom = rc.top + GetMinHeight(); + } + if( rc.bottom - rc.top >= GetMaxHeight() ) { + if( m_rcNewPos.bottom - m_rcNewPos.top >= GetMaxHeight() ) return; + rc.bottom = rc.top + GetMaxHeight(); + } + } + else { + if( cy > 0 && event.ptMouse.y < m_rcNewPos.top ) return; + if( cy < 0 && event.ptMouse.y > m_rcNewPos.top + m_iSepHeight ) return; + rc.top += cy; + if( rc.bottom - rc.top <= GetMinHeight() ) { + if( m_rcNewPos.bottom - m_rcNewPos.top <= GetMinHeight() ) return; + rc.top = rc.bottom - GetMinHeight(); + } + if( rc.bottom - rc.top >= GetMaxHeight() ) { + if( m_rcNewPos.bottom - m_rcNewPos.top >= GetMaxHeight() ) return; + rc.top = rc.bottom - GetMaxHeight(); + } + } + + CDuiRect rcInvalidate = GetThumbRect(true); + m_rcNewPos = rc; + m_cxyFixed.cy = m_rcNewPos.bottom - m_rcNewPos.top; + + if( m_bImmMode ) { + m_rcItem = m_rcNewPos; + NeedParentUpdate(); + } + else { + rcInvalidate.Join(GetThumbRect(true)); + rcInvalidate.Join(GetThumbRect(false)); + if( m_pManager ) m_pManager->Invalidate(rcInvalidate); + } + return; + } + } + if( event.Type == UIEVENT_SETCURSOR ) + { + RECT rcSeparator = GetThumbRect(false); + if( IsEnabled() && ::PtInRect(&rcSeparator, event.ptMouse) ) { + ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZENS))); + return; + } + } + } + CContainerUI::DoEvent(event); + } + + RECT CVerticalLayoutUI::GetThumbRect(bool bUseNew) const + { + if( (m_uButtonState & UISTATE_CAPTURED) != 0 && bUseNew) { + if( m_iSepHeight >= 0 ) + return CDuiRect(m_rcNewPos.left, MAX(m_rcNewPos.bottom - m_iSepHeight, m_rcNewPos.top), + m_rcNewPos.right, m_rcNewPos.bottom); + else + return CDuiRect(m_rcNewPos.left, m_rcNewPos.top, m_rcNewPos.right, + MIN(m_rcNewPos.top - m_iSepHeight, m_rcNewPos.bottom)); + } + else { + if( m_iSepHeight >= 0 ) + return CDuiRect(m_rcItem.left, MAX(m_rcItem.bottom - m_iSepHeight, m_rcItem.top), m_rcItem.right, + m_rcItem.bottom); + else + return CDuiRect(m_rcItem.left, m_rcItem.top, m_rcItem.right, + MIN(m_rcItem.top - m_iSepHeight, m_rcItem.bottom)); + + } + } +} diff --git a/DuiLib/Layout/UIVerticalLayout.h b/DuiLib/Layout/UIVerticalLayout.h new file mode 100644 index 00000000..d29b26ab --- /dev/null +++ b/DuiLib/Layout/UIVerticalLayout.h @@ -0,0 +1,37 @@ +#ifndef __UIVERTICALLAYOUT_H__ +#define __UIVERTICALLAYOUT_H__ + +#pragma once + +namespace DuiLib +{ + class UILIB_API CVerticalLayoutUI : public CContainerUI + { + public: + CVerticalLayoutUI(); + + LPCTSTR GetClass() const; + LPVOID GetInterface(LPCTSTR pstrName); + UINT GetControlFlags() const; + + void SetSepHeight(int iHeight); + int GetSepHeight() const; + void SetSepImmMode(bool bImmediately); + bool IsSepImmMode() const; + void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); + void DoEvent(TEventUI& event); + + void SetPos(RECT rc); + void DoPostPaint(HDC hDC, const RECT& rcPaint); + + RECT GetThumbRect(bool bUseNew = false) const; + + protected: + int m_iSepHeight; + UINT m_uButtonState; + POINT ptLastMouse; + RECT m_rcNewPos; + bool m_bImmMode; + }; +} +#endif // __UIVERTICALLAYOUT_H__ diff --git a/DuiLib/StdAfx.cpp b/DuiLib/StdAfx.cpp new file mode 100644 index 00000000..781d60f5 --- /dev/null +++ b/DuiLib/StdAfx.cpp @@ -0,0 +1,9 @@ +// stdafx.cpp : source file that includes just the standard includes +// UIlib.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "StdAfx.h" + + +#pragma comment( lib, "winmm.lib" ) +#pragma comment( lib, "comctl32.lib" ) diff --git a/DuiLib/StdAfx.h b/DuiLib/StdAfx.h new file mode 100644 index 00000000..74822d65 --- /dev/null +++ b/DuiLib/StdAfx.h @@ -0,0 +1,67 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__E30B2003_188B_4EB4_AB99_3F3734D6CE6C__INCLUDED_) +#define AFX_STDAFX_H__E30B2003_188B_4EB4_AB99_3F3734D6CE6C__INCLUDED_ + +#pragma once + +#ifdef __GNUC__ +// ôûҵminmaxͷļ-_- +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#endif + +#ifndef __FILET__ +#define __DUILIB_STR2WSTR(str) L##str +#define _DUILIB_STR2WSTR(str) __DUILIB_STR2WSTR(str) +#ifdef _UNICODE +#define __FILET__ _DUILIB_STR2WSTR(__FILE__) +#define __FUNCTIONT__ _DUILIB_STR2WSTR(__FUNCTION__) +#else +#define __FILET__ __FILE__ +#define __FUNCTIONT__ __FUNCTION__ +#endif +#endif + +#define _CRT_SECURE_NO_DEPRECATE + +// Remove pointless warning messages +#ifdef _MSC_VER +#pragma warning (disable : 4511) // copy operator could not be generated +#pragma warning (disable : 4512) // assignment operator could not be generated +#pragma warning (disable : 4702) // unreachable code (bugs in Microsoft's STL) +#pragma warning (disable : 4786) // identifier was truncated +#pragma warning (disable : 4996) // function or variable may be unsafe (deprecated) +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS // eliminate deprecation warnings for VS2005 +#endif +#endif // _MSC_VER +#ifdef __BORLANDC__ +#pragma option -w-8027 // function not expanded inline +#endif + +// Required for VS 2008 (fails on XP and Win2000 without this fix) +#ifndef _WIN32_WINNT +#define _WIN32_WINNT _WIN32_WINNT_WINXP//0x0500 +#endif + +#include "./UIlib.h" + +#include + +#define lengthof(x) (sizeof(x)/sizeof(*x)) +#define MAX max +#define MIN min +#define CLAMP(x,a,b) (MIN(b,MAX(a,x))) + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__E30B2003_188B_4EB4_AB99_3F3734D6CE6C__INCLUDED_) diff --git a/DuiLib/UILib.cpp b/DuiLib/UILib.cpp new file mode 100644 index 00000000..48fcf53b --- /dev/null +++ b/DuiLib/UILib.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2010-2011, duilib develop team(www.duilib.com). +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or +// without modification, are permitted provided that the +// following conditions are met. +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// +// DirectUI - UI Library +// +// Written by Bjarke Viksoe (bjarke@viksoe.dk) +// Copyright (c) 2006-2007 Bjarke Viksoe. +// +// This code may be used in compiled form in any way you desire. These +// source files may be redistributed by any means PROVIDING it is +// not sold for profit without the authors written consent, and +// providing that this notice and the authors name is included. +// +// This file is provided "as is" with no expressed or implied warranty. +// The author accepts no liability if it causes any damage to you or your +// computer whatsoever. It's free, so don't hassle me about it. +// +// Beware of bugs. +// +// + + +#include "stdafx.h" +#include "UIlib.h" + + +BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID /*lpReserved*/) +{ + switch( dwReason ) { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + ::DisableThreadLibraryCalls((HMODULE)hModule); + break; + } + return TRUE; +} + diff --git a/DuiLib/UIlib.h b/DuiLib/UIlib.h new file mode 100644 index 00000000..b395e6b8 --- /dev/null +++ b/DuiLib/UIlib.h @@ -0,0 +1,86 @@ +#if defined(UILIB_EXPORTS) +# if defined(_MSC_VER) +# define UILIB_API __declspec(dllexport) +# else +# define UILIB_API +# endif +#else +# if defined(_MSC_VER) +# define UILIB_API __declspec(dllimport) +# else +# define UILIB_API +# endif +#endif + +#define UILIB_COMDAT __declspec(selectany) + +#if defined _M_IX86 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_IA64 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_X64 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#else +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Utils/Utils.h" +#include "Utils/observer_impl_base.h" +#include "Utils/UIShadow.h" +#include "Utils/UIDelegate.h" +#include "Core/UIDefine.h" +#include "Core/UIManager.h" +#include "Core/UIBase.h" +#include "Core/UIControl.h" +#include "Core/UIContainer.h" +#include "Core/UIMarkup.h" +#include "Core/UIDlgBuilder.h" +#include "Core/UIRender.h" +#include "Utils/WinImplBase.h" + +#include "Layout/UIVerticalLayout.h" +#include "Layout/UIHorizontalLayout.h" +#include "Layout/UITileLayout.h" +#include "Layout/UITabLayout.h" +#include "Layout/UIChildLayout.h" + +#include "Control/UIList.h" +#include "Control/UICombo.h" +#include "Control/UIScrollBar.h" +#include "Control/UITreeView.h" + +#include "Control/UILabel.h" +#include "Control/UIText.h" +#include "Control/UIEdit.h" +#include "Control/UIGifAnim.h" + +#include "Control/UIButton.h" +#include "Control/UIOption.h" + +#include "Control/UIProgress.h" +#include "Control/UISlider.h" + +#include "Control/UIComboBox.h" +#include "Control/UIRichEdit.h" +#include "Control/UIDateTime.h" + +#include "Control/UIActiveX.h" +#include "Control/UIWebBrowser.h" +#include "Control/UIFlash.h" + +#include "Control/UIMenu.h" + + +#pragma comment(lib,"gdiplus.lib") \ No newline at end of file diff --git a/DuiLib/Utils/Flash11.tlb b/DuiLib/Utils/Flash11.tlb new file mode 100644 index 00000000..813228bf Binary files /dev/null and b/DuiLib/Utils/Flash11.tlb differ diff --git a/DuiLib/Utils/FlashEventHandler.h b/DuiLib/Utils/FlashEventHandler.h new file mode 100644 index 00000000..19805ab8 --- /dev/null +++ b/DuiLib/Utils/FlashEventHandler.h @@ -0,0 +1,40 @@ +/* + ڣ 2012/11/05 15:09:48 + ߣ daviyang35@gmail.com + FlashEventHandler +*/ +#pragma once +//#include + +namespace DuiLib +{ + class CFlashEventHandler + { + public: + CFlashEventHandler() {} + ~CFlashEventHandler() {} + + virtual ULONG STDMETHODCALLTYPE Release( void ) { return S_OK;} + virtual ULONG STDMETHODCALLTYPE AddRef( void ) { return S_OK;} + + virtual HRESULT OnReadyStateChange ( long newState ) + { + return S_OK; + } + + virtual HRESULT OnProgress (long percentDone ) + { + return S_OK; + } + + virtual HRESULT FSCommand ( LPCTSTR command, LPCTSTR args ) + { + return S_OK; + } + + virtual HRESULT FlashCall ( LPCTSTR request ) + { + return S_OK; + } + }; +} \ No newline at end of file diff --git a/DuiLib/Utils/UIDelegate.cpp b/DuiLib/Utils/UIDelegate.cpp new file mode 100644 index 00000000..46b0035c --- /dev/null +++ b/DuiLib/Utils/UIDelegate.cpp @@ -0,0 +1,95 @@ +#include "StdAfx.h" + +namespace DuiLib { + +CDelegateBase::CDelegateBase(void* pObject, void* pFn) +{ + m_pObject = pObject; + m_pFn = pFn; +} + +CDelegateBase::CDelegateBase(const CDelegateBase& rhs) +{ + m_pObject = rhs.m_pObject; + m_pFn = rhs.m_pFn; +} + +CDelegateBase::~CDelegateBase() +{ + +} + +bool CDelegateBase::Equals(const CDelegateBase& rhs) const +{ + return m_pObject == rhs.m_pObject && m_pFn == rhs.m_pFn; +} + +bool CDelegateBase::operator() (void* param) +{ + return Invoke(param); +} + +void* CDelegateBase::GetFn() +{ + return m_pFn; +} + +void* CDelegateBase::GetObject() +{ + return m_pObject; +} + +CEventSource::~CEventSource() +{ + for( int i = 0; i < m_aDelegates.GetSize(); i++ ) { + CDelegateBase* pObject = static_cast(m_aDelegates[i]); + if( pObject) delete pObject; + } +} + +CEventSource::operator bool() +{ + return m_aDelegates.GetSize() > 0; +} + +void CEventSource::operator+= (const CDelegateBase& d) +{ + for( int i = 0; i < m_aDelegates.GetSize(); i++ ) { + CDelegateBase* pObject = static_cast(m_aDelegates[i]); + if( pObject && pObject->Equals(d) ) return; + } + + m_aDelegates.Add(d.Copy()); +} + +void CEventSource::operator+= (FnType pFn) +{ + (*this) += MakeDelegate(pFn); +} + +void CEventSource::operator-= (const CDelegateBase& d) +{ + for( int i = 0; i < m_aDelegates.GetSize(); i++ ) { + CDelegateBase* pObject = static_cast(m_aDelegates[i]); + if( pObject && pObject->Equals(d) ) { + delete pObject; + m_aDelegates.Remove(i); + return; + } + } +} +void CEventSource::operator-= (FnType pFn) +{ + (*this) -= MakeDelegate(pFn); +} + +bool CEventSource::operator() (void* param) +{ + for( int i = 0; i < m_aDelegates.GetSize(); i++ ) { + CDelegateBase* pObject = static_cast(m_aDelegates[i]); + if( pObject && !(*pObject)(param) ) return false; + } + return true; +} + +} // namespace DuiLib diff --git a/DuiLib/Utils/UIDelegate.h b/DuiLib/Utils/UIDelegate.h new file mode 100644 index 00000000..940b27dd --- /dev/null +++ b/DuiLib/Utils/UIDelegate.h @@ -0,0 +1,93 @@ +#ifndef __UIDELEGATE_H__ +#define __UIDELEGATE_H__ + +#pragma once + +namespace DuiLib { + +class UILIB_API CDelegateBase +{ +public: + CDelegateBase(void* pObject, void* pFn); + CDelegateBase(const CDelegateBase& rhs); + virtual ~CDelegateBase(); + bool Equals(const CDelegateBase& rhs) const; + bool operator() (void* param); + virtual CDelegateBase* Copy() const = 0; // add const for gcc + +protected: + void* GetFn(); + void* GetObject(); + virtual bool Invoke(void* param) = 0; + +private: + void* m_pObject; + void* m_pFn; +}; + +class CDelegateStatic: public CDelegateBase +{ + typedef bool (*Fn)(void*); +public: + CDelegateStatic(Fn pFn) : CDelegateBase(NULL, pFn) { } + CDelegateStatic(const CDelegateStatic& rhs) : CDelegateBase(rhs) { } + virtual CDelegateBase* Copy() const { return new CDelegateStatic(*this); } + +protected: + virtual bool Invoke(void* param) + { + Fn pFn = (Fn)GetFn(); + return (*pFn)(param); + } +}; + +template +class CDelegate : public CDelegateBase +{ + typedef bool (T::* Fn)(void*); +public: + CDelegate(O* pObj, Fn pFn) : CDelegateBase(pObj, &pFn), m_pFn(pFn) { } + CDelegate(const CDelegate& rhs) : CDelegateBase(rhs) { m_pFn = rhs.m_pFn; } + virtual CDelegateBase* Copy() const { return new CDelegate(*this); } + +protected: + virtual bool Invoke(void* param) + { + O* pObject = (O*) GetObject(); + return (pObject->*m_pFn)(param); + } + +private: + Fn m_pFn; +}; + +template +CDelegate MakeDelegate(O* pObject, bool (T::* pFn)(void*)) +{ + return CDelegate(pObject, pFn); +} + +inline CDelegateStatic MakeDelegate(bool (*pFn)(void*)) +{ + return CDelegateStatic(pFn); +} + +class UILIB_API CEventSource +{ + typedef bool (*FnType)(void*); +public: + ~CEventSource(); + operator bool(); + void operator+= (const CDelegateBase& d); // add const for gcc + void operator+= (FnType pFn); + void operator-= (const CDelegateBase& d); + void operator-= (FnType pFn); + bool operator() (void* param); + +protected: + CStdPtrArray m_aDelegates; +}; + +} // namespace DuiLib + +#endif // __UIDELEGATE_H__ \ No newline at end of file diff --git a/DuiLib/Utils/UIShadow.cpp b/DuiLib/Utils/UIShadow.cpp new file mode 100644 index 00000000..b17ade48 --- /dev/null +++ b/DuiLib/Utils/UIShadow.cpp @@ -0,0 +1,634 @@ +#include "StdAfx.h" +#include "UIShadow.h" +#include "math.h" +#include "crtdbg.h" + +namespace DuiLib +{ + +const TCHAR *strWndClassName = _T("PerryShadowWnd"); +bool CShadowUI::s_bHasInit = FALSE; + +CShadowUI::CShadowUI(void) +: m_hWnd((HWND)NULL) +, m_OriParentProc(NULL) +, m_Status(0) +, m_nDarkness(150) +, m_nSharpness(5) +, m_nSize(0) +, m_nxOffset(0) +, m_nyOffset(0) +, m_Color(RGB(0, 0, 0)) +, m_WndSize(0) +, m_bUpdate(false) +, m_bIsImageMode(false) +, m_bIsShowShadow(false) +{ + ::ZeroMemory(&m_rcShadowCorner, sizeof(RECT)); +} + +CShadowUI::~CShadowUI(void) +{ +} + +bool CShadowUI::Initialize(HINSTANCE hInstance) +{ + if (s_bHasInit) + return false; + + // Register window class for shadow window + WNDCLASSEX wcex; + + memset(&wcex, 0, sizeof(wcex)); + + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = DefWindowProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = NULL; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = NULL; + wcex.lpszClassName = strWndClassName; + wcex.hIconSm = NULL; + + RegisterClassEx(&wcex); + + s_bHasInit = true; + return true; +} + +void CShadowUI::Create(CPaintManagerUI* pPaintManager) +{ + if(!m_bIsShowShadow) + return; + + // Already initialized + _ASSERT(CPaintManagerUI::GetInstance() != INVALID_HANDLE_VALUE); + _ASSERT(pPaintManager != NULL); + m_pManager = pPaintManager; + HWND hParentWnd = m_pManager->GetPaintWindow(); + // Add parent window - shadow pair to the map + _ASSERT(GetShadowMap().find(hParentWnd) == GetShadowMap().end()); // Only one shadow for each window + GetShadowMap()[hParentWnd] = this; + + // Determine the initial show state of shadow according to parent window's state + LONG lParentStyle = GetWindowLongPtr(hParentWnd, GWL_STYLE); + + // Create the shadow window + LONG styleValue = lParentStyle & WS_CAPTION; + m_hWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, strWndClassName, NULL, + /*WS_VISIBLE | */styleValue | WS_POPUPWINDOW, + CW_USEDEFAULT, 0, 0, 0, hParentWnd, NULL, CPaintManagerUI::GetInstance(), NULL); + + if(!(WS_VISIBLE & lParentStyle)) // Parent invisible + m_Status = SS_ENABLED; + else if((WS_MAXIMIZE | WS_MINIMIZE) & lParentStyle) // Parent visible but does not need shadow + m_Status = SS_ENABLED | SS_PARENTVISIBLE; + else // Show the shadow + { + m_Status = SS_ENABLED | SS_VISABLE | SS_PARENTVISIBLE; + ::ShowWindow(m_hWnd, SW_SHOWNA); + Update(hParentWnd); + } + + // Replace the original WndProc of parent window to steal messages + m_OriParentProc = GetWindowLongPtr(hParentWnd, GWLP_WNDPROC); + +#pragma warning(disable: 4311) // temporrarily disable the type_cast warning in Win32 + SetWindowLongPtr(hParentWnd, GWLP_WNDPROC, (LONG_PTR)ParentProc); +#pragma warning(default: 4311) + +} + +std::map& CShadowUI::GetShadowMap() +{ + static std::map s_Shadowmap; + return s_Shadowmap; +} + +LRESULT CALLBACK CShadowUI::ParentProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + _ASSERT(GetShadowMap().find(hwnd) != GetShadowMap().end()); // Shadow must have been attached + + CShadowUI *pThis = GetShadowMap()[hwnd]; + + switch(uMsg) + { + case WM_MOVE: + if(pThis->m_Status & SS_VISABLE) + { + RECT WndRect; + GetWindowRect(hwnd, &WndRect); + if (pThis->m_bIsImageMode) + { + SetWindowPos(pThis->m_hWnd, 0, + WndRect.left - pThis->m_rcShadowCorner.left, WndRect.top - pThis->m_rcShadowCorner.top, + 0, 0, SWP_NOSIZE | SWP_NOACTIVATE); + } + else + { + SetWindowPos(pThis->m_hWnd, 0, + WndRect.left + pThis->m_nxOffset - pThis->m_nSize, WndRect.top + pThis->m_nyOffset - pThis->m_nSize, + 0, 0, SWP_NOSIZE | SWP_NOACTIVATE); + } + } + break; + + case WM_SIZE: + if(pThis->m_Status & SS_ENABLED) + { + if(SIZE_MAXIMIZED == wParam || SIZE_MINIMIZED == wParam) + { + ::ShowWindow(pThis->m_hWnd, SW_HIDE); + pThis->m_Status &= ~SS_VISABLE; + } + else if(pThis->m_Status & SS_PARENTVISIBLE) // Parent maybe resized even if invisible + { + // Awful! It seems that if the window size was not decreased + // the window region would never be updated until WM_PAINT was sent. + // So do not Update() until next WM_PAINT is received in this case + if(LOWORD(lParam) > LOWORD(pThis->m_WndSize) || HIWORD(lParam) > HIWORD(pThis->m_WndSize)) + pThis->m_bUpdate = true; + else + pThis->Update(hwnd); + if(!(pThis->m_Status & SS_VISABLE)) + { + ::ShowWindow(pThis->m_hWnd, SW_SHOWNA); + pThis->m_Status |= SS_VISABLE; + } + } + pThis->m_WndSize = lParam; + } + break; + + case WM_PAINT: + { + if(pThis->m_bUpdate) + { + pThis->Update(hwnd); + pThis->m_bUpdate = false; + } + //return hr; + break; + } + + // In some cases of sizing, the up-right corner of the parent window region would not be properly updated + // Update() again when sizing is finished + case WM_EXITSIZEMOVE: + if(pThis->m_Status & SS_VISABLE) + { + pThis->Update(hwnd); + } + break; + + case WM_SHOWWINDOW: + if(pThis->m_Status & SS_ENABLED) + { + if(!wParam) // the window is being hidden + { + ::ShowWindow(pThis->m_hWnd, SW_HIDE); + pThis->m_Status &= ~(SS_VISABLE | SS_PARENTVISIBLE); + } + else if(!(pThis->m_Status & SS_PARENTVISIBLE)) + { + //pThis->Update(hwnd); + pThis->m_bUpdate = true; + ::ShowWindow(pThis->m_hWnd, SW_SHOWNA); + pThis->m_Status |= SS_VISABLE | SS_PARENTVISIBLE; + } + } + break; + + case WM_DESTROY: + DestroyWindow(pThis->m_hWnd); // Destroy the shadow + break; + + case WM_NCDESTROY: + GetShadowMap().erase(hwnd); // Remove this window and shadow from the map + break; + + } + + +#pragma warning(disable: 4312) // temporrarily disable the type_cast warning in Win32 + // Call the default(original) window procedure for other messages or messages processed but not returned + return ((WNDPROC)pThis->m_OriParentProc)(hwnd, uMsg, wParam, lParam); +#pragma warning(default: 4312) + +} + +void CShadowUI::Update(HWND hParent) +{ + + RECT WndRect; + GetWindowRect(hParent, &WndRect); + int nShadWndWid; + int nShadWndHei; + if (m_bIsImageMode) + { + if(m_sShadowImage.IsEmpty()) + return; + + nShadWndWid = WndRect.right - WndRect.left + m_rcShadowCorner.left + m_rcShadowCorner.right; + nShadWndHei = WndRect.bottom - WndRect.top + m_rcShadowCorner.top + m_rcShadowCorner.bottom; + } + else + { + nShadWndWid = WndRect.right - WndRect.left + m_nSize * 2; + nShadWndHei = WndRect.bottom - WndRect.top + m_nSize * 2; + } + + // Create the alpha blending bitmap + BITMAPINFO bmi; // bitmap header + + ZeroMemory(&bmi, sizeof(BITMAPINFO)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = nShadWndWid; + bmi.bmiHeader.biHeight = nShadWndHei; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; // four 8-bit components + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = nShadWndWid * nShadWndHei * 4; + + BYTE *pvBits; // pointer to DIB section + HBITMAP hbitmap = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void **)&pvBits, NULL, 0); + HDC hMemDC = CreateCompatibleDC(NULL); + HBITMAP hOriBmp = (HBITMAP)SelectObject(hMemDC, hbitmap); + + if (m_bIsImageMode) + { + RECT rcPaint = {0, 0, nShadWndWid, nShadWndHei}; + + const TImageInfo* data = m_pManager->GetImageEx((LPCTSTR)m_sShadowImage, NULL, 0); + + if( !data ) + return; + + RECT rcBmpPart = {0}; + rcBmpPart.right = data->nX; + rcBmpPart.bottom = data->nY; + + CRenderEngine::DrawImage(hMemDC, data->hBitmap, rcPaint, rcPaint, rcBmpPart, m_rcShadowCorner, data->alphaChannel, 0xFF, true, false, false); + + } + else + { + ZeroMemory(pvBits, bmi.bmiHeader.biSizeImage); + MakeShadow((UINT32 *)pvBits, hParent, &WndRect); + } + + POINT ptDst; + if (m_bIsImageMode) + { + ptDst.x = WndRect.left - m_rcShadowCorner.left; + ptDst.y = WndRect.top - m_rcShadowCorner.top; + } + else + { + ptDst.x = WndRect.left + m_nxOffset - m_nSize; + ptDst.y = WndRect.top + m_nyOffset - m_nSize; + } + + POINT ptSrc = {0, 0}; + SIZE WndSize = {nShadWndWid, nShadWndHei}; + BLENDFUNCTION blendPixelFunction= { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; + + MoveWindow(m_hWnd, ptDst.x, ptDst.y, nShadWndWid, nShadWndHei, FALSE); + + BOOL bRet= ::UpdateLayeredWindow(m_hWnd, NULL, &ptDst, &WndSize, hMemDC, + &ptSrc, 0, &blendPixelFunction, ULW_ALPHA); + + _ASSERT(bRet); // something was wrong.... + + // Delete used resources + SelectObject(hMemDC, hOriBmp); + DeleteObject(hbitmap); + DeleteDC(hMemDC); + +} + +void CShadowUI::MakeShadow(UINT32 *pShadBits, HWND hParent, RECT *rcParent) +{ + // The shadow algorithm: + // Get the region of parent window, + // Apply morphologic erosion to shrink it into the size (ShadowWndSize - Sharpness) + // Apply modified (with blur effect) morphologic dilation to make the blurred border + // The algorithm is optimized by assuming parent window is just "one piece" and without "wholes" on it + + // Get the region of parent window, + HRGN hParentRgn = CreateRectRgn(0, 0, 0, 0); + GetWindowRgn(hParent, hParentRgn); + + // Determine the Start and end point of each horizontal scan line + SIZE szParent = {rcParent->right - rcParent->left, rcParent->bottom - rcParent->top}; + SIZE szShadow = {szParent.cx + 2 * m_nSize, szParent.cy + 2 * m_nSize}; + // Extra 2 lines (set to be empty) in ptAnchors are used in dilation + int nAnchors = max(szParent.cy, szShadow.cy); // # of anchor points pares + int (*ptAnchors)[2] = new int[nAnchors + 2][2]; + int (*ptAnchorsOri)[2] = new int[szParent.cy][2]; // anchor points, will not modify during erosion + ptAnchors[0][0] = szParent.cx; + ptAnchors[0][1] = 0; + ptAnchors[nAnchors + 1][0] = szParent.cx; + ptAnchors[nAnchors + 1][1] = 0; + if(m_nSize > 0) + { + // Put the parent window anchors at the center + for(int i = 0; i < m_nSize; i++) + { + ptAnchors[i + 1][0] = szParent.cx; + ptAnchors[i + 1][1] = 0; + ptAnchors[szShadow.cy - i][0] = szParent.cx; + ptAnchors[szShadow.cy - i][1] = 0; + } + ptAnchors += m_nSize; + } + for(int i = 0; i < szParent.cy; i++) + { + // find start point + int j; + for(j = 0; j < szParent.cx; j++) + { + if(PtInRegion(hParentRgn, j, i)) + { + ptAnchors[i + 1][0] = j + m_nSize; + ptAnchorsOri[i][0] = j; + break; + } + } + + if(j >= szParent.cx) // Start point not found + { + ptAnchors[i + 1][0] = szParent.cx; + ptAnchorsOri[i][1] = 0; + ptAnchors[i + 1][0] = szParent.cx; + ptAnchorsOri[i][1] = 0; + } + else + { + // find end point + for(j = szParent.cx - 1; j >= ptAnchors[i + 1][0]; j--) + { + if(PtInRegion(hParentRgn, j, i)) + { + ptAnchors[i + 1][1] = j + 1 + m_nSize; + ptAnchorsOri[i][1] = j + 1; + break; + } + } + } + } + + if(m_nSize > 0) + ptAnchors -= m_nSize; // Restore pos of ptAnchors for erosion + int (*ptAnchorsTmp)[2] = new int[nAnchors + 2][2]; // Store the result of erosion + // First and last line should be empty + ptAnchorsTmp[0][0] = szParent.cx; + ptAnchorsTmp[0][1] = 0; + ptAnchorsTmp[nAnchors + 1][0] = szParent.cx; + ptAnchorsTmp[nAnchors + 1][1] = 0; + int nEroTimes = 0; + // morphologic erosion + for(int i = 0; i < m_nSharpness - m_nSize; i++) + { + nEroTimes++; + //ptAnchorsTmp[1][0] = szParent.cx; + //ptAnchorsTmp[1][1] = 0; + //ptAnchorsTmp[szParent.cy + 1][0] = szParent.cx; + //ptAnchorsTmp[szParent.cy + 1][1] = 0; + for(int j = 1; j < nAnchors + 1; j++) + { + ptAnchorsTmp[j][0] = max(ptAnchors[j - 1][0], max(ptAnchors[j][0], ptAnchors[j + 1][0])) + 1; + ptAnchorsTmp[j][1] = min(ptAnchors[j - 1][1], min(ptAnchors[j][1], ptAnchors[j + 1][1])) - 1; + } + // Exchange ptAnchors and ptAnchorsTmp; + int (*ptAnchorsXange)[2] = ptAnchorsTmp; + ptAnchorsTmp = ptAnchors; + ptAnchors = ptAnchorsXange; + } + + // morphologic dilation + ptAnchors += (m_nSize < 0 ? -m_nSize : 0) + 1; // now coordinates in ptAnchors are same as in shadow window + // Generate the kernel + int nKernelSize = m_nSize > m_nSharpness ? m_nSize : m_nSharpness; + int nCenterSize = m_nSize > m_nSharpness ? (m_nSize - m_nSharpness) : 0; + UINT32 *pKernel = new UINT32[(2 * nKernelSize + 1) * (2 * nKernelSize + 1)]; + UINT32 *pKernelIter = pKernel; + for(int i = 0; i <= 2 * nKernelSize; i++) + { + for(int j = 0; j <= 2 * nKernelSize; j++) + { + double dLength = sqrt((i - nKernelSize) * (i - nKernelSize) + (j - nKernelSize) * (double)(j - nKernelSize)); + if(dLength < nCenterSize) + *pKernelIter = m_nDarkness << 24 | PreMultiply(m_Color, m_nDarkness); + else if(dLength <= nKernelSize) + { + UINT32 nFactor = ((UINT32)((1 - (dLength - nCenterSize) / (m_nSharpness + 1)) * m_nDarkness)); + *pKernelIter = nFactor << 24 | PreMultiply(m_Color, nFactor); + } + else + *pKernelIter = 0; + //TRACE("%d ", *pKernelIter >> 24); + pKernelIter ++; + } + //TRACE("\n"); + } + // Generate blurred border + for(int i = nKernelSize; i < szShadow.cy - nKernelSize; i++) + { + int j; + if(ptAnchors[i][0] < ptAnchors[i][1]) + { + + // Start of line + for(j = ptAnchors[i][0]; + j < min(max(ptAnchors[i - 1][0], ptAnchors[i + 1][0]) + 1, ptAnchors[i][1]); + j++) + { + for(int k = 0; k <= 2 * nKernelSize; k++) + { + UINT32 *pPixel = pShadBits + + (szShadow.cy - i - 1 + nKernelSize - k) * szShadow.cx + j - nKernelSize; + UINT32 *pKernelPixel = pKernel + k * (2 * nKernelSize + 1); + for(int l = 0; l <= 2 * nKernelSize; l++) + { + if(*pPixel < *pKernelPixel) + *pPixel = *pKernelPixel; + pPixel++; + pKernelPixel++; + } + } + } // for() start of line + + // End of line + for(j = max(j, min(ptAnchors[i - 1][1], ptAnchors[i + 1][1]) - 1); + j < ptAnchors[i][1]; + j++) + { + for(int k = 0; k <= 2 * nKernelSize; k++) + { + UINT32 *pPixel = pShadBits + + (szShadow.cy - i - 1 + nKernelSize - k) * szShadow.cx + j - nKernelSize; + UINT32 *pKernelPixel = pKernel + k * (2 * nKernelSize + 1); + for(int l = 0; l <= 2 * nKernelSize; l++) + { + if(*pPixel < *pKernelPixel) + *pPixel = *pKernelPixel; + pPixel++; + pKernelPixel++; + } + } + } // for() end of line + + } + } // for() Generate blurred border + + // Erase unwanted parts and complement missing + UINT32 clCenter = m_nDarkness << 24 | PreMultiply(m_Color, m_nDarkness); + for(int i = min(nKernelSize, max(m_nSize - m_nyOffset, 0)); + i < max(szShadow.cy - nKernelSize, min(szParent.cy + m_nSize - m_nyOffset, szParent.cy + 2 * m_nSize)); + i++) + { + UINT32 *pLine = pShadBits + (szShadow.cy - i - 1) * szShadow.cx; + if(i - m_nSize + m_nyOffset < 0 || i - m_nSize + m_nyOffset >= szParent.cy) // Line is not covered by parent window + { + for(int j = ptAnchors[i][0]; j < ptAnchors[i][1]; j++) + { + *(pLine + j) = clCenter; + } + } + else + { + for(int j = ptAnchors[i][0]; + j < min(ptAnchorsOri[i - m_nSize + m_nyOffset][0] + m_nSize - m_nxOffset, ptAnchors[i][1]); + j++) + *(pLine + j) = clCenter; + for(int j = max(ptAnchorsOri[i - m_nSize + m_nyOffset][0] + m_nSize - m_nxOffset, 0); + j < min(ptAnchorsOri[i - m_nSize + m_nyOffset][1] + m_nSize - m_nxOffset, szShadow.cx); + j++) + *(pLine + j) = 0; + for(int j = max(ptAnchorsOri[i - m_nSize + m_nyOffset][1] + m_nSize - m_nxOffset, ptAnchors[i][0]); + j < ptAnchors[i][1]; + j++) + *(pLine + j) = clCenter; + } + } + + // Delete used resources + delete[] (ptAnchors - (m_nSize < 0 ? -m_nSize : 0) - 1); + delete[] ptAnchorsTmp; + delete[] ptAnchorsOri; + delete[] pKernel; + DeleteObject(hParentRgn); +} + +void CShadowUI::ShowShadow(bool bShow) +{ + m_bIsShowShadow = bShow; +} + +bool CShadowUI::IsShowShadow() const +{ + return m_bIsShowShadow; +} + +bool CShadowUI::SetSize(int NewSize) +{ + if(NewSize > 20 || NewSize < -20) + return false; + + m_nSize = (signed char)NewSize; + if(m_hWnd != NULL && (SS_VISABLE & m_Status)) + Update(GetParent(m_hWnd)); + return true; +} + +bool CShadowUI::SetSharpness(unsigned int NewSharpness) +{ + if(NewSharpness > 20) + return false; + + m_nSharpness = (unsigned char)NewSharpness; + if(m_hWnd != NULL && (SS_VISABLE & m_Status)) + Update(GetParent(m_hWnd)); + return true; +} + +bool CShadowUI::SetDarkness(unsigned int NewDarkness) +{ + if(NewDarkness > 255) + return false; + + m_nDarkness = (unsigned char)NewDarkness; + if(m_hWnd != NULL && (SS_VISABLE & m_Status)) + Update(GetParent(m_hWnd)); + return true; +} + +bool CShadowUI::SetPosition(int NewXOffset, int NewYOffset) +{ + if(NewXOffset > 20 || NewXOffset < -20 || + NewYOffset > 20 || NewYOffset < -20) + return false; + + m_nxOffset = (signed char)NewXOffset; + m_nyOffset = (signed char)NewYOffset; + if(m_hWnd != NULL && (SS_VISABLE & m_Status)) + Update(GetParent(m_hWnd)); + return true; +} + +bool CShadowUI::SetColor(COLORREF NewColor) +{ + m_Color = NewColor; + if(m_hWnd != NULL && (SS_VISABLE & m_Status)) + Update(GetParent(m_hWnd)); + return true; +} + +bool CShadowUI::SetImage(LPCTSTR szImage) +{ + if (szImage == NULL) + return false; + + m_bIsImageMode = true; + m_sShadowImage = szImage; + if(m_hWnd != NULL && (SS_VISABLE & m_Status)) + Update(GetParent(m_hWnd)); + + return true; +} + +bool CShadowUI::SetShadowCorner(RECT rcCorner) +{ + if (rcCorner.left < 0 || rcCorner.top < 0 || rcCorner.right < 0 || rcCorner.bottom < 0) + return false; + + m_rcShadowCorner = rcCorner; + + if(m_hWnd != NULL && (SS_VISABLE & m_Status)) + Update(GetParent(m_hWnd)); + + return true; +} + +bool CShadowUI::CopyShadow(CShadowUI* pShadow) +{ + if (m_bIsImageMode) + { + pShadow->SetImage(m_sShadowImage); + pShadow->SetShadowCorner(m_rcShadowCorner); + } + else + { + pShadow->SetSize((int)m_nSize); + pShadow->SetSharpness((unsigned int)m_nSharpness); + pShadow->SetDarkness((unsigned int)m_nDarkness); + pShadow->SetColor(m_Color); + pShadow->SetPosition((int)m_nxOffset, (int)m_nyOffset); + } + return true; +} +} //namespace DuiLib \ No newline at end of file diff --git a/DuiLib/Utils/UIShadow.h b/DuiLib/Utils/UIShadow.h new file mode 100644 index 00000000..c581d97a --- /dev/null +++ b/DuiLib/Utils/UIShadow.h @@ -0,0 +1,106 @@ +#ifndef __UISHADOW_H__ +#define __UISHADOW_H__ + +#pragma once + +#include + +namespace DuiLib +{ + class UILIB_API CShadowUI + { + public: + friend class CPaintManagerUI; + + CShadowUI(void); + virtual ~CShadowUI(void); + + public: + // bShowΪʱŻᴴӰ + void ShowShadow(bool bShow); + bool IsShowShadow() const; + + // 㷨Ӱĺ + bool SetSize(int NewSize = 0); + bool SetSharpness(unsigned int NewSharpness = 5); + bool SetDarkness(unsigned int NewDarkness = 200); + bool SetPosition(int NewXOffset = 5, int NewYOffset = 5); + bool SetColor(COLORREF NewColor = 0); + + // ͼƬӰĺ + bool SetImage(LPCTSTR szImage); + bool SetShadowCorner(RECT rcCorner); // ŹʽӰ + + // ԼӰʽƵ + bool CopyShadow(CShadowUI* pShadow); + + // Ӱ壬CPaintManagerUIԶ,ԼҪӰ + void Create(CPaintManagerUI* pPaintManager); + protected: + + // ʼעӰ + static bool Initialize(HINSTANCE hInstance); + + // ѾӵĴӰ,ParentProc()ͨõӰ + static std::map& GetShadowMap(); + + // ໯ + static LRESULT CALLBACK ParentProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + // ıСƶػӰʱ + void Update(HWND hParent); + + // ͨ㷨Ӱ + void MakeShadow(UINT32 *pShadBits, HWND hParent, RECT *rcParent); + + // alphaԤֵ + inline DWORD PreMultiply(COLORREF cl, unsigned char nAlpha) + { + return (GetRValue(cl) * (DWORD)nAlpha / 255) | + (GetGValue(cl) * (DWORD)nAlpha / 255) << 8 | + (GetBValue(cl) * (DWORD)nAlpha / 255) << 16; + } + + protected: + enum ShadowStatus + { + SS_ENABLED = 1, // Shadow is enabled, if not, the following one is always false + SS_VISABLE = 1 << 1, // Shadow window is visible + SS_PARENTVISIBLE = 1 << 2 // Parent window is visible, if not, the above one is always false + }; + + + static bool s_bHasInit; + + CPaintManagerUI *m_pManager; // CPaintManagerUIȡزԴ͸ + HWND m_hWnd; // Ӱľ + LONG_PTR m_OriParentProc; // ໯ + BYTE m_Status; + bool m_bIsImageMode; // ǷΪͼƬӰģʽ + bool m_bIsShowShadow; // ǷҪʾӰ + + // 㷨ӰԱ + unsigned char m_nDarkness; // Darkness, transparency of blurred area + unsigned char m_nSharpness; // Sharpness, width of blurred border of shadow window + signed char m_nSize; // Shadow window size, relative to parent window size + + // The X and Y offsets of shadow window, + // relative to the parent window, at center of both windows (not top-left corner), signed + signed char m_nxOffset; + signed char m_nyOffset; + + // Restore last parent window size, used to determine the update strategy when parent window is resized + LPARAM m_WndSize; + + // Set this to true if the shadow should not be update until next WM_PAINT is received + bool m_bUpdate; + + COLORREF m_Color; // Color of shadow + + // ͼƬӰԱ + CDuiString m_sShadowImage; + RECT m_rcShadowCorner; + }; +} + +#endif //__UISHADOW_H__ \ No newline at end of file diff --git a/DuiLib/Utils/Utils.cpp b/DuiLib/Utils/Utils.cpp new file mode 100644 index 00000000..7579ca09 --- /dev/null +++ b/DuiLib/Utils/Utils.cpp @@ -0,0 +1,1006 @@ +#include "stdafx.h" +#include "Utils.h" + +namespace DuiLib +{ + + ///////////////////////////////////////////////////////////////////////////////////// + // + // + + CDuiPoint::CDuiPoint() + { + x = y = 0; + } + + CDuiPoint::CDuiPoint(const POINT& src) + { + x = src.x; + y = src.y; + } + + CDuiPoint::CDuiPoint(int _x, int _y) + { + x = _x; + y = _y; + } + + CDuiPoint::CDuiPoint(LPARAM lParam) + { + x = GET_X_LPARAM(lParam); + y = GET_Y_LPARAM(lParam); + } + + + ///////////////////////////////////////////////////////////////////////////////////// + // + // + + CDuiSize::CDuiSize() + { + cx = cy = 0; + } + + CDuiSize::CDuiSize(const SIZE& src) + { + cx = src.cx; + cy = src.cy; + } + + CDuiSize::CDuiSize(const RECT rc) + { + cx = rc.right - rc.left; + cy = rc.bottom - rc.top; + } + + CDuiSize::CDuiSize(int _cx, int _cy) + { + cx = _cx; + cy = _cy; + } + + + ///////////////////////////////////////////////////////////////////////////////////// + // + // + + CDuiRect::CDuiRect() + { + left = top = right = bottom = 0; + } + + CDuiRect::CDuiRect(const RECT& src) + { + left = src.left; + top = src.top; + right = src.right; + bottom = src.bottom; + } + + CDuiRect::CDuiRect(int iLeft, int iTop, int iRight, int iBottom) + { + left = iLeft; + top = iTop; + right = iRight; + bottom = iBottom; + } + + int CDuiRect::GetWidth() const + { + return right - left; + } + + int CDuiRect::GetHeight() const + { + return bottom - top; + } + + void CDuiRect::Empty() + { + left = top = right = bottom = 0; + } + + bool CDuiRect::IsNull() const + { + return (left == 0 && right == 0 && top == 0 && bottom == 0); + } + + void CDuiRect::Join(const RECT& rc) + { + if( rc.left < left ) left = rc.left; + if( rc.top < top ) top = rc.top; + if( rc.right > right ) right = rc.right; + if( rc.bottom > bottom ) bottom = rc.bottom; + } + + void CDuiRect::ResetOffset() + { + ::OffsetRect(this, -left, -top); + } + + void CDuiRect::Normalize() + { + if( left > right ) { int iTemp = left; left = right; right = iTemp; } + if( top > bottom ) { int iTemp = top; top = bottom; bottom = iTemp; } + } + + void CDuiRect::Offset(int cx, int cy) + { + ::OffsetRect(this, cx, cy); + } + + void CDuiRect::Inflate(int cx, int cy) + { + ::InflateRect(this, cx, cy); + } + + void CDuiRect::Deflate(int cx, int cy) + { + ::InflateRect(this, -cx, -cy); + } + + void CDuiRect::Union(CDuiRect& rc) + { + ::UnionRect(this, this, &rc); + } + + + ///////////////////////////////////////////////////////////////////////////////////// + // + // + + CStdPtrArray::CStdPtrArray(int iPreallocSize) : m_ppVoid(NULL), m_nCount(0), m_nAllocated(iPreallocSize) + { + ASSERT(iPreallocSize>=0); + if( iPreallocSize > 0 ) m_ppVoid = static_cast(malloc(iPreallocSize * sizeof(LPVOID))); + } + + CStdPtrArray::CStdPtrArray(const CStdPtrArray& src) : m_ppVoid(NULL), m_nCount(0), m_nAllocated(0) + { + for(int i=0; i(malloc(iSize * sizeof(LPVOID))); + ::ZeroMemory(m_ppVoid, iSize * sizeof(LPVOID)); + m_nAllocated = iSize; + m_nCount = iSize; + } + + bool CStdPtrArray::IsEmpty() const + { + return m_nCount == 0; + } + + bool CStdPtrArray::Add(LPVOID pData) + { + if( ++m_nCount >= m_nAllocated) { + int nAllocated = m_nAllocated * 2; + if( nAllocated == 0 ) nAllocated = 11; + LPVOID* ppVoid = static_cast(realloc(m_ppVoid, nAllocated * sizeof(LPVOID))); + if( ppVoid != NULL ) { + m_nAllocated = nAllocated; + m_ppVoid = ppVoid; + } + else { + --m_nCount; + return false; + } + } + m_ppVoid[m_nCount - 1] = pData; + return true; + } + + bool CStdPtrArray::InsertAt(int iIndex, LPVOID pData) + { + if( iIndex == m_nCount ) return Add(pData); + if( iIndex < 0 || iIndex > m_nCount ) return false; + if( ++m_nCount >= m_nAllocated) { + int nAllocated = m_nAllocated * 2; + if( nAllocated == 0 ) nAllocated = 11; + LPVOID* ppVoid = static_cast(realloc(m_ppVoid, nAllocated * sizeof(LPVOID))); + if( ppVoid != NULL ) { + m_nAllocated = nAllocated; + m_ppVoid = ppVoid; + } + else { + --m_nCount; + return false; + } + } + memmove(&m_ppVoid[iIndex + 1], &m_ppVoid[iIndex], (m_nCount - iIndex - 1) * sizeof(LPVOID)); + m_ppVoid[iIndex] = pData; + return true; + } + + bool CStdPtrArray::SetAt(int iIndex, LPVOID pData) + { + if( iIndex < 0 || iIndex >= m_nCount ) return false; + m_ppVoid[iIndex] = pData; + return true; + } + + bool CStdPtrArray::Remove(int iIndex) + { + if( iIndex < 0 || iIndex >= m_nCount ) return false; + if( iIndex < --m_nCount ) ::CopyMemory(m_ppVoid + iIndex, m_ppVoid + iIndex + 1, (m_nCount - iIndex) * sizeof(LPVOID)); + return true; + } + + int CStdPtrArray::Find(LPVOID pData) const + { + for( int i = 0; i < m_nCount; i++ ) if( m_ppVoid[i] == pData ) return i; + return -1; + } + + int CStdPtrArray::GetSize() const + { + return m_nCount; + } + + LPVOID* CStdPtrArray::GetData() + { + return m_ppVoid; + } + + LPVOID CStdPtrArray::GetAt(int iIndex) const + { + if( iIndex < 0 || iIndex >= m_nCount ) return NULL; + return m_ppVoid[iIndex]; + } + + LPVOID CStdPtrArray::operator[] (int iIndex) const + { + ASSERT(iIndex>=0 && iIndex0); + ASSERT(iPreallocSize>=0); + if( iPreallocSize > 0 ) m_pVoid = static_cast(malloc(iPreallocSize * m_iElementSize)); + } + + CStdValArray::~CStdValArray() + { + if( m_pVoid != NULL ) free(m_pVoid); + } + + void CStdValArray::Empty() + { + m_nCount = 0; // NOTE: We keep the memory in place + } + + bool CStdValArray::IsEmpty() const + { + return m_nCount == 0; + } + + bool CStdValArray::Add(LPCVOID pData) + { + if( ++m_nCount >= m_nAllocated) { + int nAllocated = m_nAllocated * 2; + if( nAllocated == 0 ) nAllocated = 11; + LPBYTE pVoid = static_cast(realloc(m_pVoid, nAllocated * m_iElementSize)); + if( pVoid != NULL ) { + m_nAllocated = nAllocated; + m_pVoid = pVoid; + } + else { + --m_nCount; + return false; + } + } + ::CopyMemory(m_pVoid + ((m_nCount - 1) * m_iElementSize), pData, m_iElementSize); + return true; + } + + bool CStdValArray::Remove(int iIndex) + { + if( iIndex < 0 || iIndex >= m_nCount ) return false; + if( iIndex < --m_nCount ) ::CopyMemory(m_pVoid + (iIndex * m_iElementSize), m_pVoid + ((iIndex + 1) * m_iElementSize), (m_nCount - iIndex) * m_iElementSize); + return true; + } + + int CStdValArray::GetSize() const + { + return m_nCount; + } + + LPVOID CStdValArray::GetData() + { + return static_cast(m_pVoid); + } + + LPVOID CStdValArray::GetAt(int iIndex) const + { + if( iIndex < 0 || iIndex >= m_nCount ) return NULL; + return m_pVoid + (iIndex * m_iElementSize); + } + + LPVOID CStdValArray::operator[] (int iIndex) const + { + ASSERT(iIndex>=0 && iIndex= MAX_LOCAL_STRING_LEN ) { + if( m_pstr == m_szBuffer ) { + m_pstr = static_cast(malloc((nNewLength + 1) * sizeof(TCHAR))); + _tcscpy(m_pstr, m_szBuffer); + _tcscat(m_pstr, pstr); + } + else { + m_pstr = static_cast(realloc(m_pstr, (nNewLength + 1) * sizeof(TCHAR))); + _tcscat(m_pstr, pstr); + } + } + else { + if( m_pstr != m_szBuffer ) { + free(m_pstr); + m_pstr = m_szBuffer; + } + _tcscat(m_szBuffer, pstr); + } + } + + void CDuiString::Assign(LPCTSTR pstr, int cchMax) + { + if( pstr == NULL ) pstr = _T(""); + cchMax = (cchMax < 0 ? (int) _tcslen(pstr) : cchMax); + if( cchMax < MAX_LOCAL_STRING_LEN ) { + if( m_pstr != m_szBuffer ) { + free(m_pstr); + m_pstr = m_szBuffer; + } + } + else if( cchMax > GetLength() || m_pstr == m_szBuffer ) { + if( m_pstr == m_szBuffer ) m_pstr = NULL; + m_pstr = static_cast(realloc(m_pstr, (cchMax + 1) * sizeof(TCHAR))); + } + _tcsncpy(m_pstr, pstr, cchMax); + m_pstr[cchMax] = '\0'; + } + + bool CDuiString::IsEmpty() const + { + return m_pstr[0] == '\0'; + } + + void CDuiString::Empty() + { + if( m_pstr != m_szBuffer ) free(m_pstr); + m_pstr = m_szBuffer; + m_szBuffer[0] = '\0'; + } + + LPCTSTR CDuiString::GetData() const + { + return m_pstr; + } + + TCHAR CDuiString::GetAt(int nIndex) const + { + return m_pstr[nIndex]; + } + + TCHAR CDuiString::operator[] (int nIndex) const + { + return m_pstr[nIndex]; + } + + const CDuiString& CDuiString::operator=(const CDuiString& src) + { + Assign(src); + return *this; + } + + const CDuiString& CDuiString::operator=(LPCTSTR lpStr) + { + if ( lpStr ) + { + ASSERT(!::IsBadStringPtr(lpStr,-1)); + Assign(lpStr); + } + else + { + Empty(); + } + return *this; + } + +#ifdef _UNICODE + + const CDuiString& CDuiString::operator=(LPCSTR lpStr) + { + if ( lpStr ) + { + ASSERT(!::IsBadStringPtrA(lpStr,-1)); + int cchStr = (int) strlen(lpStr) + 1; + LPWSTR pwstr = (LPWSTR) _alloca(cchStr); + if( pwstr != NULL ) ::MultiByteToWideChar(::GetACP(), 0, lpStr, -1, pwstr, cchStr) ; + Assign(pwstr); + } + else + { + Empty(); + } + return *this; + } + + const CDuiString& CDuiString::operator+=(LPCSTR lpStr) + { + if ( lpStr ) + { + ASSERT(!::IsBadStringPtrA(lpStr,-1)); + int cchStr = (int) strlen(lpStr) + 1; + LPWSTR pwstr = (LPWSTR) _alloca(cchStr); + if( pwstr != NULL ) ::MultiByteToWideChar(::GetACP(), 0, lpStr, -1, pwstr, cchStr) ; + Append(pwstr); + } + + return *this; + } + +#else + + const CDuiString& CDuiString::operator=(LPCWSTR lpwStr) + { + if ( lpwStr ) + { + ASSERT(!::IsBadStringPtrW(lpwStr,-1)); + int cchStr = ((int) wcslen(lpwStr) * 2) + 1; + LPSTR pstr = (LPSTR) _alloca(cchStr); + if( pstr != NULL ) ::WideCharToMultiByte(::GetACP(), 0, lpwStr, -1, pstr, cchStr, NULL, NULL); + Assign(pstr); + } + else + { + Empty(); + } + + return *this; + } + + const CDuiString& CDuiString::operator+=(LPCWSTR lpwStr) + { + if ( lpwStr ) + { + ASSERT(!::IsBadStringPtrW(lpwStr,-1)); + int cchStr = ((int) wcslen(lpwStr) * 2) + 1; + LPSTR pstr = (LPSTR) _alloca(cchStr); + if( pstr != NULL ) ::WideCharToMultiByte(::GetACP(), 0, lpwStr, -1, pstr, cchStr, NULL, NULL); + Append(pstr); + } + + return *this; + } + +#endif // _UNICODE + + const CDuiString& CDuiString::operator=(const TCHAR ch) + { + Empty(); + m_szBuffer[0] = ch; + m_szBuffer[1] = '\0'; + return *this; + } + + CDuiString CDuiString::operator+(const CDuiString& src) const + { + CDuiString sTemp = *this; + sTemp.Append(src); + return sTemp; + } + + CDuiString CDuiString::operator+(LPCTSTR lpStr) const + { + if ( lpStr ) + { + ASSERT(!::IsBadStringPtr(lpStr,-1)); + CDuiString sTemp = *this; + sTemp.Append(lpStr); + return sTemp; + } + + return *this; + } + + const CDuiString& CDuiString::operator+=(const CDuiString& src) + { + Append(src); + return *this; + } + + const CDuiString& CDuiString::operator+=(LPCTSTR lpStr) + { + if ( lpStr ) + { + ASSERT(!::IsBadStringPtr(lpStr,-1)); + Append(lpStr); + } + + return *this; + } + + const CDuiString& CDuiString::operator+=(const TCHAR ch) + { + TCHAR str[] = { ch, '\0' }; + Append(str); + return *this; + } + + bool CDuiString::operator == (LPCTSTR str) const { return (Compare(str) == 0); }; + bool CDuiString::operator != (LPCTSTR str) const { return (Compare(str) != 0); }; + bool CDuiString::operator <= (LPCTSTR str) const { return (Compare(str) <= 0); }; + bool CDuiString::operator < (LPCTSTR str) const { return (Compare(str) < 0); }; + bool CDuiString::operator >= (LPCTSTR str) const { return (Compare(str) >= 0); }; + bool CDuiString::operator > (LPCTSTR str) const { return (Compare(str) > 0); }; + + void CDuiString::SetAt(int nIndex, TCHAR ch) + { + ASSERT(nIndex>=0 && nIndex GetLength() ) iLength = GetLength(); + return CDuiString(m_pstr, iLength); + } + + CDuiString CDuiString::Mid(int iPos, int iLength) const + { + if( iLength < 0 ) iLength = GetLength() - iPos; + if( iPos + iLength > GetLength() ) iLength = GetLength() - iPos; + if( iLength <= 0 ) return CDuiString(); + return CDuiString(m_pstr + iPos, iLength); + } + + CDuiString CDuiString::Right(int iLength) const + { + int iPos = GetLength() - iLength; + if( iPos < 0 ) { + iPos = 0; + iLength = GetLength(); + } + return CDuiString(m_pstr + iPos, iLength); + } + + int CDuiString::Find(TCHAR ch, int iPos /*= 0*/) const + { + ASSERT(iPos>=0 && iPos<=GetLength()); + if( iPos != 0 && (iPos < 0 || iPos >= GetLength()) ) return -1; + LPCTSTR p = _tcschr(m_pstr + iPos, ch); + if( p == NULL ) return -1; + return (int)(p - m_pstr); + } + + int CDuiString::Find(LPCTSTR pstrSub, int iPos /*= 0*/) const + { + ASSERT(!::IsBadStringPtr(pstrSub,-1)); + ASSERT(iPos>=0 && iPos<=GetLength()); + if( iPos != 0 && (iPos < 0 || iPos > GetLength()) ) return -1; + LPCTSTR p = _tcsstr(m_pstr + iPos, pstrSub); + if( p == NULL ) return -1; + return (int)(p - m_pstr); + } + + int CDuiString::ReverseFind(TCHAR ch) const + { + LPCTSTR p = _tcsrchr(m_pstr, ch); + if( p == NULL ) return -1; + return (int)(p - m_pstr); + } + + int CDuiString::Replace(LPCTSTR pstrFrom, LPCTSTR pstrTo) + { + CDuiString sTemp; + int nCount = 0; + int iPos = Find(pstrFrom); + if( iPos < 0 ) return 0; + int cchFrom = (int) _tcslen(pstrFrom); + int cchTo = (int) _tcslen(pstrTo); + while( iPos >= 0 ) { + sTemp = Left(iPos); + sTemp += pstrTo; + sTemp += Mid(iPos + cchFrom); + Assign(sTemp); + iPos = Find(pstrFrom, iPos + cchTo); + nCount++; + } + return nCount; + } + + int CDuiString::Format(LPCTSTR pstrFormat, va_list Args) + { +#if _MSC_VER <= 1400 + + TCHAR *szBuffer = NULL; + int size = 512, nLen, counts; + + // + // allocate with init size + // + + szBuffer = (TCHAR*)malloc(size); + ZeroMemory(szBuffer, size); + + while (TRUE){ + counts = size / sizeof(TCHAR); + nLen = _vsntprintf (szBuffer, counts, pstrFormat, Args); + if (nLen != -1 && nLen < counts){ + break; + } + + // + // expand the buffer. + // + + if (nLen == -1){ + size *= 2; + }else{ + size += 1 * sizeof(TCHAR); + } + + + // + // realloc the buffer. + // + + if ((szBuffer = (TCHAR*)realloc(szBuffer, size)) != NULL){ + ZeroMemory(szBuffer, size); + }else{ + break; + } + + } + + Assign(szBuffer); + free(szBuffer); + return nLen; +#else + int nLen, totalLen; + TCHAR *szBuffer; + + nLen = _vsntprintf(NULL, 0, pstrFormat, Args); + totalLen = (nLen + 1)*sizeof(TCHAR); + szBuffer = (TCHAR*)malloc(totalLen); + ZeroMemory(szBuffer, totalLen); + nLen = _vsntprintf(szBuffer, nLen + 1, pstrFormat, Args); + + Assign(szBuffer); + free(szBuffer); + + return nLen; + +#endif + } + + int CDuiString::Format(LPCTSTR pstrFormat, ...) + { + int nRet; + va_list Args; + + va_start(Args, pstrFormat); + nRet = Format(pstrFormat, Args); + va_end(Args); + + return nRet; + + } + + int CDuiString::SmallFormat(LPCTSTR pstrFormat, ...) + { + CDuiString sFormat = pstrFormat; + TCHAR szBuffer[64] = { 0 }; + va_list argList; + va_start(argList, pstrFormat); + int iRet = ::_vsntprintf(szBuffer, sizeof(szBuffer), sFormat, argList); + va_end(argList); + Assign(szBuffer); + return iRet; + } + + ///////////////////////////////////////////////////////////////////////////// + // + // + + static UINT HashKey(LPCTSTR Key) + { + UINT i = 0; + SIZE_T len = _tcslen(Key); + while( len-- > 0 ) i = (i << 5) + i + Key[len]; + return i; + } + + static UINT HashKey(const CDuiString& Key) + { + return HashKey((LPCTSTR)Key); + }; + + CStdStringPtrMap::CStdStringPtrMap(int nSize) : m_nCount(0) + { + if( nSize < 16 ) nSize = 16; + m_nBuckets = nSize; + m_aT = new TITEM*[nSize]; + memset(m_aT, 0, nSize * sizeof(TITEM*)); + } + + CStdStringPtrMap::~CStdStringPtrMap() + { + if( m_aT ) { + int len = m_nBuckets; + while( len-- ) { + TITEM* pItem = m_aT[len]; + while( pItem ) { + TITEM* pKill = pItem; + pItem = pItem->pNext; + delete pKill; + } + } + delete [] m_aT; + m_aT = NULL; + } + } + + void CStdStringPtrMap::RemoveAll() + { + this->Resize(m_nBuckets); + } + + void CStdStringPtrMap::Resize(int nSize) + { + if( m_aT ) { + int len = m_nBuckets; + while( len-- ) { + TITEM* pItem = m_aT[len]; + while( pItem ) { + TITEM* pKill = pItem; + pItem = pItem->pNext; + delete pKill; + } + } + delete [] m_aT; + m_aT = NULL; + } + + if( nSize < 0 ) nSize = 0; + if( nSize > 0 ) { + m_aT = new TITEM*[nSize]; + memset(m_aT, 0, nSize * sizeof(TITEM*)); + } + m_nBuckets = nSize; + m_nCount = 0; + } + + LPVOID CStdStringPtrMap::Find(LPCTSTR key, bool optimize) const + { + if( m_nBuckets == 0 || GetSize() == 0 ) return NULL; + + UINT slot = HashKey(key) % m_nBuckets; + for( TITEM* pItem = m_aT[slot]; pItem; pItem = pItem->pNext ) { + if( pItem->Key == key ) { + if (optimize && pItem != m_aT[slot]) { + if (pItem->pNext) { + pItem->pNext->pPrev = pItem->pPrev; + } + pItem->pPrev->pNext = pItem->pNext; + pItem->pPrev = NULL; + pItem->pNext = m_aT[slot]; + pItem->pNext->pPrev = pItem; + //itemƶͷ + m_aT[slot] = pItem; + } + return pItem->Data; + } + } + + return NULL; + } + + bool CStdStringPtrMap::Insert(LPCTSTR key, LPVOID pData) + { + if( m_nBuckets == 0 ) return false; + if( Find(key) ) return false; + + // Add first in bucket + UINT slot = HashKey(key) % m_nBuckets; + TITEM* pItem = new TITEM; + pItem->Key = key; + pItem->Data = pData; + pItem->pPrev = NULL; + pItem->pNext = m_aT[slot]; + if (pItem->pNext) + pItem->pNext->pPrev = pItem; + m_aT[slot] = pItem; + m_nCount++; + return true; + } + + LPVOID CStdStringPtrMap::Set(LPCTSTR key, LPVOID pData) + { + if( m_nBuckets == 0 ) return pData; + + if (GetSize()>0) { + UINT slot = HashKey(key) % m_nBuckets; + // Modify existing item + for( TITEM* pItem = m_aT[slot]; pItem; pItem = pItem->pNext ) { + if( pItem->Key == key ) { + LPVOID pOldData = pItem->Data; + pItem->Data = pData; + return pOldData; + } + } + } + + Insert(key, pData); + return NULL; + } + + bool CStdStringPtrMap::Remove(LPCTSTR key) + { + if( m_nBuckets == 0 || GetSize() == 0 ) return false; + + UINT slot = HashKey(key) % m_nBuckets; + TITEM** ppItem = &m_aT[slot]; + while( *ppItem ) { + if( (*ppItem)->Key == key ) { + TITEM* pKill = *ppItem; + *ppItem = (*ppItem)->pNext; + if (*ppItem) + (*ppItem)->pPrev = pKill->pPrev; + delete pKill; + m_nCount--; + return true; + } + ppItem = &((*ppItem)->pNext); + } + + return false; + } + + int CStdStringPtrMap::GetSize() const + { +#if 0//def _DEBUG + int nCount = 0; + int len = m_nBuckets; + while( len-- ) { + for( const TITEM* pItem = m_aT[len]; pItem; pItem = pItem->pNext ) nCount++; + } + ASSERT(m_nCount==nCount); +#endif + return m_nCount; + } + + LPCTSTR CStdStringPtrMap::GetAt(int iIndex) const + { + if( m_nBuckets == 0 || GetSize() == 0 ) return false; + + int pos = 0; + int len = m_nBuckets; + while( len-- ) { + for( TITEM* pItem = m_aT[len]; pItem; pItem = pItem->pNext ) { + if( pos++ == iIndex ) { + return pItem->Key.GetData(); + } + } + } + + return NULL; + } + + LPCTSTR CStdStringPtrMap::operator[] (int nIndex) const + { + return GetAt(nIndex); + } + + + ///////////////////////////////////////////////////////////////////////////////////// + // + // + + CWaitCursor::CWaitCursor() + { + m_hOrigCursor = ::SetCursor(::LoadCursor(NULL, IDC_WAIT)); + } + + CWaitCursor::~CWaitCursor() + { + ::SetCursor(m_hOrigCursor); + } + +} // namespace DuiLib \ No newline at end of file diff --git a/DuiLib/Utils/Utils.h b/DuiLib/Utils/Utils.h new file mode 100644 index 00000000..dcb75c3c --- /dev/null +++ b/DuiLib/Utils/Utils.h @@ -0,0 +1,291 @@ +#ifndef __UTILS_H__ +#define __UTILS_H__ + +#pragma once +#include "OAIdl.h" +namespace DuiLib +{ + ///////////////////////////////////////////////////////////////////////////////////// + // + + class UILIB_API STRINGorID + { + public: + STRINGorID(LPCTSTR lpString) : m_lpstr(lpString) + { } + STRINGorID(UINT nID) : m_lpstr(MAKEINTRESOURCE(nID)) + { } + LPCTSTR m_lpstr; + }; + + ///////////////////////////////////////////////////////////////////////////////////// + // + + class UILIB_API CDuiPoint : public tagPOINT + { + public: + CDuiPoint(); + CDuiPoint(const POINT& src); + CDuiPoint(int x, int y); + CDuiPoint(LPARAM lParam); + }; + + + ///////////////////////////////////////////////////////////////////////////////////// + // + + class UILIB_API CDuiSize : public tagSIZE + { + public: + CDuiSize(); + CDuiSize(const SIZE& src); + CDuiSize(const RECT rc); + CDuiSize(int cx, int cy); + }; + + + ///////////////////////////////////////////////////////////////////////////////////// + // + + class UILIB_API CDuiRect : public tagRECT + { + public: + CDuiRect(); + CDuiRect(const RECT& src); + CDuiRect(int iLeft, int iTop, int iRight, int iBottom); + + int GetWidth() const; + int GetHeight() const; + void Empty(); + bool IsNull() const; + void Join(const RECT& rc); + void ResetOffset(); + void Normalize(); + void Offset(int cx, int cy); + void Inflate(int cx, int cy); + void Deflate(int cx, int cy); + void Union(CDuiRect& rc); + }; + + ///////////////////////////////////////////////////////////////////////////////////// + // + + class UILIB_API CStdPtrArray + { + public: + CStdPtrArray(int iPreallocSize = 0); + CStdPtrArray(const CStdPtrArray& src); + ~CStdPtrArray(); + + void Empty(); + void Resize(int iSize); + bool IsEmpty() const; + int Find(LPVOID iIndex) const; + bool Add(LPVOID pData); + bool SetAt(int iIndex, LPVOID pData); + bool InsertAt(int iIndex, LPVOID pData); + bool Remove(int iIndex); + int GetSize() const; + LPVOID* GetData(); + + LPVOID GetAt(int iIndex) const; + LPVOID operator[] (int nIndex) const; + + protected: + LPVOID* m_ppVoid; + int m_nCount; + int m_nAllocated; + }; + + + ///////////////////////////////////////////////////////////////////////////////////// + // + + class UILIB_API CStdValArray + { + public: + CStdValArray(int iElementSize, int iPreallocSize = 0); + ~CStdValArray(); + + void Empty(); + bool IsEmpty() const; + bool Add(LPCVOID pData); + bool Remove(int iIndex); + int GetSize() const; + LPVOID GetData(); + + LPVOID GetAt(int iIndex) const; + LPVOID operator[] (int nIndex) const; + + protected: + LPBYTE m_pVoid; + int m_iElementSize; + int m_nCount; + int m_nAllocated; + }; + + + ///////////////////////////////////////////////////////////////////////////////////// + // + + class UILIB_API CDuiString + { + public: + enum { MAX_LOCAL_STRING_LEN = 63 }; + + CDuiString(); + CDuiString(const TCHAR ch); + CDuiString(const CDuiString& src); + CDuiString(LPCTSTR lpsz, int nLen = -1); + ~CDuiString(); + + void Empty(); + int GetLength() const; + bool IsEmpty() const; + TCHAR GetAt(int nIndex) const; + void Append(LPCTSTR pstr); + void Assign(LPCTSTR pstr, int nLength = -1); + LPCTSTR GetData() const; + + void SetAt(int nIndex, TCHAR ch); + operator LPCTSTR() const; + + TCHAR operator[] (int nIndex) const; + const CDuiString& operator=(const CDuiString& src); + const CDuiString& operator=(const TCHAR ch); + const CDuiString& operator=(LPCTSTR pstr); +#ifdef _UNICODE + const CDuiString& CDuiString::operator=(LPCSTR lpStr); + const CDuiString& CDuiString::operator+=(LPCSTR lpStr); +#else + const CDuiString& CDuiString::operator=(LPCWSTR lpwStr); + const CDuiString& CDuiString::operator+=(LPCWSTR lpwStr); +#endif + CDuiString operator+(const CDuiString& src) const; + CDuiString operator+(LPCTSTR pstr) const; + const CDuiString& operator+=(const CDuiString& src); + const CDuiString& operator+=(LPCTSTR pstr); + const CDuiString& operator+=(const TCHAR ch); + + bool operator == (LPCTSTR str) const; + bool operator != (LPCTSTR str) const; + bool operator <= (LPCTSTR str) const; + bool operator < (LPCTSTR str) const; + bool operator >= (LPCTSTR str) const; + bool operator > (LPCTSTR str) const; + + int Compare(LPCTSTR pstr) const; + int CompareNoCase(LPCTSTR pstr) const; + + void MakeUpper(); + void MakeLower(); + + CDuiString Left(int nLength) const; + CDuiString Mid(int iPos, int nLength = -1) const; + CDuiString Right(int nLength) const; + + int Find(TCHAR ch, int iPos = 0) const; + int Find(LPCTSTR pstr, int iPos = 0) const; + int ReverseFind(TCHAR ch) const; + int Replace(LPCTSTR pstrFrom, LPCTSTR pstrTo); + + int __cdecl Format(LPCTSTR pstrFormat, ...); + int __cdecl Format(LPCTSTR pstrFormat, va_list Args); + int __cdecl SmallFormat(LPCTSTR pstrFormat, ...); + + protected: + LPTSTR m_pstr; + TCHAR m_szBuffer[MAX_LOCAL_STRING_LEN + 1]; + }; + + + ///////////////////////////////////////////////////////////////////////////////////// + // + + struct TITEM + { + CDuiString Key; + LPVOID Data; + struct TITEM* pPrev; + struct TITEM* pNext; + }; + + class UILIB_API CStdStringPtrMap + { + public: + CStdStringPtrMap(int nSize = 83); + ~CStdStringPtrMap(); + + void Resize(int nSize = 83); + LPVOID Find(LPCTSTR key, bool optimize = true) const; + bool Insert(LPCTSTR key, LPVOID pData); + LPVOID Set(LPCTSTR key, LPVOID pData); + bool Remove(LPCTSTR key); + void RemoveAll(); + int GetSize() const; + LPCTSTR GetAt(int iIndex) const; + LPCTSTR operator[] (int nIndex) const; + + protected: + TITEM** m_aT; + int m_nBuckets; + int m_nCount; + }; + + ///////////////////////////////////////////////////////////////////////////////////// + // + + class UILIB_API CWaitCursor + { + public: + CWaitCursor(); + ~CWaitCursor(); + + protected: + HCURSOR m_hOrigCursor; + }; + + ///////////////////////////////////////////////////////////////////////////////////// + // + + class CDuiVariant : public VARIANT + { + public: + CDuiVariant() + { + VariantInit(this); + } + CDuiVariant(int i) + { + VariantInit(this); + this->vt = VT_I4; + this->intVal = i; + } + CDuiVariant(float f) + { + VariantInit(this); + this->vt = VT_R4; + this->fltVal = f; + } + CDuiVariant(LPOLESTR s) + { + VariantInit(this); + this->vt = VT_BSTR; + this->bstrVal = s; + } + CDuiVariant(IDispatch *disp) + { + VariantInit(this); + this->vt = VT_DISPATCH; + this->pdispVal = disp; + } + + ~CDuiVariant() + { + VariantClear(this); + } + }; + +}// namespace DuiLib + +#endif // __UTILS_H__ \ No newline at end of file diff --git a/DuiLib/Utils/WebBrowserEventHandler.h b/DuiLib/Utils/WebBrowserEventHandler.h new file mode 100644 index 00000000..d40ad96a --- /dev/null +++ b/DuiLib/Utils/WebBrowserEventHandler.h @@ -0,0 +1,155 @@ +#pragma once +#include +#include +#include + +namespace DuiLib +{ + class CWebBrowserEventHandler + { + public: + CWebBrowserEventHandler() {} + ~CWebBrowserEventHandler() {} + + virtual void BeforeNavigate2( IDispatch *pDisp,VARIANT *&url,VARIANT *&Flags,VARIANT *&TargetFrameName,VARIANT *&PostData,VARIANT *&Headers,VARIANT_BOOL *&Cancel ) {} + virtual void NavigateError(IDispatch *pDisp,VARIANT * &url,VARIANT *&TargetFrameName,VARIANT *&StatusCode,VARIANT_BOOL *&Cancel) {} + virtual void NavigateComplete2(IDispatch *pDisp,VARIANT *&url){} + virtual void ProgressChange(LONG nProgress, LONG nProgressMax){} + virtual void NewWindow3(IDispatch **pDisp, VARIANT_BOOL *&Cancel, DWORD dwFlags, BSTR bstrUrlContext, BSTR bstrUrl){} + virtual void CommandStateChange(long Command,VARIANT_BOOL Enable){}; + + // interface IDocHostUIHandler + virtual HRESULT STDMETHODCALLTYPE ShowContextMenu( + /* [in] */ DWORD dwID, + /* [in] */ POINT __RPC_FAR *ppt, + /* [in] */ IUnknown __RPC_FAR *pcmdtReserved, + /* [in] */ IDispatch __RPC_FAR *pdispReserved) + { + //return E_NOTIMPL; + // E_NOTIMPL ϵͳҼ˵ + return S_OK; + //S_OK ϵͳҼ˵ + } + + virtual HRESULT STDMETHODCALLTYPE GetHostInfo( + /* [out][in] */ DOCHOSTUIINFO __RPC_FAR *pInfo) + { + // if (pInfo != NULL) + // { + // pInfo->dwFlags |= DOCHOSTUIFLAG_NO3DBORDER; + // } + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE ShowUI( + /* [in] */ DWORD dwID, + /* [in] */ IOleInPlaceActiveObject __RPC_FAR *pActiveObject, + /* [in] */ IOleCommandTarget __RPC_FAR *pCommandTarget, + /* [in] */ IOleInPlaceFrame __RPC_FAR *pFrame, + /* [in] */ IOleInPlaceUIWindow __RPC_FAR *pDoc) + { + return S_FALSE; + } + + virtual HRESULT STDMETHODCALLTYPE HideUI( void) + { + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE UpdateUI( void) + { + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE EnableModeless( + /* [in] */ BOOL fEnable) + { + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE OnDocWindowActivate( + /* [in] */ BOOL fActivate) + { + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE OnFrameWindowActivate( + /* [in] */ BOOL fActivate) + { + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE ResizeBorder( + /* [in] */ LPCRECT prcBorder, + /* [in] */ IOleInPlaceUIWindow __RPC_FAR *pUIWindow, + /* [in] */ BOOL fRameWindow) + { + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator( + /* [in] */ LPMSG lpMsg, + /* [in] */ const GUID __RPC_FAR *pguidCmdGroup, + /* [in] */ DWORD nCmdID) + { + return S_FALSE; + } + + virtual HRESULT STDMETHODCALLTYPE GetOptionKeyPath( + /* [out] */ LPOLESTR __RPC_FAR *pchKey, + /* [in] */ DWORD dw) + { + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE GetDropTarget( + /* [in] */ IDropTarget __RPC_FAR *pDropTarget, + /* [out] */ IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget) + { + return E_NOTIMPL; + } + + virtual HRESULT STDMETHODCALLTYPE GetExternal( + /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppDispatch) + { + return E_NOTIMPL; + } + + virtual HRESULT STDMETHODCALLTYPE TranslateUrl( + /* [in] */ DWORD dwTranslate, + /* [in] */ OLECHAR __RPC_FAR *pchURLIn, + /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut) + { + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE FilterDataObject( + /* [in] */ IDataObject __RPC_FAR *pDO, + /* [out] */ IDataObject __RPC_FAR *__RPC_FAR *ppDORet) + { + return S_OK; + } + + // virtual HRESULT STDMETHODCALLTYPE GetOverrideKeyPath( + // /* [annotation][out] */ + // __deref_out LPOLESTR *pchKey, + // /* [in] */ DWORD dw) + // { + // return E_NOTIMPL; + // } + + // IDownloadManager + virtual HRESULT STDMETHODCALLTYPE Download( + /* [in] */ IMoniker *pmk, + /* [in] */ IBindCtx *pbc, + /* [in] */ DWORD dwBindVerb, + /* [in] */ LONG grfBINDF, + /* [in] */ BINDINFO *pBindInfo, + /* [in] */ LPCOLESTR pszHeaders, + /* [in] */ LPCOLESTR pszRedir, + /* [in] */ UINT uiCP) + { + return S_OK; + } + }; +} diff --git a/DuiLib/Utils/WinImplBase.cpp b/DuiLib/Utils/WinImplBase.cpp new file mode 100644 index 00000000..ee0589e4 --- /dev/null +++ b/DuiLib/Utils/WinImplBase.cpp @@ -0,0 +1,469 @@ +#ifndef WIN_IMPL_BASE_HPP +#define WIN_IMPL_BASE_HPP + +#include "stdafx.h" + +namespace DuiLib +{ + +////////////////////////////////////////////////////////////////////////// + + +DUI_BEGIN_MESSAGE_MAP(WindowImplBase,CNotifyPump) + DUI_ON_MSGTYPE(DUI_MSGTYPE_CLICK,OnClick) +DUI_END_MESSAGE_MAP() + +void WindowImplBase::OnFinalMessage( HWND hWnd ) +{ + m_PaintManager.RemovePreMessageFilter(this); + m_PaintManager.RemoveNotifier(this); + m_PaintManager.ReapObjects(m_PaintManager.GetRoot()); +} + +LRESULT WindowImplBase::ResponseDefaultKeyEvent(WPARAM wParam) +{ + if (wParam == VK_RETURN) + { + return FALSE; + } + else if (wParam == VK_ESCAPE) + { + // Close(); + return TRUE; + } + + return FALSE; +} + +UINT WindowImplBase::GetClassStyle() const +{ + return CS_DBLCLKS; +} + +UILIB_RESOURCETYPE WindowImplBase::GetResourceType() const +{ + return UILIB_FILE; +} + +CDuiString WindowImplBase::GetZIPFileName() const +{ + return _T(""); +} + +LPCTSTR WindowImplBase::GetResourceID() const +{ + return _T(""); +} + +CControlUI* WindowImplBase::CreateControl(LPCTSTR pstrClass) +{ + return NULL; +} + +LRESULT WindowImplBase::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM /*lParam*/, bool& /*bHandled*/) +{ + if (uMsg == WM_KEYDOWN) + { + switch (wParam) + { + case VK_RETURN: + case VK_ESCAPE: + return ResponseDefaultKeyEvent(wParam); + default: + break; + } + } + return FALSE; +} + +LRESULT WindowImplBase::OnClose(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) +{ + bHandled = FALSE; + return 0; +} + +LRESULT WindowImplBase::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) +{ + bHandled = FALSE; + return 0; +} + +#if defined(WIN32) && !defined(UNDER_CE) +LRESULT WindowImplBase::OnNcActivate(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) +{ + if( ::IsIconic(*this) ) bHandled = FALSE; + return (wParam == 0) ? TRUE : FALSE; +} + +LRESULT WindowImplBase::OnNcCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + LPRECT pRect=NULL; + + if ( wParam == TRUE) + { + LPNCCALCSIZE_PARAMS pParam = (LPNCCALCSIZE_PARAMS)lParam; + pRect=&pParam->rgrc[0]; + } + else + { + pRect=(LPRECT)lParam; + } + + if ( ::IsZoomed(m_hWnd)) + { // ʱ㵱ǰʾʺϿ߶ + MONITORINFO oMonitor = {}; + oMonitor.cbSize = sizeof(oMonitor); + ::GetMonitorInfo(::MonitorFromWindow(*this, MONITOR_DEFAULTTONEAREST), &oMonitor); + CDuiRect rcWork = oMonitor.rcWork; + CDuiRect rcMonitor = oMonitor.rcMonitor; + rcWork.Offset(-oMonitor.rcMonitor.left, -oMonitor.rcMonitor.top); + + pRect->top = pRect->left = 0; + pRect->right = pRect->left + rcWork.GetWidth(); + pRect->bottom = pRect->top + rcWork.GetHeight(); + return WVR_REDRAW; + } + + return 0; +} + +LRESULT WindowImplBase::OnNcPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) +{ + return 0; +} + +LRESULT WindowImplBase::OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + POINT pt; pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam); + ::ScreenToClient(*this, &pt); + + RECT rcClient; + ::GetClientRect(*this, &rcClient); + + if( !::IsZoomed(*this) ) + { + RECT rcSizeBox = m_PaintManager.GetSizeBox(); + if( pt.y < rcClient.top + rcSizeBox.top ) + { + if( pt.x < rcClient.left + rcSizeBox.left ) return HTTOPLEFT; + if( pt.x > rcClient.right - rcSizeBox.right ) return HTTOPRIGHT; + return HTTOP; + } + else if( pt.y > rcClient.bottom - rcSizeBox.bottom ) + { + if( pt.x < rcClient.left + rcSizeBox.left ) return HTBOTTOMLEFT; + if( pt.x > rcClient.right - rcSizeBox.right ) return HTBOTTOMRIGHT; + return HTBOTTOM; + } + + if( pt.x < rcClient.left + rcSizeBox.left ) return HTLEFT; + if( pt.x > rcClient.right - rcSizeBox.right ) return HTRIGHT; + } + + RECT rcCaption = m_PaintManager.GetCaptionRect(); + if( pt.x >= rcClient.left + rcCaption.left && pt.x < rcClient.right - rcCaption.right \ + && pt.y >= rcCaption.top && pt.y < rcCaption.bottom ) { + CControlUI* pControl = static_cast(m_PaintManager.FindControl(pt)); + if( pControl && _tcsicmp(pControl->GetClass(), _T("ButtonUI")) != 0 && + _tcsicmp(pControl->GetClass(), _T("OptionUI")) != 0 && + _tcsicmp(pControl->GetClass(), _T("TextUI")) != 0 && + _tcsicmp(pControl->GetClass(), _T("SliderUI")) != 0) + return HTCAPTION; + } + + return HTCLIENT; +} + +LRESULT WindowImplBase::OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + LPMINMAXINFO lpMMI = (LPMINMAXINFO) lParam; + + MONITORINFO oMonitor = {}; + oMonitor.cbSize = sizeof(oMonitor); + ::GetMonitorInfo(::MonitorFromWindow(*this, MONITOR_DEFAULTTONEAREST), &oMonitor); + CDuiRect rcWork = oMonitor.rcWork; + CDuiRect rcMonitor = oMonitor.rcMonitor; + rcWork.Offset(-oMonitor.rcMonitor.left, -oMonitor.rcMonitor.top); + + // ʱȷԭ + lpMMI->ptMaxPosition.x = rcWork.left; + lpMMI->ptMaxPosition.y = rcWork.top; + + lpMMI->ptMaxTrackSize.x =rcWork.GetWidth(); + lpMMI->ptMaxTrackSize.y =rcWork.GetHeight(); + + lpMMI->ptMinTrackSize.x =m_PaintManager.GetMinInfo().cx; + lpMMI->ptMinTrackSize.y =m_PaintManager.GetMinInfo().cy; + + bHandled = FALSE; + return 0; +} + +LRESULT WindowImplBase::OnMouseWheel(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) +{ + bHandled = FALSE; + return 0; +} + +LRESULT WindowImplBase::OnMouseHover(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + bHandled = FALSE; + return 0; +} +#endif + +LRESULT WindowImplBase::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + SIZE szRoundCorner = m_PaintManager.GetRoundCorner(); +#if defined(WIN32) && !defined(UNDER_CE) + if( !::IsIconic(*this) ) { + CDuiRect rcWnd; + ::GetWindowRect(*this, &rcWnd); + rcWnd.Offset(-rcWnd.left, -rcWnd.top); + rcWnd.right++; rcWnd.bottom++; + HRGN hRgn = ::CreateRoundRectRgn(rcWnd.left, rcWnd.top, rcWnd.right, rcWnd.bottom, szRoundCorner.cx, szRoundCorner.cy); + ::SetWindowRgn(*this, hRgn, TRUE); + ::DeleteObject(hRgn); + } +#endif + bHandled = FALSE; + return 0; +} + +LRESULT WindowImplBase::OnChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + bHandled = FALSE; + return 0; +} + +LRESULT WindowImplBase::OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + if (wParam == SC_CLOSE) + { + bHandled = TRUE; + SendMessage(WM_CLOSE); + return 0; + } +#if defined(WIN32) && !defined(UNDER_CE) + BOOL bZoomed = ::IsZoomed(*this); + LRESULT lRes = CWindowWnd::HandleMessage(uMsg, wParam, lParam); + if( ::IsZoomed(*this) != bZoomed ) + { + CControlUI* pbtnMax = static_cast(m_PaintManager.FindControl(_T("maxbtn"))); // 󻯰ť + CControlUI* pbtnRestore = static_cast(m_PaintManager.FindControl(_T("restorebtn"))); // ԭť + + // л󻯰ťͻԭť״̬ + if (pbtnMax && pbtnRestore) + { + pbtnMax->SetVisible(TRUE == bZoomed); // ˴ñʽΪ˱BOOLתľ + pbtnRestore->SetVisible(FALSE == bZoomed); + } + + } +#else + LRESULT lRes = CWindowWnd::HandleMessage(uMsg, wParam, lParam); +#endif + if (SC_RESTORE == (wParam & 0xfff0)) + { + bHandled = FALSE; + } + return lRes; +} + +LRESULT WindowImplBase::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + LONG styleValue = ::GetWindowLong(*this, GWL_STYLE); + styleValue &= ~WS_CAPTION; + ::SetWindowLong(*this, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); + + RECT rcClient; + ::GetClientRect(*this, &rcClient); + ::SetWindowPos(*this, NULL, rcClient.left, rcClient.top, rcClient.right - rcClient.left, \ + rcClient.bottom - rcClient.top, SWP_FRAMECHANGED); + + m_PaintManager.Init(m_hWnd); + m_PaintManager.AddPreMessageFilter(this); + + CDialogBuilder builder; + if (m_PaintManager.GetResourcePath().IsEmpty()) + { // Դ· + CDuiString strResourcePath=m_PaintManager.GetInstancePath(); + strResourcePath+=GetSkinFolder().GetData(); + m_PaintManager.SetResourcePath(strResourcePath.GetData()); + } + + switch(GetResourceType()) + { + case UILIB_ZIP: + m_PaintManager.SetResourceZip(GetZIPFileName().GetData(), true); + break; + case UILIB_ZIPRESOURCE: + { + HRSRC hResource = ::FindResource(m_PaintManager.GetResourceDll(), GetResourceID(), _T("ZIPRES")); + if( hResource == NULL ) + return 0L; + DWORD dwSize = 0; + HGLOBAL hGlobal = ::LoadResource(m_PaintManager.GetResourceDll(), hResource); + if( hGlobal == NULL ) + { +#if defined(WIN32) && !defined(UNDER_CE) + ::FreeResource(hResource); +#endif + return 0L; + } + dwSize = ::SizeofResource(m_PaintManager.GetResourceDll(), hResource); + if( dwSize == 0 ) + return 0L; + + m_PaintManager.SetResourceZip((LPBYTE)::LockResource(hGlobal), dwSize); + +#if defined(WIN32) && !defined(UNDER_CE) + ::FreeResource(hResource); +#endif + } + break; + } + + CControlUI* pRoot=NULL; + if (GetResourceType()==UILIB_RESOURCE) + { + STRINGorID xml(_ttoi(GetSkinFile().GetData())); + pRoot = builder.Create(xml, _T("xml"), this, &m_PaintManager); + } + else + pRoot = builder.Create(GetSkinFile().GetData(), (UINT)0, this, &m_PaintManager); + ASSERT(pRoot); + if (pRoot==NULL) + { + MessageBox(NULL,_T("Դļʧ"),_T("Duilib"),MB_OK|MB_ICONERROR); + ExitProcess(1); + return 0; + } + m_PaintManager.AttachDialog(pRoot); + m_PaintManager.AddNotifier(this); + + InitWindow(); + return 0; +} + +LRESULT WindowImplBase::OnKeyDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) +{ + bHandled = FALSE; + return 0; +} + +LRESULT WindowImplBase::OnKillFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) +{ + bHandled = FALSE; + return 0; +} + +LRESULT WindowImplBase::OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) +{ + bHandled = FALSE; + return 0; +} + +LRESULT WindowImplBase::OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) +{ + bHandled = FALSE; + return 0; +} + +LRESULT WindowImplBase::OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) +{ + bHandled = FALSE; + return 0; +} + +LRESULT WindowImplBase::OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) +{ + bHandled = FALSE; + return 0; +} + +LRESULT WindowImplBase::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LRESULT lRes = 0; + BOOL bHandled = TRUE; + switch (uMsg) + { + case WM_CREATE: lRes = OnCreate(uMsg, wParam, lParam, bHandled); break; + case WM_CLOSE: lRes = OnClose(uMsg, wParam, lParam, bHandled); break; + case WM_DESTROY: lRes = OnDestroy(uMsg, wParam, lParam, bHandled); break; +#if defined(WIN32) && !defined(UNDER_CE) + case WM_NCACTIVATE: lRes = OnNcActivate(uMsg, wParam, lParam, bHandled); break; + case WM_NCCALCSIZE: lRes = OnNcCalcSize(uMsg, wParam, lParam, bHandled); break; + case WM_NCPAINT: lRes = OnNcPaint(uMsg, wParam, lParam, bHandled); break; + case WM_NCHITTEST: lRes = OnNcHitTest(uMsg, wParam, lParam, bHandled); break; + case WM_GETMINMAXINFO: lRes = OnGetMinMaxInfo(uMsg, wParam, lParam, bHandled); break; + case WM_MOUSEWHEEL: lRes = OnMouseWheel(uMsg, wParam, lParam, bHandled); break; +#endif + case WM_SIZE: lRes = OnSize(uMsg, wParam, lParam, bHandled); break; + case WM_CHAR: lRes = OnChar(uMsg, wParam, lParam, bHandled); break; + case WM_SYSCOMMAND: lRes = OnSysCommand(uMsg, wParam, lParam, bHandled); break; + case WM_KEYDOWN: lRes = OnKeyDown(uMsg, wParam, lParam, bHandled); break; + case WM_KILLFOCUS: lRes = OnKillFocus(uMsg, wParam, lParam, bHandled); break; + case WM_SETFOCUS: lRes = OnSetFocus(uMsg, wParam, lParam, bHandled); break; + case WM_LBUTTONUP: lRes = OnLButtonUp(uMsg, wParam, lParam, bHandled); break; + case WM_LBUTTONDOWN: lRes = OnLButtonDown(uMsg, wParam, lParam, bHandled); break; + case WM_MOUSEMOVE: lRes = OnMouseMove(uMsg, wParam, lParam, bHandled); break; + case WM_MOUSEHOVER: lRes = OnMouseHover(uMsg, wParam, lParam, bHandled); break; + default: bHandled = FALSE; break; + } + if (bHandled) return lRes; + + lRes = HandleCustomMessage(uMsg, wParam, lParam, bHandled); + if (bHandled) return lRes; + + if (m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes)) + return lRes; + return CWindowWnd::HandleMessage(uMsg, wParam, lParam); +} + +LRESULT WindowImplBase::HandleCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + bHandled = FALSE; + return 0; +} + +LONG WindowImplBase::GetStyle() +{ + LONG styleValue = ::GetWindowLong(*this, GWL_STYLE); + styleValue &= ~WS_CAPTION; + + return styleValue; +} + +void WindowImplBase::OnClick(TNotifyUI& msg) +{ + CDuiString sCtrlName = msg.pSender->GetName(); + if( sCtrlName == _T("closebtn") ) + { + Close(); + return; + } + else if( sCtrlName == _T("minbtn")) + { + SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0); + return; + } + else if( sCtrlName == _T("maxbtn")) + { + SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, 0); + return; + } + else if( sCtrlName == _T("restorebtn")) + { + SendMessage(WM_SYSCOMMAND, SC_RESTORE, 0); + return; + } + return; +} + +void WindowImplBase::Notify(TNotifyUI& msg) +{ + return CNotifyPump::NotifyPump(msg); +} + +} \ No newline at end of file diff --git a/DuiLib/Utils/WinImplBase.h b/DuiLib/Utils/WinImplBase.h new file mode 100644 index 00000000..1b15d95d --- /dev/null +++ b/DuiLib/Utils/WinImplBase.h @@ -0,0 +1,77 @@ +#include "StdAfx.h" + +#ifndef WIN_IMPL_BASE_HPP +#define WIN_IMPL_BASE_HPP + +namespace DuiLib +{ + + enum UILIB_RESOURCETYPE + { + UILIB_FILE=1, // Դļ + UILIB_ZIP, // Դzipѹ + UILIB_RESOURCE, // Դ + UILIB_ZIPRESOURCE, // Դzipѹ + }; + + class UILIB_API WindowImplBase + : public CWindowWnd + , public CNotifyPump + , public INotifyUI + , public IMessageFilterUI + , public IDialogBuilderCallback + { + public: + WindowImplBase(){}; + virtual ~WindowImplBase(){}; + virtual void InitWindow(){}; + virtual void OnFinalMessage( HWND hWnd ); + virtual void Notify(TNotifyUI& msg); + + DUI_DECLARE_MESSAGE_MAP() + virtual void OnClick(TNotifyUI& msg); + + protected: + virtual CDuiString GetSkinFolder() = 0; + virtual CDuiString GetSkinFile() = 0; + virtual LPCTSTR GetWindowClassName(void) const = 0 ; + virtual LRESULT ResponseDefaultKeyEvent(WPARAM wParam); + + CPaintManagerUI m_PaintManager; + + public: + virtual UINT GetClassStyle() const; + virtual UILIB_RESOURCETYPE GetResourceType() const; + virtual CDuiString GetZIPFileName() const; + virtual LPCTSTR GetResourceID() const; + virtual CControlUI* CreateControl(LPCTSTR pstrClass); + virtual LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM /*lParam*/, bool& /*bHandled*/); + virtual LRESULT OnClose(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled); + virtual LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled); + +#if defined(WIN32) && !defined(UNDER_CE) + virtual LRESULT OnNcActivate(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled); + virtual LRESULT OnNcCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + virtual LRESULT OnNcPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); + virtual LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + virtual LRESULT OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + virtual LRESULT OnMouseWheel(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled); + virtual LRESULT OnMouseHover(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); +#endif + virtual LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + virtual LRESULT OnChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + virtual LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + virtual LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + virtual LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled); + virtual LRESULT OnKillFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled); + virtual LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled); + virtual LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled); + virtual LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled); + virtual LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled); + virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual LRESULT HandleCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + virtual LONG GetStyle(); + }; +} + +#endif // WIN_IMPL_BASE_HPP diff --git a/DuiLib/Utils/XUnzip.cpp b/DuiLib/Utils/XUnzip.cpp new file mode 100644 index 00000000..2f7b40a8 --- /dev/null +++ b/DuiLib/Utils/XUnzip.cpp @@ -0,0 +1,4445 @@ +// XUnzip.cpp Version 1.3 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich@gmail.com +// +// Version 1.3: - Corrected size bug introduced by 1.2 +// +// Version 1.2: - Many bug fixes. See CodeProject article for list. +// +// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd() +// - Changed file names to avoid conflicts with Lucian's files +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by Info-ZIP. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + + +//#define _USE_32BIT_TIME_T //+++1.2 + + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include + +#pragma warning(disable : 4996) // disable bogus deprecation warning + +// THIS FILE is almost entirely based upon code by Jean-loup Gailly +// and Mark Adler. It has been modified by Lucian Wischik. +// The original code may be found at http://www.gzip.org/zlib/ +// The original copyright text follows. +// +// +// +// zlib.h -- interface of the 'zlib' general purpose compression library +// version 1.1.3, July 9th, 1998 +// +// Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Jean-loup Gailly Mark Adler +// jloup@gzip.org madler@alumni.caltech.edu +// +// +// The data format used by the zlib library is described by RFCs (Request for +// Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt +// (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +// +// +// The 'zlib' compression library provides in-memory compression and +// decompression functions, including integrity checks of the uncompressed +// data. This version of the library supports only one compression method +// (deflation) but other algorithms will be added later and will have the same +// stream interface. +// +// Compression can be done in a single step if the buffers are large +// enough (for example if an input file is mmap'ed), or can be done by +// repeated calls of the compression function. In the latter case, the +// application must provide more input and/or consume the output +// (providing more output space) before each call. +// +// The library also supports reading and writing files in gzip (.gz) format +// with an interface similar to that of stdio. +// +// The library does not install any signal handler. The decoder checks +// the consistency of the compressed data, so the library should never +// crash even in case of corrupted input. +// +// for more info about .ZIP format, see ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip +// PkWare has also a specification at ftp://ftp.pkware.com/probdesc.zip + +#define zmalloc(len) malloc(len) + +#define zfree(p) free(p) + +/* +void *zmalloc(unsigned int len) +{ char *buf = new char[len+32]; + for (int i=0; i<16; i++) + { buf[i]=i; + buf[len+31-i]=i; + } + *((unsigned int*)buf) = len; + char c[1000]; wsprintf(c,"malloc 0x%lx - %lu",buf+16,len); + OutputDebugString(c); + return buf+16; +} + +void zfree(void *buf) +{ char c[1000]; wsprintf(c,"free 0x%lx",buf); + OutputDebugString(c); + char *p = ((char*)buf)-16; + unsigned int len = *((unsigned int*)p); + bool blown=false; + for (int i=0; i<16; i++) + { char lo = p[i]; + char hi = p[len+31-i]; + if (hi!=i || (lo!=i && i>4)) blown=true; + } + if (blown) + { OutputDebugString("BLOWN!!!"); + } + delete[] p; +} +*/ + +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened + +typedef DWORD ZRESULT; +// return codes from any of the zip functions. Listed later. + +#define ZIP_HANDLE 1 +#define ZIP_FILENAME 2 +#define ZIP_MEMORY 3 + +typedef struct +{ int index; // index of this file within the zip +char name[MAX_PATH]; // filename within the zip +DWORD attr; // attributes, as in GetFileAttributes. +FILETIME atime,ctime,mtime;// access, create, modify filetimes +long comp_size; // sizes of item, compressed and uncompressed. These +long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; + +typedef struct +{ int index; // index of this file within the zip +TCHAR name[MAX_PATH]; // filename within the zip +DWORD attr; // attributes, as in GetFileAttributes. +FILETIME atime,ctime,mtime;// access, create, modify filetimes +long comp_size; // sizes of item, compressed and uncompressed. These +long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; + +// These are the result codes: +#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, +#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. +// The following come from general system stuff (e.g. files not openable) +#define ZR_GENMASK 0x0000FF00 +#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle +#define ZR_NOFILE 0x00000200 // couldn't create/open the file +#define ZR_NOALLOC 0x00000300 // failed to allocate some resource +#define ZR_WRITE 0x00000400 // a general error writing to the file +#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip +#define ZR_MORE 0x00000600 // there's still more data to be unzipped +#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile +#define ZR_READ 0x00000800 // a general error reading the file +// The following come from mistakes on the part of the caller +#define ZR_CALLERMASK 0x00FF0000 +#define ZR_ARGS 0x00010000 // general mistake with the arguments +#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't +#define ZR_MEMSIZE 0x00030000 // the memory size is too small +#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function +#define ZR_ENDED 0x00050000 // the zip creation has already been closed +#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken +#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped +#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip +// The following come from bugs within the zip library itself +#define ZR_BUGMASK 0xFF000000 +#define ZR_NOTINITED 0x01000000 // initialisation didn't work +#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file +#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed +#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code + +#pragma warning(disable : 4702) // unreachable code + +static ZRESULT zopenerror = ZR_OK; //+++1.2 + +typedef struct tm_unz_s +{ unsigned int tm_sec; // seconds after the minute - [0,59] + unsigned int tm_min; // minutes after the hour - [0,59] + unsigned int tm_hour; // hours since midnight - [0,23] + unsigned int tm_mday; // day of the month - [1,31] + unsigned int tm_mon; // months since January - [0,11] + unsigned int tm_year; // years - [1980..2044] +} tm_unz; + + +// unz_global_info structure contain global data about the ZIPfile +typedef struct unz_global_info_s +{ unsigned long number_entry; // total number of entries in the central dir on this disk + unsigned long size_comment; // size of the global comment of the zipfile +} unz_global_info; + +// unz_file_info contain information about a file in the zipfile +typedef struct unz_file_info_s +{ unsigned long version; // version made by 2 bytes + unsigned long version_needed; // version needed to extract 2 bytes + unsigned long flag; // general purpose bit flag 2 bytes + unsigned long compression_method; // compression method 2 bytes + unsigned long dosDate; // last mod file date in Dos fmt 4 bytes + unsigned long crc; // crc-32 4 bytes + unsigned long compressed_size; // compressed size 4 bytes + unsigned long uncompressed_size; // uncompressed size 4 bytes + unsigned long size_filename; // filename length 2 bytes + unsigned long size_file_extra; // extra field length 2 bytes + unsigned long size_file_comment; // file comment length 2 bytes + unsigned long disk_num_start; // disk number start 2 bytes + unsigned long internal_fa; // internal file attributes 2 bytes + unsigned long external_fa; // external file attributes 4 bytes + tm_unz tmu_date; +} unz_file_info; + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + + + + + + + +#define ZLIB_VERSION "1.1.3" + + +// Allowed flush values; see deflate() for details +#define Z_NO_FLUSH 0 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 + + +// compression levels +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) + +// compression strategy; see deflateInit2() for details +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +// Possible values of the data_type field +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 + +// The deflate compression method (the only one supported in this version) +#define Z_DEFLATED 8 + +// for initializing zalloc, zfree, opaque +#define Z_NULL 0 + +// case sensitivity when searching for filenames +#define CASE_SENSITIVE 1 +#define CASE_INSENSITIVE 2 + + +// Return codes for the compression/decompression functions. Negative +// values are errors, positive values are used for special but normal events. +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) + + + +// Basic data types +typedef unsigned char Byte; // 8 bits +typedef unsigned int uInt; // 16 bits or more +typedef unsigned long uLong; // 32 bits or more +typedef void *voidpf; +typedef void *voidp; +typedef long z_off_t; + + + + + + + + + + + + +typedef voidpf (*alloc_func) (voidpf opaque, uInt items, uInt size); +typedef void (*free_func) (voidpf opaque, voidpf address); + +struct internal_state; + +typedef struct z_stream_s { + Byte *next_in; // next input byte + uInt avail_in; // number of bytes available at next_in + uLong total_in; // total nb of input bytes read so far + + Byte *next_out; // next output byte should be put there + uInt avail_out; // remaining free space at next_out + uLong total_out; // total nb of bytes output so far + + char *msg; // last error message, NULL if no error + struct internal_state *state; // not visible by applications + + alloc_func zalloc; // used to allocate the internal state + free_func zfree; // used to free the internal state + voidpf opaque; // private data object passed to zalloc and zfree + + int data_type; // best guess about the data type: ascii or binary + uLong adler; // adler32 value of the uncompressed data + uLong reserved; // reserved for future use +} z_stream; + +typedef z_stream *z_streamp; + + +// The application must update next_in and avail_in when avail_in has +// dropped to zero. It must update next_out and avail_out when avail_out +// has dropped to zero. The application must initialize zalloc, zfree and +// opaque before calling the init function. All other fields are set by the +// compression library and must not be updated by the application. +// +// The opaque value provided by the application will be passed as the first +// parameter for calls of zalloc and zfree. This can be useful for custom +// memory management. The compression library attaches no meaning to the +// opaque value. +// +// zalloc must return Z_NULL if there is not enough memory for the object. +// If zlib is used in a multi-threaded application, zalloc and zfree must be +// thread safe. +// +// The fields total_in and total_out can be used for statistics or +// progress reports. After compression, total_in holds the total size of +// the uncompressed data and may be saved for use in the decompressor +// (particularly if the decompressor wants to decompress everything in +// a single step). +// + + +// basic functions + +const char *zlibVersion (); +// The application can compare zlibVersion and ZLIB_VERSION for consistency. +// If the first character differs, the library code actually used is +// not compatible with the zlib.h header file used by the application. +// This check is automatically made by inflateInit. + + + + + + +int inflate (z_streamp strm, int flush); +// +// inflate decompresses as much data as possible, and stops when the input +// buffer becomes empty or the output buffer becomes full. It may some +// introduce some output latency (reading input without producing any output) +// except when forced to flush. +// +// The detailed semantics are as follows. inflate performs one or both of the +// following actions: +// +// - Decompress more input starting at next_in and update next_in and avail_in +// accordingly. If not all input can be processed (because there is not +// enough room in the output buffer), next_in is updated and processing +// will resume at this point for the next call of inflate(). +// +// - Provide more output starting at next_out and update next_out and avail_out +// accordingly. inflate() provides as much output as possible, until there +// is no more input data or no more space in the output buffer (see below +// about the flush parameter). +// +// Before the call of inflate(), the application should ensure that at least +// one of the actions is possible, by providing more input and/or consuming +// more output, and updating the next_* and avail_* values accordingly. +// The application can consume the uncompressed output when it wants, for +// example when the output buffer is full (avail_out == 0), or after each +// call of inflate(). If inflate returns Z_OK and with zero avail_out, it +// must be called again after making room in the output buffer because there +// might be more output pending. +// +// If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much +// output as possible to the output buffer. The flushing behavior of inflate is +// not specified for values of the flush parameter other than Z_SYNC_FLUSH +// and Z_FINISH, but the current implementation actually flushes as much output +// as possible anyway. +// +// inflate() should normally be called until it returns Z_STREAM_END or an +// error. However if all decompression is to be performed in a single step +// (a single call of inflate), the parameter flush should be set to +// Z_FINISH. In this case all pending input is processed and all pending +// output is flushed; avail_out must be large enough to hold all the +// uncompressed data. (The size of the uncompressed data may have been saved +// by the compressor for this purpose.) The next operation on this stream must +// be inflateEnd to deallocate the decompression state. The use of Z_FINISH +// is never required, but can be used to inform inflate that a faster routine +// may be used for the single inflate() call. +// +// If a preset dictionary is needed at this point (see inflateSetDictionary +// below), inflate sets strm-adler to the adler32 checksum of the +// dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise +// it sets strm->adler to the adler32 checksum of all output produced +// so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or +// an error code as described below. At the end of the stream, inflate() +// checks that its computed adler32 checksum is equal to that saved by the +// compressor and returns Z_STREAM_END only if the checksum is correct. +// +// inflate() returns Z_OK if some progress has been made (more input processed +// or more output produced), Z_STREAM_END if the end of the compressed data has +// been reached and all uncompressed output has been produced, Z_NEED_DICT if a +// preset dictionary is needed at this point, Z_DATA_ERROR if the input data was +// corrupted (input stream not conforming to the zlib format or incorrect +// adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent +// (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not +// enough memory, Z_BUF_ERROR if no progress is possible or if there was not +// enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR +// case, the application may then call inflateSync to look for a good +// compression block. +// + + +int inflateEnd (z_streamp strm); +// +// All dynamically allocated data structures for this stream are freed. +// This function discards any unprocessed input and does not flush any +// pending output. +// +// inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state +// was inconsistent. In the error case, msg may be set but then points to a +// static string (which must not be deallocated). + + // Advanced functions + +// The following functions are needed only in some special applications. + + + + + +int inflateSetDictionary (z_streamp strm, + const Byte *dictionary, + uInt dictLength); +// +// Initializes the decompression dictionary from the given uncompressed byte +// sequence. This function must be called immediately after a call of inflate +// if this call returned Z_NEED_DICT. The dictionary chosen by the compressor +// can be determined from the Adler32 value returned by this call of +// inflate. The compressor and decompressor must use exactly the same +// dictionary. +// +// inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a +// parameter is invalid (such as NULL dictionary) or the stream state is +// inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the +// expected one (incorrect Adler32 value). inflateSetDictionary does not +// perform any decompression: this will be done by subsequent calls of +// inflate(). + + +int inflateSync (z_streamp strm); +// +// Skips invalid compressed data until a full flush point can be found, or until all +// available input is skipped. No output is provided. +// +// inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR +// if no more input was provided, Z_DATA_ERROR if no flush point has been found, +// or Z_STREAM_ERROR if the stream structure was inconsistent. In the success +// case, the application may save the current current value of total_in which +// indicates where valid compressed data was found. In the error case, the +// application may repeatedly call inflateSync, providing more input each time, +// until success or end of the input data. + + +int inflateReset (z_streamp strm); +// This function is equivalent to inflateEnd followed by inflateInit, +// but does not free and reallocate all the internal decompression state. +// The stream will keep attributes that may have been set by inflateInit2. +// +// inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source +// stream state was inconsistent (such as zalloc or state being NULL). +// + + + +// checksum functions +// These functions are not related to compression but are exported +// anyway because they might be useful in applications using the +// compression library. + +uLong adler32 (uLong adler, const Byte *buf, uInt len); +// Update a running Adler-32 checksum with the bytes buf[0..len-1] and +// return the updated checksum. If buf is NULL, this function returns +// the required initial value for the checksum. +// An Adler-32 checksum is almost as reliable as a CRC32 but can be computed +// much faster. Usage example: +// +// uLong adler = adler32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// adler = adler32(adler, buffer, length); +// } +// if (adler != original_adler) error(); + +uLong ucrc32 (uLong crc, const Byte *buf, uInt len); +// Update a running crc with the bytes buf[0..len-1] and return the updated +// crc. If buf is NULL, this function returns the required initial value +// for the crc. Pre- and post-conditioning (one's complement) is performed +// within this function so it shouldn't be done by the application. +// Usage example: +// +// uLong crc = crc32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// crc = crc32(crc, buffer, length); +// } +// if (crc != original_crc) error(); + + + + +const char *zError (int err); +int inflateSyncPoint (z_streamp z); +const uLong *get_crc_table (void); + + + +typedef unsigned char uch; +typedef uch uchf; +typedef unsigned short ush; +typedef ush ushf; +typedef unsigned long ulg; + + + +const char * const z_errmsg[10] = { // indexed by 2-zlib_error +"need dictionary", // Z_NEED_DICT 2 +"stream end", // Z_STREAM_END 1 +"", // Z_OK 0 +"file error", // Z_ERRNO (-1) +"stream error", // Z_STREAM_ERROR (-2) +"data error", // Z_DATA_ERROR (-3) +"insufficient memory", // Z_MEM_ERROR (-4) +"buffer error", // Z_BUF_ERROR (-5) +"incompatible version",// Z_VERSION_ERROR (-6) +""}; + + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +// To be used only when the state is known to be valid + + // common constants + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + +#define PRESET_DICT 0x20 // preset dictionary flag in zlib header + + // target dependencies + +#define OS_CODE 0x0b // Window 95 & Windows NT + + + + // functions + +#define zmemzero(dest, len) memset(dest, 0, len) + +// Diagnostic functions +#undef Assert +#undef Trace +#undef Tracev +#undef Tracevv +#undef Tracec +#undef Tracecv + +#ifdef DEBUG + + int z_verbose = 0; + void z_error (char *m) {fprintf(stderr, "%s\n", m); exit(1);} + +#define Assert(cond,msg) {if(!(cond)) z_error(msg);} +#define Trace(x) {if (z_verbose>=0) fprintf x ;} +#define Tracev(x) {if (z_verbose>0) fprintf x ;} +#define Tracevv(x) {if (z_verbose>1) fprintf x ;} +#define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +#define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} + +#else + +#ifndef __noop +#if _MSC_VER < 1300 +#define __noop ((void)0) +#endif +#endif + +#define Assert(cond,msg) __noop +#define Trace(x) __noop +#define Tracev(x) __noop +#define Tracevv(x) __noop +#define Tracec(c,x) __noop +#define Tracecv(c,x) __noop + +#endif + + +typedef uLong (*check_func) (uLong check, const Byte *buf, uInt len); +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size); +void zcfree (voidpf opaque, voidpf ptr); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) + +//void ZFREE(z_streamp strm,voidpf addr) +//{ *((strm)->zfree))((strm)->opaque, addr); +//} + +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + + + + +// Huffman code lookup table entry--this entry is four bytes for machines +// that have 16-bit pointers (e.g. PC's in the small or medium model). + + +typedef struct inflate_huft_s inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; // number of extra bits or operation + Byte Bits; // number of bits in this code or subcode + } what; + uInt pad; // pad structure to a power of 2 (4 bytes for + } word; // 16-bit, 8 bytes for 32-bit int's) + uInt base; // literal, length base, distance base, or table offset +}; + +// Maximum size of dynamic tree. The maximum found in a long but non- +// exhaustive search was 1004 huft structures (850 for length/literals +// and 154 for distances, the latter actually the result of an +// exhaustive search). The actual maximum is not known, but the +// value below is more than safe. +#define MANY 1440 + +int inflate_trees_bits ( + uInt *, // 19 code lengths + uInt *, // bits tree desired/actual depth + inflate_huft * *, // bits tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_dynamic ( + uInt, // number of literal/length codes + uInt, // number of distance codes + uInt *, // that many (total) code lengths + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + inflate_huft * *, // literal/length tree result + inflate_huft * *, // distance tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_fixed ( + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + const inflate_huft * *, // literal/length tree result + const inflate_huft * *, // distance tree result + z_streamp); // for memory allocation + + + + + +struct inflate_blocks_state; +typedef struct inflate_blocks_state inflate_blocks_statef; + +inflate_blocks_statef * inflate_blocks_new ( + z_streamp z, + check_func c, // check function + uInt w); // window size + +int inflate_blocks ( + inflate_blocks_statef *, + z_streamp , + int); // initial return code + +void inflate_blocks_reset ( + inflate_blocks_statef *, + z_streamp , + uLong *); // check value on output + +int inflate_blocks_free ( + inflate_blocks_statef *, + z_streamp); + +void inflate_set_dictionary ( + inflate_blocks_statef *s, + const Byte *d, // dictionary + uInt n); // dictionary length + +int inflate_blocks_sync_point ( + inflate_blocks_statef *s); + + + + +struct inflate_codes_state; +typedef struct inflate_codes_state inflate_codes_statef; + +inflate_codes_statef *inflate_codes_new ( + uInt, uInt, + const inflate_huft *, const inflate_huft *, + z_streamp ); + +int inflate_codes ( + inflate_blocks_statef *, + z_streamp , + int); + +void inflate_codes_free ( + inflate_codes_statef *, + z_streamp ); + + + + +typedef enum { + IBM_TYPE, // get type bits (3, including end bit) + IBM_LENS, // get lengths for stored + IBM_STORED, // processing stored block + IBM_TABLE, // get table lengths + IBM_BTREE, // get bit lengths tree for a dynamic block + IBM_DTREE, // get length, distance trees for a dynamic block + IBM_CODES, // processing fixed or dynamic block + IBM_DRY, // output remaining window bytes + IBM_DONE, // finished last block, done + IBM_BAD} // got a data error--stuck here +inflate_block_mode; + +// inflate blocks semi-private state +struct inflate_blocks_state { + + // mode + inflate_block_mode mode; // current inflate_block mode + + // mode dependent information + union { + uInt left; // if STORED, bytes left to copy + struct { + uInt table; // table lengths (14 bits) + uInt index; // index into blens (or border) + uInt *blens; // bit lengths of codes + uInt bb; // bit length tree depth + inflate_huft *tb; // bit length decoding tree + } trees; // if DTREE, decoding info for trees + struct { + inflate_codes_statef + *codes; + } decode; // if CODES, current state + } sub; // submode + uInt last; // true if this block is the last block + + // mode independent information + uInt bitk; // bits in bit buffer + uLong bitb; // bit buffer + inflate_huft *hufts; // single malloc for tree space + Byte *window; // sliding window + Byte *end; // one byte after sliding window + Byte *read; // window read pointer + Byte *write; // window write pointer + check_func checkfn; // check function + uLong check; // check on output + +}; + + +// defines for inflate input/output +// update pointers and return +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=(uLong)(p-z->next_in);z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +// get bytes and bits +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +// output bytes +#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;m;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +// load local pointers +#define LOAD {LOADIN LOADOUT} + +// masks for lower bits (size given to avoid silly warnings with Visual C++) +// And'ing with mask[n] masks the lower n bits +const uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +// copy as much as possible from the sliding window to the output area +int inflate_flush (inflate_blocks_statef *, z_streamp, int); + +int inflate_fast (uInt, uInt, const inflate_huft *, const inflate_huft *, inflate_blocks_statef *, z_streamp ); + + + +const uInt fixed_bl = 9; +const uInt fixed_bd = 5; +const inflate_huft fixed_tl[] = { + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} + }; +const inflate_huft fixed_td[] = { + {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, + {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, + {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, + {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, + {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, + {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, + {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, + {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} + }; + + + + + + + +// copy as much as possible from the sliding window to the output area +int inflate_flush(inflate_blocks_statef *s,z_streamp z,int r) +{ + uInt n; + Byte *p; + Byte *q; + + // local copies of source and destination pointers + p = z->next_out; + q = s->read; + + // compute number of bytes to copy as far as end of window + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy as far as end of window + if (n!=0) // check for n!=0 to avoid waking up CodeGuard + { memcpy(p, q, n); + p += n; + q += n; + } + + // see if more to copy at beginning of window + if (q == s->end) + { + // wrap pointers + q = s->window; + if (s->write == s->end) + s->write = s->window; + + // compute bytes to copy + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy + memcpy(p, q, n); + p += n; + q += n; + } + + // update pointers + z->next_out = p; + s->read = q; + + // done + return r; +} + + + + + + +// simplify the use of the inflate_huft type with some defines +#define exop word.what.Exop +#define bits word.what.Bits + +typedef enum { // waiting for "i:"=input, "o:"=output, "x:"=nothing + START, // x: set up for LEN + LEN, // i: get length/literal/eob next + LENEXT, // i: getting length extra (have base) + DIST, // i: get distance next + DISTEXT, // i: getting distance extra + COPY, // o: copying bytes in window, waiting for space + LIT, // o: got literal, waiting for output space + WASH, // o: got eob, possibly still output waiting + END, // x: got eob and all data flushed + BADCODE} // x: got error +inflate_codes_mode; + +// inflate codes private state +struct inflate_codes_state { + + // mode + inflate_codes_mode mode; // current inflate_codes mode + + // mode dependent information + uInt len; + union { + struct { + const inflate_huft *tree; // pointer into tree + uInt need; // bits needed + } code; // if LEN or DIST, where in tree + uInt lit; // if LIT, literal + struct { + uInt get; // bits to get for extra + uInt dist; // distance back to copy from + } copy; // if EXT or COPY, where and how much + } sub; // submode + + // mode independent information + Byte lbits; // ltree bits decoded per branch + Byte dbits; // dtree bits decoder per branch + const inflate_huft *ltree; // literal/length/eob tree + const inflate_huft *dtree; // distance tree + +}; + + +inflate_codes_statef *inflate_codes_new( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +z_streamp z) +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt j; // temporary storage + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + Byte *f; // pointer to copy strings from + inflate_codes_statef *c = s->sub.decode.codes; // codes state + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input and output based on current state + for(;;) switch (c->mode) + { // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif // !SLOW + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: // i: get length/literal/eob next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) // literal + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) // length + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + if (e & 32) // end of block + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: // i: getting length extra (have base) + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: // i: get distance next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) // distance + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: // i: getting distance extra + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: // o: copying bytes in window, waiting for space + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: // o: got literal, waiting for output space + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: // o: got eob, possibly more output + if (k > 7) // return unused byte, if any + { + Assert(k < 16, "inflate_codes grabbed too many bytes"); + k -= 8; + n++; + p--; // can always return one + } + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: // x: got error + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +void inflate_codes_free(inflate_codes_statef *c,z_streamp z) +{ ZFREE(z, c); + Tracev((stderr, "inflate: codes free\n")); +} + + + +// infblock.c -- interpret and process block types to last block +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + + +// Table for deflate from PKZIP's appnote.txt. +const uInt border[] = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +// +// Notes beyond the 1.93a appnote.txt: +// +// 1. Distance pointers never point before the beginning of the output stream. +// 2. Distance pointers can point back across blocks, up to 32k away. +// 3. There is an implied maximum of 7 bits for the bit length table and +// 15 bits for the actual data. +// 4. If only one code exists, then it is encoded using one bit. (Zero +// would be more efficient, but perhaps a little confusing.) If two +// codes exist, they are coded using one bit each (0 and 1). +// 5. There is no way of sending zero distance codes--a dummy must be +// sent if there are none. (History: a pre 2.0 version of PKZIP would +// store blocks with no distance codes, but this was discovered to be +// too harsh a criterion.) Valid only for 1.93a. 2.04c does allow +// zero distance codes, which is sent as one code of zero bits in +// length. +// 6. There are up to 286 literal/length codes. Code 256 represents the +// end-of-block. Note however that the static length tree defines +// 288 codes just to fill out the Huffman codes. Codes 286 and 287 +// cannot be used though, since there is no length base or extra bits +// defined for them. Similarily, there are up to 30 distance codes. +// However, static trees define 32 codes (all 5 bits) to fill out the +// Huffman codes, but the last two had better not show up in the data. +// 7. Unzip can check dynamic Huffman blocks for complete code sets. +// The exception is that a single code would not be complete (see #4). +// 8. The five bits following the block type is really the number of +// literal codes sent minus 257. +// 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits +// (1+6+6). Therefore, to output three times the length, you output +// three codes (1+1+1), whereas to output four times the same length, +// you only need two codes (1+3). Hmm. +//10. In the tree reconstruction algorithm, Code = Code + Increment +// only if BitLength(i) is not zero. (Pretty obvious.) +//11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) +//12. Note: length code 284 can represent 227-258, but length code 285 +// really is 258. The last length deserves its own, short code +// since it gets used a lot in very redundant files. The length +// 258 is special since 258 - 3 (the min match length) is 255. +//13. The literal/length and distance code bit lengths are read as a +// single stream of lengths. It is possible (and advantageous) for +// a repeat code (16, 17, or 18) to go across the boundary between +// the two sets of lengths. + + +void inflate_blocks_reset(inflate_blocks_statef *s, z_streamp z, uLong *c) +{ + if (c != Z_NULL) + *c = s->check; + if (s->mode == IBM_BTREE || s->mode == IBM_DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == IBM_CODES) + inflate_codes_free(s->sub.decode.codes, z); + s->mode = IBM_TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, (const Byte *)Z_NULL, 0); + Tracev((stderr, "inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z_streamp z, check_func c, uInt w) +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->hufts = + (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + if ((s->window = (Byte *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s->hufts); + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = IBM_TYPE; + Tracev((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, Z_NULL); + return s; +} + + +int inflate_blocks(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt t; // temporary storage + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input based on current state + for(;;) switch (s->mode) + { + case IBM_TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: // stored + Tracev((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; // go to byte boundary + DUMPBITS(t) + s->mode = IBM_LENS; // get length of stored block + break; + case 1: // fixed + Tracev((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + const inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td, z); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + } + DUMPBITS(3) + s->mode = IBM_CODES; + break; + case 2: // dynamic + Tracev((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = IBM_TABLE; + break; + case 3: // illegal + DUMPBITS(3) + s->mode = IBM_BAD; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case IBM_LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = IBM_BAD; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; // dump bits + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? IBM_STORED : (s->last ? IBM_DRY : IBM_TYPE); + break; + case IBM_STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + memcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? IBM_DRY : IBM_TYPE; + break; + case IBM_TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; + // remove this section to workaround bug in pkzip + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = IBM_BAD; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } + // end remove + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if ((s->sub.trees.blens = (uInt*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = IBM_BTREE; + case IBM_BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, s->hufts, z); + if (t != Z_OK) + { + ZFREE(z, s->sub.trees.blens); + r = t; + if (r == Z_DATA_ERROR) + s->mode = IBM_BAD; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = IBM_DTREE; + case IBM_DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->bits; + c = h->base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else // c == 16..18 + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + ZFREE(z, s->sub.trees.blens); + s->mode = IBM_BAD; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; // must be <= 9 for lookahead assumptions + bd = 6; // must be <= 9 for lookahead assumptions + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, + s->hufts, z); + ZFREE(z, s->sub.trees.blens); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = IBM_BAD; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + } + s->mode = IBM_CODES; + case IBM_CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = IBM_TYPE; + break; + } + s->mode = IBM_DRY; + case IBM_DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = IBM_DONE; + case IBM_DONE: + r = Z_STREAM_END; + LEAVE + case IBM_BAD: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(inflate_blocks_statef *s, z_streamp z) +{ + inflate_blocks_reset(s, z, Z_NULL); + ZFREE(z, s->window); + ZFREE(z, s->hufts); + ZFREE(z, s); + Tracev((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + + +// inftrees.c -- generate Huffman trees for efficient decoding +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + + +extern const char inflate_copyright[] = + " ";//inflate 1.1.3 Copyright 1995-1998 Mark Adler "; +// If you use the zlib library in a product, an acknowledgment is welcome +// in the documentation of your product. If for some reason you cannot +// include such an acknowledgment, I would appreciate that you keep this +// copyright string in the executable of your product. + + + +int huft_build ( + uInt *, // code lengths in bits + uInt, // number of codes + uInt, // number of "simple" codes + const uInt *, // list of base values for non-simple codes + const uInt *, // list of extra bits for non-simple codes + inflate_huft **,// result: starting table + uInt *, // maximum lookup bits (returns actual) + inflate_huft *, // space for trees + uInt *, // hufts used in space + uInt * ); // space for values + +// Tables for deflate from PKZIP's appnote.txt. +const uInt cplens[31] = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + // see note #13 above about 258 +const uInt cplext[31] = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; // 112==invalid +const uInt cpdist[30] = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +const uInt cpdext[30] = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +// +// Huffman code decoding is performed using a multi-level table lookup. +// The fastest way to decode is to simply build a lookup table whose +// size is determined by the longest code. However, the time it takes +// to build this table can also be a factor if the data being decoded +// is not very long. The most common codes are necessarily the +// shortest codes, so those codes dominate the decoding time, and hence +// the speed. The idea is you can have a shorter table that decodes the +// shorter, more probable codes, and then point to subsidiary tables for +// the longer codes. The time it costs to decode the longer codes is +// then traded against the time it takes to make longer tables. +// +// This results of this trade are in the variables lbits and dbits +// below. lbits is the number of bits the first level table for literal/ +// length codes can decode in one step, and dbits is the same thing for +// the distance codes. Subsequent tables are also less than or equal to +// those sizes. These values may be adjusted either when all of the +// codes are shorter than that, in which case the longest code length in +// bits is used, or when the shortest code is *longer* than the requested +// table size, in which case the length of the shortest code in bits is +// used. +// +// There are two different values for the two tables, since they code a +// different number of possibilities each. The literal/length table +// codes 286 possible values, or in a flat code, a little over eight +// bits. The distance table codes 30 possible values, or a little less +// than five bits, flat. The optimum values for speed end up being +// about one bit more than those, so lbits is 8+1 and dbits is 5+1. +// The optimum values may differ though from machine to machine, and +// possibly even between compilers. Your mileage may vary. +// + + +// If BMAX needs to be larger than 16, then h and x[] should be uLong. +#define BMAX 15 // maximum bit length of any code + +int huft_build( +uInt *b, // code lengths in bits (all assumed <= BMAX) +uInt n, // number of codes (assumed <= 288) +uInt s, // number of simple-valued codes (0..s-1) +const uInt *d, // list of base values for non-simple codes +const uInt *e, // list of extra bits for non-simple codes +inflate_huft * *t, // result: starting table +uInt *m, // maximum lookup bits, returns actual +inflate_huft *hp, // space for trees +uInt *hn, // hufts used in space +uInt *v) // working area: values in order of bit length +// Given a list of code lengths and a maximum table size, make a set of +// tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR +// if the given code set is incomplete (the tables are still built in this +// case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of +// lengths), or Z_MEM_ERROR if not enough memory. +{ + + uInt a; // counter for codes of length k + uInt c[BMAX+1]; // bit length count table + uInt f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + register uInt i; // counter, current code + register uInt j; // counter + register int k; // number of bits in current code + int l; // bits per table (returned in m) + uInt mask; // (1 << w) - 1, to avoid cc -O bug on HP + register uInt *p; // pointer into c[], b[], or v[] + inflate_huft *q; // points to current table + struct inflate_huft_s r; // table entry for structure assignment + inflate_huft *u[BMAX]; // table stack + register int w; // bits before this table == (l * h) + uInt x[BMAX+1]; // bit offsets, then code stack + uInt *xp; // pointer into x + int y; // number of dummy codes added + uInt z; // number of entries in current table + + + // Generate counts for each bit length + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4; p; // clear c[]--assume BMAX+1 is 16 + p = b; i = n; + do { + c[*p++]++; // assume all entries <= BMAX + } while (--i); + if (c[0] == n) // null input--all zero length codes + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + // Find minimum and maximum length, bound *m by those + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; // minimum code length + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; // maximum code length + if ((uInt)l > i) + l = i; + *m = l; + + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { // note that i == g from above + *xp++ = (j += *p++); + } + + + // Make a table of values in order of bit lengths + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; // set n to length of v + + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = v; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = (inflate_huft *)Z_NULL; // just to keep compilers happy + q = (inflate_huft *)Z_NULL; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + + // compute minimum size table less than or equal to l bits + z = g - w; + z = z > (uInt)l ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) // try a k-w bit table + { // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = c + k; + if (j < z) + while (++j < z) // try smaller tables up to z bits + { + if ((f <<= 1) <= *++xp) + break; // enough codes to use up j bits + f -= *xp; // else deduct codes from patterns + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (*hn + z > MANY) // (note: doesn't matter for fixed) + return Z_MEM_ERROR; // not enough memory + u[h] = q = hp + *hn; + *hn += z; + + // connect to last table, if there is one + if (h) + { + x[h] = i; // save pattern for backing up + r.bits = (Byte)l; // bits to dump before this table + r.exop = (Byte)j; // bits in this table + j = i >> (w - l); + r.base = (uInt)(q - u[h-1] - j); // offset to this table + u[h-1][j] = r; // connect to last table + } + else + *t = q; // first table is returned result + } + + // set up table entry in r + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; // out of values--invalid code + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); // 256 is end-of-block + r.base = *p++; // simple code is just the value + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);// non-simple--look up in lists + r.base = d[*p++ - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + + + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits( +uInt *c, // 19 code lengths +uInt *bb, // bits tree desired/actual depth +inflate_huft * *tb, // bits tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + if ((v = (uInt*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + r = huft_build(c, 19, 19, (uInt*)Z_NULL, (uInt*)Z_NULL, + tb, bb, hp, &hn, v); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +} + + +int inflate_trees_dynamic( +uInt nl, // number of literal/length codes +uInt nd, // number of distance codes +uInt *c, // that many (total) code lengths +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +inflate_huft * *tl, // literal/length tree result +inflate_huft * *td, // distance tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + // allocate work area + if ((v = (uInt*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + + // build literal/length tree + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // build distance tree + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // done + ZFREE(z, v); + return Z_OK; +} + + + + + +int inflate_trees_fixed( +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +const inflate_huft * * tl, // literal/length tree result +const inflate_huft * *td, // distance tree result +z_streamp ) // for memory allocation +{ + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +// inffast.c -- process literals and length/distance pairs fast +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + +// macros for bit input with no checking and for returning unused bytes +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} + +// Called with number of bytes left to write in window at least 258 +// (the maximum string length) and number of input bytes available +// at least ten. The ten bytes are six bytes for the longest length/ +// distance pair plus four bytes for overloading the bit buffer. + +int inflate_fast( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +inflate_blocks_statef *s, +z_streamp z) +{ + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + uInt ml; // mask for literal/length tree + uInt md; // mask for distance tree + uInt c; // bytes to copy + uInt d; // distance back to copy from + Byte *r; // copy source pointer + + // load input, output, bit values + LOAD + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do { // assume called with m >= 258 && n >= 10 + // get literal/length code + GRABBITS(20) // max bits for literal/length code + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits for length + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + // decode distance base of block to copy + GRABBITS(15); // max bits for distance code + e = (t = td + ((uInt)b & md))->exop; + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits to add to distance base + e &= 15; + GRABBITS(e) // get extra bits (up to 13) + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + // do the copy + m -= c; + if ((uInt)(q - s->window) >= d) // offset before dest + { // just copy + r = q - d; + *q++ = *r++; c--; // minimum count is three, + *q++ = *r++; c--; // so unroll loop a little + } + else // else offset after destination + { + e = d - (uInt)(q - s->window); // bytes from offset to end + r = s->end - e; // pointer to offset + if (c > e) // if source crosses, + { + c -= e; // copy to end of window + do { + *q++ = *r++; + } while (--e); + r = s->window; // copy rest from start of window + } + } + do { // copy all or what's left + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + { + t += t->base; + e = (t += ((uInt)b & inflate_mask[e]))->exop; + } + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + break; + } + if ((e & 64) == 0) + { + t += t->base; + if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + } while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + UNGRAB + UPDATE + return Z_OK; +} + + + + + + +// crc32.c -- compute the CRC-32 of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + + + + + +// Table of CRC-32's of all single-byte values (made by make_crc_table) +const uLong crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +const uLong * get_crc_table() +{ return (const uLong *)crc_table; +} + +#define CRC_DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define CRC_DO2(buf) CRC_DO1(buf); CRC_DO1(buf); +#define CRC_DO4(buf) CRC_DO2(buf); CRC_DO2(buf); +#define CRC_DO8(buf) CRC_DO4(buf); CRC_DO4(buf); + +uLong ucrc32(uLong crc, const Byte *buf, uInt len) +{ if (buf == Z_NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) {CRC_DO8(buf); len -= 8;} + if (len) do {CRC_DO1(buf);} while (--len); + return crc ^ 0xffffffffL; +} + + +// adler32.c -- compute the Adler-32 checksum of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + +#define BASE 65521L // largest prime smaller than 65536 +#define NMAX 5552 +// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + +#define AD_DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define AD_DO2(buf,i) AD_DO1(buf,i); AD_DO1(buf,i+1); +#define AD_DO4(buf,i) AD_DO2(buf,i); AD_DO2(buf,i+2); +#define AD_DO8(buf,i) AD_DO4(buf,i); AD_DO4(buf,i+4); +#define AD_DO16(buf) AD_DO8(buf,0); AD_DO8(buf,8); + +// ========================================================================= +uLong adler32(uLong adler, const Byte *buf, uInt len) +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + AD_DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} + + + +// zutil.c -- target dependent utility functions for the compression library +// Copyright (C) 1995-1998 Jean-loup Gailly. +// For conditions of distribution and use, see copyright notice in zlib.h +// @(#) $Id$ + + + + + + +const char * zlibVersion() +{ + return ZLIB_VERSION; +} + +// exported to allow conversion of error code to string for compress() and +// uncompress() +const char * zError(int err) +{ return ERR_MSG(err); +} + + + + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) items += size - size; // make compiler happy + return (voidpf)calloc(items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + zfree(ptr); + if (opaque) return; // make compiler happy +} + + + +// inflate.c -- zlib interface to inflate modules +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_blocks_state {int dummy;}; // for buggy compilers + +typedef enum { + IM_METHOD, // waiting for method byte + IM_FLAG, // waiting for flag byte + IM_DICT4, // four dictionary check bytes to go + IM_DICT3, // three dictionary check bytes to go + IM_DICT2, // two dictionary check bytes to go + IM_DICT1, // one dictionary check byte to go + IM_DICT0, // waiting for inflateSetDictionary + IM_BLOCKS, // decompressing blocks + IM_CHECK4, // four check bytes to go + IM_CHECK3, // three check bytes to go + IM_CHECK2, // two check bytes to go + IM_CHECK1, // one check byte to go + IM_DONE, // finished check, done + IM_BAD} // got an error--stay here +inflate_mode; + +// inflate private state +struct internal_state { + + // mode + inflate_mode mode; // current inflate mode + + // mode dependent information + union { + uInt method; // if IM_FLAGS, method byte + struct { + uLong was; // computed check value + uLong need; // stream check value + } check; // if CHECK, check values to compare + uInt marker; // if IM_BAD, inflateSync's marker bytes count + } sub; // submode + + // mode independent information + int nowrap; // flag for no wrapper + uInt wbits; // log2(window size) (8..15, defaults to 15) + inflate_blocks_statef + *blocks; // current inflate_blocks state + +}; + +int inflateReset(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? IM_BLOCKS : IM_METHOD; + inflate_blocks_reset(z->state->blocks, z, Z_NULL); + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int inflateEnd(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z); + ZFREE(z, z->state); + z->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z_streamp z) +{ const char *version = ZLIB_VERSION; int stream_size = sizeof(z_stream); + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != sizeof(z_stream)) return Z_VERSION_ERROR; + + int w = -15; // MAX_WBITS: 32K LZ77 window. + // Warning: reducing MAX_WBITS makes minigzip unable to extract .gz files created by gzip. + // The memory requirements for deflate are (in bytes): + // (1 << (windowBits+2)) + (1 << (memLevel+9)) + // that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + // plus a few kilobytes for small objects. For example, if you want to reduce + // the default memory requirements from 256K to 128K, compile with + // make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + // Of course this will generally degrade compression (there's no free lunch). + // + // The memory requirements for inflate are (in bytes) 1 << windowBits + // that is, 32K for windowBits=15 (default value) plus a few kilobytes + // for small objects. + + // initialize state + if (z == Z_NULL) return Z_STREAM_ERROR; + z->msg = Z_NULL; + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; + if ((z->state = (struct internal_state *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + // handle undocumented nowrap option (no zlib header or check) + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + // create inflate_blocks state + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Tracev((stderr, "inflate: allocated\n")); + + // reset state + inflateReset(z); + return Z_OK; +} + + + +#define IM_NEEDBYTE {if(z->avail_in==0)return r;r=f;} +#define IM_NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z_streamp z, int f) +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + for (;;) switch (z->state->mode) + { + case IM_METHOD: + IM_NEEDBYTE + if (((z->state->sub.method = IM_NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = IM_BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = IM_BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + z->state->mode = IM_FLAG; + case IM_FLAG: + IM_NEEDBYTE + b = IM_NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = IM_BLOCKS; + break; + } + z->state->mode = IM_DICT4; + case IM_DICT4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_DICT3; + case IM_DICT3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_DICT2; + case IM_DICT2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_DICT1; + case IM_DICT1: + IM_NEEDBYTE; r; + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = IM_DICT0; + return Z_NEED_DICT; + case IM_DICT0: + z->state->mode = IM_BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case IM_BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (r == Z_DATA_ERROR) + { + z->state->mode = IM_BAD; + z->state->sub.marker = 0; // can try inflateSync + break; + } + if (r == Z_OK) + r = f; + if (r != Z_STREAM_END) + return r; + r = f; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = IM_DONE; + break; + } + z->state->mode = IM_CHECK4; + case IM_CHECK4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_CHECK3; + case IM_CHECK3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_CHECK2; + case IM_CHECK2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_CHECK1; + case IM_CHECK1: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib check ok\n")); + z->state->mode = IM_DONE; + case IM_DONE: + return Z_STREAM_END; + case IM_BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } +} + + + +#ifdef _UNICODE + +static int GetAnsiFileName(LPCWSTR name, char * buf, int nBufSize) +{ + memset(buf, 0, nBufSize); + + int n = WideCharToMultiByte(CP_ACP, // code page + 0, // performance and mapping flags + name, // wide-character string + -1, // number of chars in string + buf, // buffer for new string + nBufSize, // size of buffer + NULL, // default for unmappable chars + NULL); // set when default char used + return n; +} + +static int GetUnicodeFileName(const char * name, LPWSTR buf, int nBufSize) +{ + memset(buf, 0, nBufSize*sizeof(TCHAR)); + + int n = MultiByteToWideChar(CP_ACP, // code page + 0, // character-type options + name, // string to map + -1, // number of bytes in string + buf, // wide-character buffer + nBufSize); // size of buffer + + return n; +} + +#endif + + +// unzip.c -- IO on .zip files using zlib +// Version 0.15 beta, Mar 19th, 1998, +// Read unzip.h for more info + + + + +#define UNZ_BUFSIZE (16384) +#define UNZ_MAXFILENAMEINZIP (256) +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = " ";//unzip 0.15 Copyright 1998 Gilles Vollant "; + +// unz_file_info_interntal contain internal info about a file in zipfile +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;// relative offset of local header 4 bytes +} unz_file_info_internal; + + +typedef struct +{ bool is_handle; // either a handle or memory + bool canseek; + // for handles: + HANDLE h; bool herr; unsigned long initial_offset; + // for memory: + void *buf; unsigned int len,pos; // if it's a memory block +} LUFILE; + + +LUFILE *lufopen(void *z,unsigned int len,DWORD flags,ZRESULT *err) +{ + if (flags!=ZIP_HANDLE && flags!=ZIP_FILENAME && flags!=ZIP_MEMORY) + { + *err=ZR_ARGS; + return NULL; + } + // + HANDLE h=0; bool canseek=false; *err=ZR_OK; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + if (flags==ZIP_HANDLE) + { + HANDLE hf = z; + + BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&h,0,FALSE,DUPLICATE_SAME_ACCESS); + + if (!res) + { + *err=ZR_NODUPH; + return NULL; + } + } + else + { + h = CreateFile((const TCHAR *)z, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) + { + *err = ZR_NOFILE; + return NULL; + } + } + DWORD type = GetFileType(h); + canseek = (type==FILE_TYPE_DISK); + } + LUFILE *lf = new LUFILE; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + lf->is_handle=true; + lf->canseek=canseek; + lf->h=h; lf->herr=false; + lf->initial_offset=0; + if (canseek) + lf->initial_offset = SetFilePointer(h,0,NULL,FILE_CURRENT); + } + else + { + lf->is_handle=false; + lf->canseek=true; + lf->buf=z; + lf->len=len; + lf->pos=0; + lf->initial_offset=0; + } + *err=ZR_OK; + return lf; +} + + +int lufclose(LUFILE *stream) +{ if (stream==NULL) return EOF; + if (stream->is_handle) CloseHandle(stream->h); + delete stream; + return 0; +} + +int luferror(LUFILE *stream) +{ if (stream->is_handle && stream->herr) return 1; + else return 0; +} + +long int luftell(LUFILE *stream) +{ if (stream->is_handle && stream->canseek) return SetFilePointer(stream->h,0,NULL,FILE_CURRENT)-stream->initial_offset; + else if (stream->is_handle) return 0; + else return stream->pos; +} + +int lufseek(LUFILE *stream, long offset, int whence) +{ if (stream->is_handle && stream->canseek) + { if (whence==SEEK_SET) SetFilePointer(stream->h,stream->initial_offset+offset,0,FILE_BEGIN); + else if (whence==SEEK_CUR) SetFilePointer(stream->h,offset,NULL,FILE_CURRENT); + else if (whence==SEEK_END) SetFilePointer(stream->h,offset,NULL,FILE_END); + else return 19; // EINVAL + return 0; + } + else if (stream->is_handle) return 29; // ESPIPE + else + { if (whence==SEEK_SET) stream->pos=offset; + else if (whence==SEEK_CUR) stream->pos+=offset; + else if (whence==SEEK_END) stream->pos=stream->len+offset; + return 0; + } +} + + +size_t lufread(void *ptr,size_t size,size_t n,LUFILE *stream) +{ unsigned int toread = (unsigned int)(size*n); + if (stream->is_handle) + { DWORD red; BOOL res = ReadFile(stream->h,ptr,toread,&red,NULL); + if (!res) stream->herr=true; + return red/size; + } + if (stream->pos+toread > stream->len) toread = stream->len-stream->pos; + memcpy(ptr, (char*)stream->buf + stream->pos, toread); DWORD red = toread; + stream->pos += red; + return red/size; +} + + + + +// file_in_zip_read_info_s contain internal information about a file in zipfile, +// when reading and decompress it +typedef struct +{ + char *read_buffer; // internal buffer for compressed data + z_stream stream; // zLib stream structure for inflate + + uLong pos_in_zipfile; // position in byte on the zipfile, for fseek + uLong stream_initialised; // flag set if stream structure is initialised + + uLong offset_local_extrafield;// offset of the local extra field + uInt size_local_extrafield;// size of the local extra field + uLong pos_local_extrafield; // position in the local extra field in read + + uLong crc32; // crc32 of all data uncompressed + uLong crc32_wait; // crc32 we must obtain after decompress all + uLong rest_read_compressed; // number of byte to be decompressed + uLong rest_read_uncompressed;//number of byte to be obtained after decomp + LUFILE* file; // io structore of the zipfile + uLong compression_method; // compression method (0==store) + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) +} file_in_zip_read_info_s; + + +// unz_s contain internal information about the zipfile +typedef struct +{ + LUFILE* file; // io structore of the zipfile + unz_global_info gi; // public global information + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) + uLong num_file; // number of the current file in the zipfile + uLong pos_in_central_dir; // pos of the current file in the central dir + uLong current_file_ok; // flag about the usability of the current file + uLong central_pos; // position of the beginning of the central dir + + uLong size_central_dir; // size of the central directory + uLong offset_central_dir; // offset of start of central directory with respect to the starting disk number + + unz_file_info cur_file_info; // public info about the current file in zip + unz_file_info_internal cur_file_info_internal; // private info about it + file_in_zip_read_info_s* pfile_in_zip_read; // structure about the current file if we are decompressing it +} unz_s, *unzFile; + + +int unzStringFileNameCompare (const char* fileName1,const char* fileName2,int iCaseSensitivity); +// Compare two filename (fileName1,fileName2). + +z_off_t unztell (unzFile file); +// Give the current position in uncompressed data + +int unzeof (unzFile file); +// return 1 if the end of file was reached, 0 elsewhere + +int unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len); +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// +// if buf==NULL, it return the size of the local extra field +// +// if buf!=NULL, len is the size of the buffer, the extra header is copied in +// buf. +// the return value is the number of bytes copied in buf, or (if <0) +// the error code + + + +// =========================================================================== +// Read a byte from a gz_stream; update next_in and avail_in. Return EOF +// for end of file. +// IN assertion: the stream s has been sucessfully opened for reading. + +int unzlocal_getByte(LUFILE *fin,int *pi) +{ unsigned char c; + int err = (int)lufread(&c, 1, 1, fin); + if (err==1) + { *pi = (int)c; + return UNZ_OK; + } + else + { if (luferror(fin)) return UNZ_ERRNO; + else return UNZ_EOF; + } +} + + +// =========================================================================== +// Reads a long in LSB order from the given gz_stream. Sets +int unzlocal_getShort (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +int unzlocal_getLong (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +// My own strcmpi / strcasecmp +int strcmpcasenosensitive_internal (const char* fileName1,const char *fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= (char)0x20; + if ((c2>='a') && (c2<='z')) + c2 -= (char)0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + + + +// +// Compare two filename (fileName1,fileName2). +// If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) +// If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) +// +int unzStringFileNameCompare (const char*fileName1,const char*fileName2,int iCaseSensitivity) +{ if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); + else return strcmpcasenosensitive_internal(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + + +// Locate the Central directory of a zipfile (at the end, just before +// the global comment) +uLong unzlocal_SearchCentralDir(LUFILE *fin) +{ if (lufseek(fin,0,SEEK_END) != 0) return 0; + uLong uSizeFile = luftell(fin); + + uLong uMaxBack=0xffff; // maximum size of global comment + if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; + + unsigned char *buf = (unsigned char*)zmalloc(BUFREADCOMMENT+4); + if (buf==NULL) return 0; + uLong uPosFound=0; + + uLong uBackRead = 4; + while (uBackReaduMaxBack) uBackRead = uMaxBack; + else uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (lufseek(fin,uReadPos,SEEK_SET)!=0) break; + if (lufread(buf,(uInt)uReadSize,1,fin)!=1) break; + for (i=(int)uReadSize-3; (i--)>0;) + { if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { uPosFound = uReadPos+i; break; + } + } + if (uPosFound!=0) break; + } + if (buf) zfree(buf); + return uPosFound; +} + + +int unzGoToFirstFile (unzFile file); +int unzCloseCurrentFile (unzFile file); + +// Open a Zip file. +// If the zipfile cannot be opened (file don't exist or in not valid), return NULL. +// Otherwise, the return value is a unzFile Handle, usable with other unzip functions +unzFile unzOpenInternal(LUFILE *fin) +{ + zopenerror = ZR_OK; //+++1.2 + if (fin==NULL) { zopenerror = ZR_ARGS; return NULL; } //+++1.2 + if (unz_copyright[0]!=' ') {lufclose(fin); zopenerror = ZR_CORRUPT; return NULL; } //+++1.2 + + int err=UNZ_OK; + unz_s us; + uLong central_pos,uL; + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) err=UNZ_ERRNO; + if (lufseek(fin,central_pos,SEEK_SET)!=0) err=UNZ_ERRNO; + // the signature, already checked + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) err=UNZ_ERRNO; + // number of this disk + uLong number_disk; // number of the current dist, used for spanning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; + // number of the disk with the start of the central directory + uLong number_disk_with_CD; // number the the disk with central dir, used for spaning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir on this disk + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir + uLong number_entry_CD; // total number of entries in the central dir (same than number_entry on nospan) + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; + if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; + // size of the central directory + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // offset of start of central directory with respect to the starting disk number + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // zipfile comment length + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; + if ((central_pos+fin->initial_offsetinitial_offset - (us.offset_central_dir+us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + fin->initial_offset = 0; // since the zipfile itself is expected to handle this + + unz_s *s = (unz_s*)zmalloc(sizeof(unz_s)); + *s=us; + unzGoToFirstFile((unzFile)s); + return (unzFile)s; +} + + + +// Close a ZipFile opened with unzipOpen. +// If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), +// these files MUST be closed with unzipCloseCurrentFile before call unzipClose. +// return UNZ_OK if there is no problem. +int unzClose (unzFile file) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + lufclose(s->file); + if (s) zfree(s); // unused s=0; + return UNZ_OK; +} + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +// Translate date/time from Dos format to tm_unz (readable more easilty) +void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +// Get Info about the current file in the zipfile, with internal only info +int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize); + +int unzlocal_GetCurrentFileInfoInternal (unzFile file, unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, char *szFileName, + uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (lufseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + // we check the magic + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (lufread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (lufread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + {} // unused lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (lufread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + //unused lSeek+=file_info.size_file_comment - uSizeRead; + } + else {} //unused lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, + char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, szComment,commentBufferSize); +} + + +// Set the current file of the zipfile to the first file. +// return UNZ_OK if there is no problem +int unzGoToFirstFile (unzFile file) +{ + int err; + unz_s* s; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Set the current file of the zipfile to the next file. +// return UNZ_OK if there is no problem +// return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +int unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Try locate the file szFileName in the zipfile. +// For the iCaseSensitivity signification, see unzStringFileNameCompare +// return value : +// UNZ_OK if the file is found. It becomes the current file. +// UNZ_END_OF_LIST_OF_FILE if the file is not found +int unzLocateFile (unzFile file, const TCHAR *szFileName, int iCaseSensitivity) +{ + unz_s* s; + int err; + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + if (file==NULL) + return UNZ_PARAMERROR; + + if (_tcslen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + char szFileNameA[MAX_PATH]; + +#ifdef _UNICODE + GetAnsiFileName(szFileName, szFileNameA, MAX_PATH-1); +#else + strcpy(szFileNameA, szFileName); +#endif + + // support Windows subdirectory by:daviyang35 + int iLen=strlen(szFileNameA); + for (int i=0;icurrent_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName,szFileNameA,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +// Read the local header of the current zipfile +// Check the coherency of the local header and info in the end of central +// directory about this file +// store in *piSizeVar the size of extra info in local header +// (filename and size of extra field data) +int unzlocal_CheckCurrentFileCoherencyHeader (unz_s *s,uInt *piSizeVar, + uLong *poffset_local_extrafield, uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (lufseek(s->file,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +// else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) +// err=UNZ_BADZIPFILE; + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // date/time + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // crc + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size compr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size uncompr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + + + + + +// Open for reading data the current file in the zipfile. +// If there is no error and the file is opened, the return value is UNZ_OK. +int unzOpenCurrentFile (unzFile file) +{ + int err; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; // offset of the local extra field + uInt size_local_extrafield; // size of the local extra field + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*)zmalloc(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)zmalloc(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); //unused pfile_in_zip_read_info=0; + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) + { // unused err=UNZ_BADZIPFILE; + } + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + // windowBits is passed < 0 to tell that there is no zlib header. + // Note that in this case inflate *requires* an extra "dummy" byte + // after the compressed stream in order to complete decompression and + // return Z_STREAM_END. + // In unzip, i don't wait absolutely Z_STREAM_END because I known the + // size of both compressed and uncompressed data + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +// Read bytes from the current file. +// buf contain buffer where data must be copied +// len the size of buf. +// return the number of byte copied if somes bytes are copied +// return 0 if the end of file was reached +// return <0 with error code if there is an error +// (UNZ_ERRNO for IO error, or zLib error for uncompress error) +int unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ int err=UNZ_OK; + uInt iRead = 0; + + unz_s *s = (unz_s*)file; + if (s==NULL) return UNZ_PARAMERROR; + + file_in_zip_read_info_s* pfile_in_zip_read_info = s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; + if ((pfile_in_zip_read_info->read_buffer == NULL)) return UNZ_END_OF_LIST_OF_FILE; + if (len==0) return 0; + + pfile_in_zip_read_info->stream.next_out = (Byte*)buf; + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + { pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + } + + while (pfile_in_zip_read_info->stream.avail_out>0) + { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) + { uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) return UNZ_EOF; + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) return UNZ_ERRNO; + if (lufread(pfile_in_zip_read_info->read_buffer,uReadThis,1,pfile_in_zip_read_info->file)!=1) return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + pfile_in_zip_read_info->stream.next_in = (Byte*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) + { uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + } + else + { uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + } + for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); + } + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,pfile_in_zip_read_info->stream.next_out,uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { uLong uTotalOutBefore,uTotalOutAfter; + const Byte *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + err=inflate(&pfile_in_zip_read_info->stream,flush); + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,bufBefore,(uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; //+++1.3 + //if (err==Z_STREAM_END) return (iRead==len) ? UNZ_EOF : iRead; //+++1.2 + + if (err != Z_OK) break; + } + } + + if (err==Z_OK) return iRead; + + return iRead; +} + + +// Give the current position in uncompressed data +z_off_t unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +// return 1 if the end of file was reached, 0 elsewhere +int unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// if buf==NULL, it return the size of the local extra field that can be read +// if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. +// the return value is the number of bytes copied in buf, or (if <0) the error code +int unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (lufread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +// Close the file in zip opened with unzipOpenCurrentFile +// Return UNZ_CRCERROR if all the file was read but the CRC is not good +int unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + if (pfile_in_zip_read_info->read_buffer!=0) + { void *buf = pfile_in_zip_read_info->read_buffer; + zfree(buf); + pfile_in_zip_read_info->read_buffer=0; + } + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); // unused pfile_in_zip_read_info=0; + + s->pfile_in_zip_read=NULL; + + return err; +} + + +// Get the global comment string of the ZipFile, in the szComment buffer. +// uSizeBuf is the size of the szComment buffer. +// return the number of byte copied or an error code <0 +int unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) +{ //int err=UNZ_OK; + unz_s* s; + uLong uReadThis ; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; + if (lufseek(s->file,s->central_pos+22,SEEK_SET)!=0) return UNZ_ERRNO; + if (uReadThis>0) + { *szComment='\0'; + if (lufread(szComment,(uInt)uReadThis,1,s->file)!=1) return UNZ_ERRNO; + } + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + + + + + +int unzOpenCurrentFile (unzFile file); +int unzReadCurrentFile (unzFile file, void *buf, unsigned len); +int unzCloseCurrentFile (unzFile file); + + +FILETIME timet2filetime(time_t timer) +{ + struct tm *tm = gmtime(&timer); + SYSTEMTIME st; + st.wYear = (WORD)(tm->tm_year+1900); + st.wMonth = (WORD)(tm->tm_mon+1); + st.wDay = (WORD)(tm->tm_mday); + st.wHour = (WORD)(tm->tm_hour); + st.wMinute = (WORD)(tm->tm_min); + st.wSecond = (WORD)(tm->tm_sec); + st.wMilliseconds=0; + FILETIME ft; + SystemTimeToFileTime(&st,&ft); + return ft; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class TUnzip +{ public: + TUnzip() : uf(0), currentfile(-1), czei(-1) {} + + unzFile uf; int currentfile; ZIPENTRY cze; int czei; + TCHAR rootdir[MAX_PATH]; + + ZRESULT Open(void *z,unsigned int len,DWORD flags); + ZRESULT Get(int index,ZIPENTRY *ze); + ZRESULT Find(const TCHAR *name,bool ic,int *index,ZIPENTRY *ze); + ZRESULT Unzip(int index,void *dst,unsigned int len,DWORD flags); + ZRESULT Close(); +}; + + +ZRESULT TUnzip::Open(void *z,unsigned int len,DWORD flags) +{ + if (uf!=0 || currentfile!=-1) + return ZR_NOTINITED; + GetCurrentDirectory(MAX_PATH,rootdir); + _tcscat(rootdir,_T("\\")); + if (flags==ZIP_HANDLE) + { + DWORD type = GetFileType(z); + if (type!=FILE_TYPE_DISK) + return ZR_SEEK; + } + ZRESULT e; + LUFILE *f = lufopen(z,len,flags,&e); + if (f==NULL) + return e; + uf = unzOpenInternal(f); + //return ZR_OK; + return zopenerror; //+++1.2 +} + +ZRESULT TUnzip::Get(int index,ZIPENTRY *ze) +{ if (index<-1 || index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index==czei && index!=-1) {memcpy(ze,&cze,sizeof(ZIPENTRY)); return ZR_OK;} + if (index==-1) + { ze->index = uf->gi.number_entry; + ze->name[0]=0; + ze->attr=0; + ze->atime.dwLowDateTime=0; ze->atime.dwHighDateTime=0; + ze->ctime.dwLowDateTime=0; ze->ctime.dwHighDateTime=0; + ze->mtime.dwLowDateTime=0; ze->mtime.dwHighDateTime=0; + ze->comp_size=0; + ze->unc_size=0; + return ZR_OK; + } + if (index<(int)uf->num_file) unzGoToFirstFile(uf); + while ((int)uf->num_filefile,offset,SEEK_SET)!=0) return ZR_READ; + char *extra = new char[extralen]; + if (lufread(extra,1,(uInt)extralen,uf->file)!=extralen) {delete[] extra; return ZR_READ;} + // + ze->index=uf->num_file; + strcpy(ze->name,fn); + // zip has an 'attribute' 32bit value. Its lower half is windows stuff + // its upper half is standard unix attr. + unsigned long a = ufi.external_fa; + bool uisdir = (a&0x40000000)!=0; + //bool uwriteable= (a&0x08000000)!=0; + bool uwriteable= (a&0x00800000)!=0; // ***hd*** + //bool ureadable= (a&0x01000000)!=0; + //bool uexecutable=(a&0x00400000)!=0; + bool wreadonly= (a&0x00000001)!=0; + bool whidden= (a&0x00000002)!=0; + bool wsystem= (a&0x00000004)!=0; + bool wisdir= (a&0x00000010)!=0; + bool warchive= (a&0x00000020)!=0; + ze->attr=FILE_ATTRIBUTE_NORMAL; + if (uisdir || wisdir) ze->attr |= FILE_ATTRIBUTE_DIRECTORY; + if (warchive) ze->attr|=FILE_ATTRIBUTE_ARCHIVE; + if (whidden) ze->attr|=FILE_ATTRIBUTE_HIDDEN; + if (!uwriteable||wreadonly) ze->attr|=FILE_ATTRIBUTE_READONLY; + if (wsystem) ze->attr|=FILE_ATTRIBUTE_SYSTEM; + ze->comp_size = ufi.compressed_size; + ze->unc_size = ufi.uncompressed_size; + // + WORD dostime = (WORD)(ufi.dosDate&0xFFFF); + WORD dosdate = (WORD)((ufi.dosDate>>16)&0xFFFF); + FILETIME ft; + DosDateTimeToFileTime(dosdate,dostime,&ft); + ze->atime=ft; ze->ctime=ft; ze->mtime=ft; + // the zip will always have at least that dostime. But if it also has + // an extra header, then we'll instead get the info from that. + unsigned int epos=0; + while (epos+4mtime = timet2filetime(mtime); + } + if (hasatime) + { time_t atime = *(__time32_t*)(extra+epos); epos+=4; + ze->atime = timet2filetime(atime); + } + if (hasctime) + { time_t ctime = *(__time32_t*)(extra+epos); + ze->ctime = timet2filetime(ctime); + } + break; + } + // + if (extra!=0) delete[] extra; + memcpy(&cze,ze,sizeof(ZIPENTRY)); czei=index; + return ZR_OK; +} + +ZRESULT TUnzip::Find(const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + int res = unzLocateFile(uf,name,ic?CASE_INSENSITIVE:CASE_SENSITIVE); + if (res!=UNZ_OK) + { + if (index!=0) + *index=-1; + if (ze!=NULL) + { + ZeroMemory(ze,sizeof(ZIPENTRY)); ze->index=-1; + } + return ZR_NOTFOUND; + } + if (currentfile!=-1) + unzCloseCurrentFile(uf); currentfile=-1; + int i = (int)uf->num_file; + if (index!=NULL) + *index=i; + if (ze!=NULL) + { + ZRESULT zres = Get(i,ze); + if (zres!=ZR_OK) + return zres; + } + return ZR_OK; +} + +void EnsureDirectory(const TCHAR *rootdir, const TCHAR *dir) +{ + if (dir==NULL || dir[0] == _T('\0')) + return; + const TCHAR *lastslash = dir, *c = lastslash; + while (*c != _T('\0')) + { + if (*c==_T('/') || *c==_T('\\')) + lastslash=c; + c++; + } + const TCHAR *name=lastslash; + if (lastslash!=dir) + { + TCHAR tmp[MAX_PATH]; + _tcsncpy(tmp, dir, lastslash-dir); + tmp[lastslash-dir] = _T('\0'); + EnsureDirectory(rootdir,tmp); + name++; + } + TCHAR cd[MAX_PATH]; + _tcscpy(cd,rootdir); + //_tcscat(cd,name); + _tcscat(cd,dir); //+++1.2 + CreateDirectory(cd,NULL); +} + +ZRESULT TUnzip::Unzip(int index,void *dst,unsigned int len,DWORD flags) +{ + if (flags!=ZIP_MEMORY && flags!=ZIP_FILENAME && flags!=ZIP_HANDLE) + return ZR_ARGS; + if (flags==ZIP_MEMORY) + { + if (index!=currentfile) + { + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (index<(int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_file0) + return ZR_MORE; + unzCloseCurrentFile(uf); + currentfile=-1; + if (res==0) + return ZR_OK; + else + return ZR_FLATE; + } + + // otherwise we're writing to a handle or a file + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index >= (int)uf->gi.number_entry) + return ZR_ARGS; + if (index < (int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_filelen) n=len-1; + strncpy(buf,msg,n); buf[n]=0; + return mlen; +} + + +typedef struct +{ DWORD flag; + TUnzip *unz; +} TUnzipHandleData; + +HZIP OpenZipU(void *z,unsigned int len,DWORD flags) +{ + TUnzip *unz = new TUnzip(); + lasterrorU = unz->Open(z,len,flags); + if (lasterrorU!=ZR_OK) + { + delete unz; + return 0; + } + TUnzipHandleData *han = new TUnzipHandleData; + han->flag=1; + han->unz=unz; + return (HZIP)han; +} + +ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Get(index,ze); + return lasterrorU; +} + +ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Get(index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + return lasterrorU; +} + +ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Find(name,ic,index,ze); + return lasterrorU; +} + +ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Find(name,ic,index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + + return lasterrorU; +} + +ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Unzip(index,dst,len,flags); + return lasterrorU; +} + +ZRESULT CloseZipU(HZIP hz) +{ if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} + TUnzip *unz = han->unz; + lasterrorU = unz->Close(); + delete unz; + delete han; + return lasterrorU; +} + +bool IsZipHandleU(HZIP hz) +{ if (hz==0) return true; + TUnzipHandleData *han = (TUnzipHandleData*)hz; + return (han->flag==1); +} + + diff --git a/DuiLib/Utils/downloadmgr.h b/DuiLib/Utils/downloadmgr.h new file mode 100644 index 00000000..2575840a --- /dev/null +++ b/DuiLib/Utils/downloadmgr.h @@ -0,0 +1,211 @@ + +#pragma warning( disable: 4049 ) /* more than 64k source lines */ + +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + + /* File created by MIDL compiler version 5.03.0279 */ +/* at Mon Jul 23 17:42:46 2001 + */ +/* Compiler settings for downloadmgr.idl: + Oicf (OptLev=i2), W1, Zp8, env=Win32 (32b run), ms_ext, c_ext + error checks: allocation ref bounds_check enum stub_data + VC __declspec() decoration level: + __declspec(uuid()), __declspec(selectany), __declspec(novtable) + DECLSPEC_UUID(), MIDL_INTERFACE() +*/ +//@@MIDL_FILE_HEADING( ) + + +/* verify that the version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCNDR_H_VERSION__ +#define __REQUIRED_RPCNDR_H_VERSION__ 440 +#endif + +#include "rpc.h" +#include "rpcndr.h" + +#ifndef __RPCNDR_H_VERSION__ +#error this stub requires an updated version of +#endif // __RPCNDR_H_VERSION__ + +#ifndef COM_NO_WINDOWS_H +#include "windows.h" +#include "ole2.h" +#endif /*COM_NO_WINDOWS_H*/ + +#ifndef __downloadmgr_h__ +#define __downloadmgr_h__ + +/* Forward Declarations */ + +#ifndef __IDownloadManager_FWD_DEFINED__ +#define __IDownloadManager_FWD_DEFINED__ +typedef interface IDownloadManager IDownloadManager; +#endif /* __IDownloadManager_FWD_DEFINED__ */ + + +/* header files for imported files */ +#include "unknwn.h" +#include "ocidl.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t); +void __RPC_USER MIDL_user_free( void __RPC_FAR * ); + +/* interface __MIDL_itf_downloadmgr_0000 */ +/* [local] */ + +//=--------------------------------------------------------------------------= +// downloadmgr.h +//=--------------------------------------------------------------------------= +// (C) Copyright 2000 Microsoft Corporation. All Rights Reserved. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +//=--------------------------------------------------------------------------= + +#pragma comment(lib,"uuid.lib") + +//---------------------------------------------------------------------------= +// Internet Explorer Download Manager Interfaces + +// -------------------------------------------------------------------------------- +// GUIDS +// -------------------------------------------------------------------------------- +// {988934A4-064B-11D3-BB80-00104B35E7F9} +DEFINE_GUID(IID_IDownloadManager, 0x988934a4, 0x064b, 0x11d3, 0xbb, 0x80, 0x0, 0x10, 0x4b, 0x35, 0xe7, 0xf9); +#define SID_SDownloadManager IID_IDownloadManager + + + +extern RPC_IF_HANDLE __MIDL_itf_downloadmgr_0000_v0_0_c_ifspec; +extern RPC_IF_HANDLE __MIDL_itf_downloadmgr_0000_v0_0_s_ifspec; + +#ifndef __IDownloadManager_INTERFACE_DEFINED__ +#define __IDownloadManager_INTERFACE_DEFINED__ + +/* interface IDownloadManager */ +/* [local][unique][uuid][object][helpstring] */ + + +EXTERN_C const IID IID_IDownloadManager; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("988934A4-064B-11D3-BB80-00104B35E7F9") + IDownloadManager : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Download( + /* [in] */ IMoniker __RPC_FAR *pmk, + /* [in] */ IBindCtx __RPC_FAR *pbc, + /* [in] */ DWORD dwBindVerb, + /* [in] */ LONG grfBINDF, + /* [in] */ BINDINFO __RPC_FAR *pBindInfo, + /* [in] */ LPCOLESTR pszHeaders, + /* [in] */ LPCOLESTR pszRedir, + /* [in] */ UINT uiCP) = 0; + + }; + +#else /* C style interface */ + + typedef struct IDownloadManagerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IDownloadManager __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IDownloadManager __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IDownloadManager __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Download )( + IDownloadManager __RPC_FAR * This, + /* [in] */ IMoniker __RPC_FAR *pmk, + /* [in] */ IBindCtx __RPC_FAR *pbc, + /* [in] */ DWORD dwBindVerb, + /* [in] */ LONG grfBINDF, + /* [in] */ BINDINFO __RPC_FAR *pBindInfo, + /* [in] */ LPCOLESTR pszHeaders, + /* [in] */ LPCOLESTR pszRedir, + /* [in] */ UINT uiCP); + + END_INTERFACE + } IDownloadManagerVtbl; + + interface IDownloadManager + { + CONST_VTBL struct IDownloadManagerVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IDownloadManager_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IDownloadManager_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IDownloadManager_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IDownloadManager_Download(This,pmk,pbc,dwBindVerb,grfBINDF,pBindInfo,pszHeaders,pszRedir,uiCP) \ + (This)->lpVtbl -> Download(This,pmk,pbc,dwBindVerb,grfBINDF,pBindInfo,pszHeaders,pszRedir,uiCP) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IDownloadManager_Download_Proxy( + IDownloadManager __RPC_FAR * This, + /* [in] */ IMoniker __RPC_FAR *pmk, + /* [in] */ IBindCtx __RPC_FAR *pbc, + /* [in] */ DWORD dwBindVerb, + /* [in] */ LONG grfBINDF, + /* [in] */ BINDINFO __RPC_FAR *pBindInfo, + /* [in] */ LPCOLESTR pszHeaders, + /* [in] */ LPCOLESTR pszRedir, + /* [in] */ UINT uiCP); + + +void __RPC_STUB IDownloadManager_Download_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IDownloadManager_INTERFACE_DEFINED__ */ + + +/* Additional Prototypes for ALL interfaces */ + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/DuiLib/Utils/flash11.tlh b/DuiLib/Utils/flash11.tlh new file mode 100644 index 00000000..4dee0385 --- /dev/null +++ b/DuiLib/Utils/flash11.tlh @@ -0,0 +1,384 @@ +// Created by Microsoft (R) C/C++ Compiler Version 10.00.30319.01 (9b1e6a3c). +// +// f:\duilib\duilib\duilib\build\debug\flash11.tlh +// +// C++ source equivalent of Win32 type library C:\Windows\System32\Macromed\Flash\flash11.ocx +// compiler-generated file created 12/07/12 at 22:56:25 - DO NOT EDIT! + +#pragma once +#pragma pack(push, 8) + +#include + +// +// Forward references and typedefs +// + +struct __declspec(uuid("d27cdb6b-ae6d-11cf-96b8-444553540000")) +/* LIBID */ __ShockwaveFlashObjects; +struct __declspec(uuid("d27cdb6c-ae6d-11cf-96b8-444553540000")) +/* dual interface */ IShockwaveFlash; +struct __declspec(uuid("c5598e60-b307-11d1-b27d-006008c3fbfb")) +/* interface */ ICanHandleException; +struct __declspec(uuid("d27cdb6d-ae6d-11cf-96b8-444553540000")) +/* dispinterface */ _IShockwaveFlashEvents; +struct /* coclass */ ShockwaveFlash; +struct __declspec(uuid("d27cdb70-ae6d-11cf-96b8-444553540000")) +/* interface */ IFlashFactory; +struct __declspec(uuid("d27cdb72-ae6d-11cf-96b8-444553540000")) +/* interface */ IFlashObjectInterface; +struct __declspec(uuid("a6ef9860-c720-11d0-9337-00a0c90dcaa9")) +/* interface */ IMyDispatchEx; +struct /* coclass */ FlashObjectInterface; +struct __declspec(uuid("86230738-d762-4c50-a2de-a753e5b1686f")) +/* dual interface */ IFlashObject; +struct /* coclass */ FlashObject; + +// +// Smart pointer typedef declarations +// + +_COM_SMARTPTR_TYPEDEF(IShockwaveFlash, __uuidof(IShockwaveFlash)); +_COM_SMARTPTR_TYPEDEF(ICanHandleException, __uuidof(ICanHandleException)); +_COM_SMARTPTR_TYPEDEF(_IShockwaveFlashEvents, __uuidof(_IShockwaveFlashEvents)); +_COM_SMARTPTR_TYPEDEF(IFlashFactory, __uuidof(IFlashFactory)); +_COM_SMARTPTR_TYPEDEF(IMyDispatchEx, __uuidof(IMyDispatchEx)); +_COM_SMARTPTR_TYPEDEF(IFlashObjectInterface, __uuidof(IFlashObjectInterface)); +_COM_SMARTPTR_TYPEDEF(IFlashObject, __uuidof(IFlashObject)); + +// +// Type library items +// + +struct __declspec(uuid("d27cdb6c-ae6d-11cf-96b8-444553540000")) +IShockwaveFlash : IDispatch +{ + // + // Raw methods provided by interface + // + + virtual HRESULT __stdcall get_ReadyState ( + /*[out,retval]*/ long * pVal ) = 0; + virtual HRESULT __stdcall get_TotalFrames ( + /*[out,retval]*/ long * pVal ) = 0; + virtual HRESULT __stdcall get_Playing ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_Playing ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; + virtual HRESULT __stdcall get_Quality ( + /*[out,retval]*/ int * pVal ) = 0; + virtual HRESULT __stdcall put_Quality ( + /*[in]*/ int pVal ) = 0; + virtual HRESULT __stdcall get_ScaleMode ( + /*[out,retval]*/ int * pVal ) = 0; + virtual HRESULT __stdcall put_ScaleMode ( + /*[in]*/ int pVal ) = 0; + virtual HRESULT __stdcall get_AlignMode ( + /*[out,retval]*/ int * pVal ) = 0; + virtual HRESULT __stdcall put_AlignMode ( + /*[in]*/ int pVal ) = 0; + virtual HRESULT __stdcall get_BackgroundColor ( + /*[out,retval]*/ long * pVal ) = 0; + virtual HRESULT __stdcall put_BackgroundColor ( + /*[in]*/ long pVal ) = 0; + virtual HRESULT __stdcall get_Loop ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_Loop ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; + virtual HRESULT __stdcall get_Movie ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_Movie ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_FrameNum ( + /*[out,retval]*/ long * pVal ) = 0; + virtual HRESULT __stdcall put_FrameNum ( + /*[in]*/ long pVal ) = 0; + virtual HRESULT __stdcall SetZoomRect ( + /*[in]*/ long left, + /*[in]*/ long top, + /*[in]*/ long right, + /*[in]*/ long bottom ) = 0; + virtual HRESULT __stdcall Zoom ( + /*[in]*/ int factor ) = 0; + virtual HRESULT __stdcall Pan ( + /*[in]*/ long x, + /*[in]*/ long y, + /*[in]*/ int mode ) = 0; + virtual HRESULT __stdcall Play ( ) = 0; + virtual HRESULT __stdcall Stop ( ) = 0; + virtual HRESULT __stdcall Back ( ) = 0; + virtual HRESULT __stdcall Forward ( ) = 0; + virtual HRESULT __stdcall Rewind ( ) = 0; + virtual HRESULT __stdcall StopPlay ( ) = 0; + virtual HRESULT __stdcall GotoFrame ( + /*[in]*/ long FrameNum ) = 0; + virtual HRESULT __stdcall CurrentFrame ( + /*[out,retval]*/ long * FrameNum ) = 0; + virtual HRESULT __stdcall IsPlaying ( + /*[out,retval]*/ VARIANT_BOOL * Playing ) = 0; + virtual HRESULT __stdcall PercentLoaded ( + /*[out,retval]*/ long * percent ) = 0; + virtual HRESULT __stdcall FrameLoaded ( + /*[in]*/ long FrameNum, + /*[out,retval]*/ VARIANT_BOOL * loaded ) = 0; + virtual HRESULT __stdcall FlashVersion ( + /*[out,retval]*/ long * version ) = 0; + virtual HRESULT __stdcall get_WMode ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_WMode ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_SAlign ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_SAlign ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_Menu ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_Menu ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; + virtual HRESULT __stdcall get_Base ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_Base ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_Scale ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_Scale ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_DeviceFont ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_DeviceFont ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; + virtual HRESULT __stdcall get_EmbedMovie ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_EmbedMovie ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; + virtual HRESULT __stdcall get_BGColor ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_BGColor ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_Quality2 ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_Quality2 ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall LoadMovie ( + /*[in]*/ int layer, + /*[in]*/ BSTR url ) = 0; + virtual HRESULT __stdcall TGotoFrame ( + /*[in]*/ BSTR target, + /*[in]*/ long FrameNum ) = 0; + virtual HRESULT __stdcall TGotoLabel ( + /*[in]*/ BSTR target, + /*[in]*/ BSTR label ) = 0; + virtual HRESULT __stdcall TCurrentFrame ( + /*[in]*/ BSTR target, + /*[out,retval]*/ long * FrameNum ) = 0; + virtual HRESULT __stdcall TCurrentLabel ( + /*[in]*/ BSTR target, + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall TPlay ( + /*[in]*/ BSTR target ) = 0; + virtual HRESULT __stdcall TStopPlay ( + /*[in]*/ BSTR target ) = 0; + virtual HRESULT __stdcall SetVariable ( + /*[in]*/ BSTR name, + /*[in]*/ BSTR value ) = 0; + virtual HRESULT __stdcall GetVariable ( + /*[in]*/ BSTR name, + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall TSetProperty ( + /*[in]*/ BSTR target, + /*[in]*/ int property, + /*[in]*/ BSTR value ) = 0; + virtual HRESULT __stdcall TGetProperty ( + /*[in]*/ BSTR target, + /*[in]*/ int property, + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall TCallFrame ( + /*[in]*/ BSTR target, + /*[in]*/ int FrameNum ) = 0; + virtual HRESULT __stdcall TCallLabel ( + /*[in]*/ BSTR target, + /*[in]*/ BSTR label ) = 0; + virtual HRESULT __stdcall TSetPropertyNum ( + /*[in]*/ BSTR target, + /*[in]*/ int property, + /*[in]*/ double value ) = 0; + virtual HRESULT __stdcall TGetPropertyNum ( + /*[in]*/ BSTR target, + /*[in]*/ int property, + /*[out,retval]*/ double * pVal ) = 0; + virtual HRESULT __stdcall TGetPropertyAsNumber ( + /*[in]*/ BSTR target, + /*[in]*/ int property, + /*[out,retval]*/ double * pVal ) = 0; + virtual HRESULT __stdcall get_SWRemote ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_SWRemote ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_FlashVars ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_FlashVars ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_AllowScriptAccess ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_AllowScriptAccess ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_MovieData ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_MovieData ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_InlineData ( + /*[out,retval]*/ IUnknown * * ppIUnknown ) = 0; + virtual HRESULT __stdcall put_InlineData ( + /*[in]*/ IUnknown * ppIUnknown ) = 0; + virtual HRESULT __stdcall get_SeamlessTabbing ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_SeamlessTabbing ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; + virtual HRESULT __stdcall EnforceLocalSecurity ( ) = 0; + virtual HRESULT __stdcall get_Profile ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_Profile ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; + virtual HRESULT __stdcall get_ProfileAddress ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_ProfileAddress ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_ProfilePort ( + /*[out,retval]*/ long * pVal ) = 0; + virtual HRESULT __stdcall put_ProfilePort ( + /*[in]*/ long pVal ) = 0; + virtual HRESULT __stdcall CallFunction ( + /*[in]*/ BSTR request, + /*[out,retval]*/ BSTR * response ) = 0; + virtual HRESULT __stdcall SetReturnValue ( + /*[in]*/ BSTR returnValue ) = 0; + virtual HRESULT __stdcall DisableLocalSecurity ( ) = 0; + virtual HRESULT __stdcall get_AllowNetworking ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_AllowNetworking ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_AllowFullScreen ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_AllowFullScreen ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_AllowFullScreenInteractive ( + /*[out,retval]*/ BSTR * pVal ) = 0; + virtual HRESULT __stdcall put_AllowFullScreenInteractive ( + /*[in]*/ BSTR pVal ) = 0; + virtual HRESULT __stdcall get_IsDependent ( + /*[out,retval]*/ VARIANT_BOOL * pVal ) = 0; + virtual HRESULT __stdcall put_IsDependent ( + /*[in]*/ VARIANT_BOOL pVal ) = 0; +}; + +struct __declspec(uuid("c5598e60-b307-11d1-b27d-006008c3fbfb")) +IMyCanHandleException : IUnknown +{ + // + // Raw methods provided by interface + // + + virtual HRESULT __stdcall CanHandleException ( + /*[in]*/ EXCEPINFO * pExcepInfo, + /*[in]*/ VARIANT * pvar ) = 0; +}; + +struct __declspec(uuid("d27cdb6d-ae6d-11cf-96b8-444553540000")) +_IShockwaveFlashEvents : IDispatch +{}; + +struct __declspec(uuid("d27cdb6e-ae6d-11cf-96b8-444553540000")) +ShockwaveFlash; + // [ default ] interface IShockwaveFlash + // [ default, source ] dispinterface _IShockwaveFlashEvents + +struct __declspec(uuid("d27cdb70-ae6d-11cf-96b8-444553540000")) +IFlashFactory : IUnknown +{}; + +struct __declspec(uuid("a6ef9860-c720-11d0-9337-00a0c90dcaa9")) +IMyDispatchEx : IDispatch +{ + // + // Raw methods provided by interface + // + + virtual HRESULT __stdcall GetDispID ( + /*[in]*/ BSTR bstrName, + /*[in]*/ unsigned long grfdex, + /*[out]*/ long * pid ) = 0; + virtual HRESULT __stdcall RemoteInvokeEx ( + /*[in]*/ long id, + /*[in]*/ unsigned long lcid, + /*[in]*/ unsigned long dwFlags, + /*[in]*/ DISPPARAMS * pdp, + /*[out]*/ VARIANT * pvarRes, + /*[out]*/ EXCEPINFO * pei, + /*[in]*/ struct IServiceProvider * pspCaller, + /*[in]*/ unsigned int cvarRefArg, + /*[in]*/ unsigned int * rgiRefArg, + /*[in,out]*/ VARIANT * rgvarRefArg ) = 0; + virtual HRESULT __stdcall DeleteMemberByName ( + /*[in]*/ BSTR bstrName, + /*[in]*/ unsigned long grfdex ) = 0; + virtual HRESULT __stdcall DeleteMemberByDispID ( + /*[in]*/ long id ) = 0; + virtual HRESULT __stdcall GetMemberProperties ( + /*[in]*/ long id, + /*[in]*/ unsigned long grfdexFetch, + /*[out]*/ unsigned long * pgrfdex ) = 0; + virtual HRESULT __stdcall GetMemberName ( + /*[in]*/ long id, + /*[out]*/ BSTR * pbstrName ) = 0; + virtual HRESULT __stdcall GetNextDispID ( + /*[in]*/ unsigned long grfdex, + /*[in]*/ long id, + /*[out]*/ long * pid ) = 0; + virtual HRESULT __stdcall GetNameSpaceParent ( + /*[out]*/ IUnknown * * ppunk ) = 0; +}; + +struct __declspec(uuid("d27cdb72-ae6d-11cf-96b8-444553540000")) +IFlashObjectInterface : IMyDispatchEx +{}; + +struct __declspec(uuid("d27cdb71-ae6d-11cf-96b8-444553540000")) +FlashObjectInterface; + // [ default ] interface IFlashObjectInterface + +struct __declspec(uuid("86230738-d762-4c50-a2de-a753e5b1686f")) +IFlashObject : IMyDispatchEx +{}; + +struct __declspec(uuid("e0920e11-6b65-4d5d-9c58-b1fc5c07dc43")) +FlashObject; + // [ default ] interface IFlashObject + +// +// Named GUID constants initializations +// + +extern "C" const GUID __declspec(selectany) LIBID_ShockwaveFlashObjects = + {0xd27cdb6b,0xae6d,0x11cf,{0x96,0xb8,0x44,0x45,0x53,0x54,0x00,0x00}}; +extern "C" const GUID __declspec(selectany) IID_IShockwaveFlash = + {0xd27cdb6c,0xae6d,0x11cf,{0x96,0xb8,0x44,0x45,0x53,0x54,0x00,0x00}}; +extern "C" const GUID __declspec(selectany) IID_IMyCanHandleException = + {0xc5598e60,0xb307,0x11d1,{0xb2,0x7d,0x00,0x60,0x08,0xc3,0xfb,0xfb}}; +extern "C" const GUID __declspec(selectany) DIID__IShockwaveFlashEvents = + {0xd27cdb6d,0xae6d,0x11cf,{0x96,0xb8,0x44,0x45,0x53,0x54,0x00,0x00}}; +extern "C" const GUID __declspec(selectany) CLSID_ShockwaveFlash = + {0xd27cdb6e,0xae6d,0x11cf,{0x96,0xb8,0x44,0x45,0x53,0x54,0x00,0x00}}; +extern "C" const GUID __declspec(selectany) IID_IFlashFactory = + {0xd27cdb70,0xae6d,0x11cf,{0x96,0xb8,0x44,0x45,0x53,0x54,0x00,0x00}}; +extern "C" const GUID __declspec(selectany) IID_IMyDispatchEx = + {0xa6ef9860,0xc720,0x11d0,{0x93,0x37,0x00,0xa0,0xc9,0x0d,0xca,0xa9}}; +extern "C" const GUID __declspec(selectany) IID_IFlashObjectInterface = + {0xd27cdb72,0xae6d,0x11cf,{0x96,0xb8,0x44,0x45,0x53,0x54,0x00,0x00}}; +extern "C" const GUID __declspec(selectany) CLSID_FlashObjectInterface = + {0xd27cdb71,0xae6d,0x11cf,{0x96,0xb8,0x44,0x45,0x53,0x54,0x00,0x00}}; +extern "C" const GUID __declspec(selectany) IID_IFlashObject = + {0x86230738,0xd762,0x4c50,{0xa2,0xde,0xa7,0x53,0xe5,0xb1,0x68,0x6f}}; +extern "C" const GUID __declspec(selectany) CLSID_FlashObject = + {0xe0920e11,0x6b65,0x4d5d,{0x9c,0x58,0xb1,0xfc,0x5c,0x07,0xdc,0x43}}; + +#pragma pack(pop) diff --git a/DuiLib/Utils/observer_impl_base.h b/DuiLib/Utils/observer_impl_base.h new file mode 100644 index 00000000..efc0cf7d --- /dev/null +++ b/DuiLib/Utils/observer_impl_base.h @@ -0,0 +1,139 @@ +#ifndef OBSERVER_IMPL_BASE_HPP +#define OBSERVER_IMPL_BASE_HPP + +#include +#include + +template +class ReceiverImplBase; + +template +class ObserverImplBase +{ +public: + virtual void AddReceiver(ReceiverImplBase* receiver) = 0; + virtual void RemoveReceiver(ReceiverImplBase* receiver) = 0; + virtual ReturnT Broadcast(ParamT param) = 0; + virtual ReturnT Notify(ParamT param) = 0; +}; + +template +class ReceiverImplBase +{ +public: + virtual void AddObserver(ObserverImplBase* observer) = 0; + virtual void RemoveObserver() = 0; + virtual ReturnT Receive(ParamT param) = 0; + virtual ReturnT Respond(ParamT param, ObserverImplBase* observer) = 0; +}; + +template +class ReceiverImpl; + +template +class ObserverImpl : public ObserverImplBase +{ +public: + ObserverImpl() + : count_(0) + {} + + virtual ~ObserverImpl() {} + + virtual void AddReceiver(ReceiverImplBase* receiver) + { + if (receiver == NULL) + return; + + receivers_[count_] = receiver; + receiver->AddObserver(this); + count_++; + } + + virtual void RemoveReceiver(ReceiverImplBase* receiver) + { + if (receiver == NULL) + return; + + ReceiversMap::iterator it = receivers_.begin(); + for (; it != receivers_.end(); ++it) + { + if (it->second == receiver) + { + receivers_.erase(it); + break; + } + } + } + + virtual ReturnT Broadcast(ParamT param) + { + ReceiversMap::iterator it = receivers_.begin(); + for (; it != receivers_.end(); ++it) + { + it->second->Receive(param); + } + + return ReturnT(); + } + + virtual ReturnT Notify(ParamT param) + { + ReceiversMap::iterator it = receivers_.begin(); + for (; it != receivers_.end(); ++it) + { + it->second->Respond(param, this); + } + + return ReturnT(); + } + +protected: + typedef std::map*> ReceiversMap; + ReceiversMap receivers_; + int count_; +}; + + +template +class ReceiverImpl : public ReceiverImplBase +{ +public: + ReceiverImpl() + : count_(0) + {} + + virtual ~ReceiverImpl() {} + + virtual void AddObserver(ObserverImplBase* observer) + { + observers_[count_] = observer; + count_++; + } + + virtual void RemoveObserver() + { + ObserversMap::iterator it = observers_.begin(); + for (; it != observers_.end(); ++it) + { + it->second->RemoveReceiver(this); + } + } + + virtual ReturnT Receive(ParamT param) + { + return ReturnT(); + } + + virtual ReturnT Respond(ParamT param, ObserverImplBase* observer) + { + return ReturnT(); + } + +protected: + typedef std::map*> ObserversMap; + ObserversMap observers_; + int count_; +}; + +#endif // OBSERVER_IMPL_BASE_HPP \ No newline at end of file diff --git a/DuiLib/Utils/stb_image.c b/DuiLib/Utils/stb_image.c new file mode 100644 index 00000000..2574463b --- /dev/null +++ b/DuiLib/Utils/stb_image.c @@ -0,0 +1,4678 @@ +#define STBI_NO_STDIO +#define STBI_NO_WRITE +#define STBI_NO_HDR + +/* stbi-1.33 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c + when you control the images you're loading + no warranty implied; use at your own risk + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline (no JPEG progressive) + PNG 8-bit only + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - overridable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) + + Latest revisions: + 1.33 (2011-07-14) minor fixes suggested by Dave Moore + 1.32 (2011-07-13) info support for all filetypes (SpartanJ) + 1.31 (2011-06-19) a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) added ability to load files via io callbacks (Ben Wenger) + 1.29 (2010-08-16) various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) cast-to-uint8 to fix warnings (Laurent Gomila) + allow trailing 0s at end of image data (Laurent Gomila) + 1.26 (2010-07-24) fix bug in file buffering for PNG reported by SpartanJ + + See end of file for full revision history. + + TODO: + stbi_info support for BMP,PSD,HDR,PIC + + + ============================ Contributors ========================= + + Image formats Optimizations & bugfixes + Sean Barrett (jpeg, png, bmp) Fabian "ryg" Giesen + Nicolas Schulz (hdr, psd) + Jonathan Dummer (tga) Bug fixes & warning fixes + Jean-Marc Lienher (gif) Marc LeBlanc + Tom Seddon (pic) Christpher Lloyd + Thatcher Ulrich (psd) Dave Moore + Won Chun + the Horde3D community + Extensions, features Janez Zemva + Jetro Lauha (stbi_info) Jonathan Blow + James "moose2000" Brown (iPhone PNG) Laurent Gomila + Ben "Disch" Wenger (io callbacks) Aruelien Pocheville + Martin "SpartanJ" Golini Ryamond Barbiero + David Woo + + + If your name should be here but isn't, let Sean know. + +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// To get a header file for this, either cut and paste the header, +// or create stb_image.h, #define STBI_HEADER_FILE_ONLY, and +// then include stb_image.c from it. + +//// begin header file //////////////////////////////////////////////////// +// +// Limitations: +// - no jpeg progressive support +// - non-HDR formats support 8-bit samples only (jpeg, png) +// - no delayed line count (jpeg) -- IJG doesn't support either +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to easily see if it's opaque. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB; nominally they +// would silently load as BGR, except the existing code should have just +// failed on such iPhone PNGs. But you can disable this conversion by +// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through. +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to +// to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). + + +#ifndef STBI_NO_STDIO + +#if defined(_MSC_VER) && _MSC_VER >= 0x1400 +#define _CRT_SECURE_NO_WARNINGS // suppress bogus warnings about fopen() +#endif + +#include +#endif + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,unsigned n); // skip the next 'n' bytes + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +extern stbi_uc *stbi_load_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_HDR + extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif + + extern float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + extern void stbi_hdr_to_ldr_gamma(float gamma); + extern void stbi_hdr_to_ldr_scale(float scale); + + extern void stbi_ldr_to_hdr_gamma(float gamma); + extern void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_HDR + +// stbi_is_hdr is always defined +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename); +extern int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +extern const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +extern void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +extern int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +extern void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +extern void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + + +// ZLIB client - used by PNG, available for other purposes + +extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +// define faster low-level operations (typically SIMD support) +#ifdef STBI_SIMD +typedef void (*stbi_idct_8x8)(stbi_uc *out, int out_stride, short data[64], unsigned short *dequantize); +// compute an integer IDCT on "input" +// input[x] = data[x] * dequantize[x] +// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' +// CLAMP results to 0..255 +typedef void (*stbi_YCbCr_to_RGB_run)(stbi_uc *output, stbi_uc const *y, stbi_uc const *cb, stbi_uc const *cr, int count, int step); +// compute a conversion from YCbCr to RGB +// 'count' pixels +// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B +// y: Y input channel +// cb: Cb input channel; scale/biased to be 0..255 +// cr: Cr input channel; scale/biased to be 0..255 + +extern void stbi_install_idct(stbi_idct_8x8 func); +extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); +#endif // STBI_SIMD + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifndef STBI_HEADER_FILE_ONLY + +#ifndef STBI_NO_HDR +#include // ldexp +#include // strcmp, strtok +#endif + +#ifndef STBI_NO_STDIO +#include +#endif +#include +#include +#include +#include + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +// implementation: +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; +typedef unsigned int uint; + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(uint32)==4 ? 1 : -1]; + +#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE) +#define STBI_NO_WRITE +#endif + +#define STBI_NOTUSED(v) (void)sizeof(v) + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +/////////////////////////////////////////////// +// +// stbi struct and start_xxx functions + +// stbi structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + uint8 buffer_start[128]; + + uint8 *img_buffer, *img_buffer_end; + uint8 *img_buffer_original; +} stbi; + + +static void refill_buffer(stbi *s); + +// initialize a memory-decode context +static void start_mem(stbi *s, uint8 const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (uint8 *) buffer; + s->img_buffer_end = (uint8 *) buffer+len; +} + +// initialize a callback-based context +static void start_callbacks(stbi *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + refill_buffer(s); +} + +#ifndef STBI_NO_STDIO + +static int stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stdio_skip(void *user, unsigned n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi_stdio_callbacks = +{ + stdio_read, + stdio_skip, + stdio_eof, +}; + +static void start_file(stbi *s, FILE *f) +{ + start_callbacks(s, &stbi_stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi_rewind(stbi *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; +} + +static int stbi_jpeg_test(stbi *s); +static stbi_uc *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp); +static int stbi_png_test(stbi *s); +static stbi_uc *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_png_info(stbi *s, int *x, int *y, int *comp); +static int stbi_bmp_test(stbi *s); +static stbi_uc *stbi_bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_test(stbi *s); +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_info(stbi *s, int *x, int *y, int *comp); +static int stbi_psd_test(stbi *s); +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_hdr_test(stbi *s); +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_pic_test(stbi *s); +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_test(stbi *s); +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp); + + +// this is not threadsafe +static const char *failure_reason; + +const char *stbi_failure_reason(void) +{ + return failure_reason; +} + +static int e(const char *str) +{ + failure_reason = str; + return 0; +} + +// e - error +// epf - error returning pointer to float +// epuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define e(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define e(x,y) e(y) +#else + #define e(x,y) e(x) +#endif + +#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) +#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) + +void stbi_image_free(void *retval_from_stbi_load) +{ + free(retval_from_stbi_load); +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static unsigned char *stbi_load_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + if (stbi_jpeg_test(s)) return stbi_jpeg_load(s,x,y,comp,req_comp); + if (stbi_png_test(s)) return stbi_png_load(s,x,y,comp,req_comp); + if (stbi_bmp_test(s)) return stbi_bmp_load(s,x,y,comp,req_comp); + if (stbi_gif_test(s)) return stbi_gif_load(s,x,y,comp,req_comp); + if (stbi_psd_test(s)) return stbi_psd_load(s,x,y,comp,req_comp); + if (stbi_pic_test(s)) return stbi_pic_load(s,x,y,comp,req_comp); + + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) { + float *hdr = stbi_hdr_load(s, x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + // test tga last because it's a crappy test! + if (stbi_tga_test(s)) + return stbi_tga_load(s,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + unsigned char *result; + if (!f) return epuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_load_main(&s,x,y,comp,req_comp); +} +#endif //!STBI_NO_STDIO + +unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_HDR + +float *stbi_loadf_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) + return stbi_hdr_load(s,x,y,comp,req_comp); + #endif + data = stbi_load_main(s, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} + +float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + float *result; + if (!f) return epf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_HDR + +// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is +// defined, for API simplicity; if STBI_NO_HDR is defined, it always +// reports false! + +int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi s; + start_mem(&s,buffer,len); + return stbi_hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename) +{ + FILE *f = fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +extern int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi s; + start_file(&s,f); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} + +#ifndef STBI_NO_HDR +static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; +static float l2h_gamma=2.2f, l2h_scale=1.0f; + +void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } +void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } + +void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } +void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + SCAN_load=0, + SCAN_type, + SCAN_header +}; + +static void refill_buffer(stbi *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_end-1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static int get8(stbi *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int at_eof(stbi *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +stbi_inline static uint8 get8u(stbi *s) +{ + return (uint8) get8(s); +} + +static void skip(stbi *s, int n) +{ + if (s->io.read) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int getn(stbi *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int get16(stbi *s) +{ + int z = get8(s); + return (z << 8) + get8(s); +} + +static uint32 get32(stbi *s) +{ + uint32 z = get16(s); + return (z << 16) + get16(s); +} + +static int get16le(stbi *s) +{ + int z = get8(s); + return z + (get8(s) << 8); +} + +static uint32 get32le(stbi *s) +{ + uint32 z = get16le(s); + return z + (get16le(s) << 16); +} + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static uint8 compute_y(int r, int g, int b) +{ + return (uint8) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, uint x, uint y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + assert(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) malloc(req_comp * x * y); + if (good == NULL) { + free(data); + return epuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: assert(0); + } + #undef CASE + } + + free(data); + return good; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) malloc(x * y * comp * sizeof(float)); + if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + free(data); + return output; +} + +#define float2int(x) ((int) (x)) +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) malloc(x * y * comp); + if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + } + free(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) +// +// simple implementation +// - channel subsampling of at most 2 in each dimension +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - uses a lot of intermediate memory, could cache poorly +// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 +// stb_jpeg: 1.34 seconds (MSVC6, default release build) +// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) +// IJL11.dll: 1.08 seconds (compiled by intel) +// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) +// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + uint8 fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + uint16 code[256]; + uint8 values[256]; + uint8 size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} huffman; + +typedef struct +{ + #ifdef STBI_SIMD + unsigned short dequant2[4][64]; + #endif + stbi *s; + huffman huff_dc[4]; + huffman huff_ac[4]; + uint8 dequant[4][64]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + uint8 *data; + void *raw_data; + uint8 *linebuf; + } img_comp[4]; + + uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int scan_n, order[4]; + int restart_interval, todo; +} jpeg; + +static int build_huffman(huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (uint8) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (uint16) (code++); + if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (uint8) i; + } + } + } + return 1; +} + +static void grow_buffer_unsafe(jpeg *j) +{ + do { + int b = j->nomore ? 0 : get8(j->s); + if (b == 0xff) { + int c = get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int decode(jpeg *j, huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & bmask[k]) + h->delta[k]; + assert((((j->code_buffer) >> (32 - h->size[c])) & bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +stbi_inline static int extend_receive(jpeg *j, int n) +{ + unsigned int m = 1 << (n-1); + unsigned int k; + if (j->code_bits < n) grow_buffer_unsafe(j); + + #if 1 + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~bmask[n]; + k &= bmask[n]; + j->code_bits -= n; + #else + k = (j->code_buffer >> (32 - n)) & bmask[n]; + j->code_bits -= n; + j->code_buffer <<= n; + #endif + // the following test is probably a random branch that won't + // predict well. I tried to table accelerate it but failed. + // maybe it's compiling as a conditional move? + if (k < m) + return (-1 << n) + k + 1; + else + return k; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static uint8 dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int b) +{ + int diff,dc,k; + int t = decode(j, hdc); + if (t < 0) return e("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) dc; + + // decode AC components, see JPEG spec + k = 1; + do { + int r,s; + int rs = decode(j, hac); + if (rs < 0) return e("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + data[dezigzag[k++]] = (short) extend_receive(j,s); + } + } while (k < 64); + return 1; +} + +// take a -128..127 value and clamp it and convert to 0..255 +stbi_inline static uint8 clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (uint8) x; +} + +#define f2f(x) (int) (((x) * 4096 + 0.5)) +#define fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * f2f(0.5411961f); \ + t2 = p1 + p3*f2f(-1.847759065f); \ + t3 = p1 + p2*f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = fsh(p2+p3); \ + t1 = fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*f2f( 1.175875602f); \ + t0 = t0*f2f( 0.298631336f); \ + t1 = t1*f2f( 2.053119869f); \ + t2 = t2*f2f( 3.072711026f); \ + t3 = t3*f2f( 1.501321110f); \ + p1 = p5 + p1*f2f(-0.899976223f); \ + p2 = p5 + p2*f2f(-2.562915447f); \ + p3 = p3*f2f(-1.961570560f); \ + p4 = p4*f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +#ifdef STBI_SIMD +typedef unsigned short stbi_dequantize_t; +#else +typedef uint8 stbi_dequantize_t; +#endif + +// .344 seconds on 3*anemones.jpg +static void idct_block(uint8 *out, int out_stride, short data[64], stbi_dequantize_t *dequantize) +{ + int i,val[64],*v=val; + stbi_dequantize_t *dq = dequantize; + uint8 *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SIMD +static stbi_idct_8x8 stbi_idct_installed = idct_block; + +void stbi_install_idct(stbi_idct_8x8 func) +{ + stbi_idct_installed = func; +} +#endif + +#define MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static uint8 get_marker(jpeg *j) +{ + uint8 x; + if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; } + x = get8u(j->s); + if (x != 0xff) return MARKER_none; + while (x == 0xff) + x = get8u(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, reset the entropy decoder and +// the dc prediction +static void reset(jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int parse_entropy_coded_data(jpeg *z) +{ + reset(z); + if (z->scan_n == 1) { + int i,j; + #ifdef STBI_SIMD + __declspec(align(16)) + #endif + short data[64]; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } else { // interleaved! + int i,j,k,x,y; + short data[64]; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } + return 1; +} + +static int process_marker(jpeg *z, int m) +{ + int L; + switch (m) { + case MARKER_none: // no marker found + return e("expected marker","Corrupt JPEG"); + + case 0xC2: // SOF - progressive + return e("progressive jpeg","JPEG format not supported (progressive)"); + + case 0xDD: // DRI - specify restart interval + if (get16(z->s) != 4) return e("bad DRI len","Corrupt JPEG"); + z->restart_interval = get16(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = get16(z->s)-2; + while (L > 0) { + int q = get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return e("bad DQT type","Corrupt JPEG"); + if (t > 3) return e("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][dezigzag[i]] = get8u(z->s); + #ifdef STBI_SIMD + for (i=0; i < 64; ++i) + z->dequant2[t][i] = z->dequant[t][i]; + #endif + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = get16(z->s)-2; + while (L > 0) { + uint8 *v; + int sizes[16],i,m=0; + int q = get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = get8(z->s); + m += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < m; ++i) + v[i] = get8u(z->s); + L -= m; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + skip(z->s, get16(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int process_scan_header(jpeg *z) +{ + int i; + int Ls = get16(z->s); + z->scan_n = get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return e("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = get8(z->s), which; + int q = get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + get8(z->s); // should be 63, but might be 0 + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + + return 1; +} + +static int process_frame_header(jpeg *z, int scan) +{ + stbi *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG + p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = get16(s); if (s->img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = get16(s); if (s->img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires + c = get8(s); + if (c != 3 && c != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return e("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return e("bad component ID","Corrupt JPEG"); + q = get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); + z->img_comp[i].tq = get8(s); if (z->img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); + } + + if (scan != SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + free(z->img_comp[i].raw_data); + z->img_comp[i].data = NULL; + } + return e("outofmem", "Out of memory"); + } + // align blocks for installable-idct using mmx/sse + z->img_comp[i].data = (uint8*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define DNL(x) ((x) == 0xdc) +#define SOI(x) ((x) == 0xd8) +#define EOI(x) ((x) == 0xd9) +#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) +#define SOS(x) ((x) == 0xda) + +static int decode_jpeg_header(jpeg *z, int scan) +{ + int m; + z->marker = MARKER_none; // initialize cached marker to empty + m = get_marker(z); + if (!SOI(m)) return e("no SOI","Corrupt JPEG"); + if (scan == SCAN_type) return 1; + m = get_marker(z); + while (!SOF(m)) { + if (!process_marker(z,m)) return 0; + m = get_marker(z); + while (m == MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (at_eof(z->s)) return e("no SOF", "Corrupt JPEG"); + m = get_marker(z); + } + } + if (!process_frame_header(z, scan)) return 0; + return 1; +} + +static int decode_jpeg_image(jpeg *j) +{ + int m; + j->restart_interval = 0; + if (!decode_jpeg_header(j, SCAN_load)) return 0; + m = get_marker(j); + while (!EOI(m)) { + if (SOS(m)) { + if (!process_scan_header(j)) return 0; + if (!parse_entropy_coded_data(j)) return 0; + if (j->marker == MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!at_eof(j->s)) { + int x = get8(j->s); + if (x == 255) { + j->marker = get8u(j->s); + break; + } else if (x != 0) { + return 0; + } + } + // if we reach eof without hitting a marker, get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!process_marker(j, m)) return 0; + } + m = get_marker(j); + } + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1, + int w, int hs); + +#define div4(x) ((uint8) ((x) >> 2)) + +static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + uint8 *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = div4(n+input[i-1]); + out[i*2+1] = div4(n+input[i+1]); + } + out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define div16(x) ((uint8) ((x) >> 4)) + +static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = div16(3*t0 + t1 + 8); + out[i*2 ] = div16(3*t1 + t0 + 8); + } + out[w*2-1] = div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + in_far = in_far; + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) + +// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) +// VC6 without processor=Pro is generating multiple LEAs per multiply! +static void YCbCr_to_RGB_row(uint8 *out, const uint8 *y, const uint8 *pcb, const uint8 *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (uint8)r; + out[1] = (uint8)g; + out[2] = (uint8)b; + out[3] = 255; + out += step; + } +} + +#ifdef STBI_SIMD +static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; + +void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) +{ + stbi_YCbCr_installed = func; +} +#endif + + +// clean up the temporary component buffers +static void cleanup_jpeg(jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].data) { + free(j->img_comp[i].raw_data); + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].linebuf) { + free(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + uint8 *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi_resample; + +static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + // validate req_comp + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + z->s->img_n = 0; + + // load a jpeg image from whichever source + if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + uint i,j; + uint8 *output; + uint8 *coutput[4]; + + stbi_resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (uint8 *) malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; + else r->resample = resample_row_generic; + } + + // can't error after this so, this is safe + output = (uint8 *) malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + uint8 *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + uint8 *y = coutput[0]; + if (z->s->img_n == 3) { + #ifdef STBI_SIMD + stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s.img_x, n); + #else + YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s->img_x, n); + #endif + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + uint8 *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + jpeg j; + j.s = s; + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +static int stbi_jpeg_test(stbi *s) +{ + int r; + jpeg j; + j.s = s; + r = decode_jpeg_header(&j, SCAN_type); + stbi_rewind(s); + return r; +} + +static int stbi_jpeg_info_raw(jpeg *j, int *x, int *y, int *comp) +{ + if (!decode_jpeg_header(j, SCAN_header)) { + stbi_rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp) +{ + jpeg j; + j.s = s; + return stbi_jpeg_info_raw(&j, x, y, comp); +} + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define ZFAST_BITS 9 // accelerate all cases in default tables +#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + uint16 fast[1 << ZFAST_BITS]; + uint16 firstcode[16]; + int maxcode[17]; + uint16 firstsymbol[16]; + uint8 size[288]; + uint16 value[288]; +} zhuffman; + +stbi_inline static int bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int bit_reverse(int v, int bits) +{ + assert(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return bitreverse16(v) >> (16-bits); +} + +static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 255, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + assert(sizes[i] <= (1 << i)); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (uint16) code; + z->firstsymbol[i] = (uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + z->size[c] = (uint8)s; + z->value[c] = (uint16)i; + if (s <= ZFAST_BITS) { + int k = bit_reverse(next_code[s],s); + while (k < (1 << ZFAST_BITS)) { + z->fast[k] = (uint16) c; + k += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + uint8 *zbuffer, *zbuffer_end; + int num_bits; + uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + zhuffman z_length, z_distance; +} zbuf; + +stbi_inline static int zget8(zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void fill_bits(zbuf *z) +{ + do { + assert(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int zreceive(zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +stbi_inline static int zhuffman_decode(zbuf *a, zhuffman *z) +{ + int b,s,k; + if (a->num_bits < 16) fill_bits(a); + b = z->fast[a->code_buffer & ZFAST_MASK]; + if (b < 0xffff) { + s = z->size[b]; + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; + } + + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = bit_reverse(a->code_buffer, 16); + for (s=ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + assert(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +static int expand(zbuf *z, int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + if (!z->z_expandable) return e("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) realloc(z->zout_start, limit); + if (q == NULL) return e("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int length_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int length_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int dist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int parse_huffman_block(zbuf *a) +{ + for(;;) { + int z = zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes + if (a->zout >= a->zout_end) if (!expand(a, 1)) return 0; + *a->zout++ = (char) z; + } else { + uint8 *p; + int len,dist; + if (z == 256) return 1; + z -= 257; + len = length_base[z]; + if (length_extra[z]) len += zreceive(a, length_extra[z]); + z = zhuffman_decode(a, &a->z_distance); + if (z < 0) return e("bad huffman code","Corrupt PNG"); + dist = dist_base[z]; + if (dist_extra[z]) dist += zreceive(a, dist_extra[z]); + if (a->zout - a->zout_start < dist) return e("bad dist","Corrupt PNG"); + if (a->zout + len > a->zout_end) if (!expand(a, len)) return 0; + p = (uint8 *) (a->zout - dist); + while (len--) + *a->zout++ = *p++; + } + } +} + +static int compute_huffman_codes(zbuf *a) +{ + static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + zhuffman z_codelength; + uint8 lencodes[286+32+137];//padding for maximum single op + uint8 codelength_sizes[19]; + int i,n; + + int hlit = zreceive(a,5) + 257; + int hdist = zreceive(a,5) + 1; + int hclen = zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (uint8) s; + } + if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = zhuffman_decode(a, &z_codelength); + assert(c >= 0 && c < 19); + if (c < 16) + lencodes[n++] = (uint8) c; + else if (c == 16) { + c = zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + assert(c == 18); + c = zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); + if (!zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int parse_uncompressed_block(zbuf *a) +{ + uint8 header[4]; + int len,nlen,k; + if (a->num_bits & 7) + zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (uint8) (a->code_buffer & 255); // wtf this warns? + a->code_buffer >>= 8; + a->num_bits -= 8; + } + assert(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = (uint8) zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return e("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!expand(a, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int parse_zlib_header(zbuf *a) +{ + int cmf = zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = zget8(a); + if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static uint8 default_length[288], default_distance[32]; +static void init_defaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) default_length[i] = 8; + for ( ; i <= 255; ++i) default_length[i] = 9; + for ( ; i <= 279; ++i) default_length[i] = 7; + for ( ; i <= 287; ++i) default_length[i] = 8; + + for (i=0; i <= 31; ++i) default_distance[i] = 5; +} + +int stbi_png_partial; // a quick hack to only allow decoding some of a PNG... I should implement real streaming support instead +static int parse_zlib(zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = zreceive(a,1); + type = zreceive(a,2); + if (type == 0) { + if (!parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!default_distance[31]) init_defaults(); + if (!zbuild_huffman(&a->z_length , default_length , 288)) return 0; + if (!zbuild_huffman(&a->z_distance, default_distance, 32)) return 0; + } else { + if (!compute_huffman_codes(a)) return 0; + } + if (!parse_huffman_block(a)) return 0; + } + if (stbi_png_partial && a->zout - a->zout_start > 65536) + break; + } while (!final); + return 1; +} + +static int do_zlib(zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return parse_zlib(a, parse_header); +} + +char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer+len; + if (do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + + +typedef struct +{ + uint32 length; + uint32 type; +} chunk; + +#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static chunk get_chunk_header(stbi *s) +{ + chunk c; + c.length = get32(s); + c.type = get32(s); + return c; +} + +static int check_png_header(stbi *s) +{ + static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (get8u(s) != png_sig[i]) return e("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi *s; + uint8 *idata, *expanded, *out; +} png; + + +enum { + F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, + F_avg_first, F_paeth_first +}; + +static uint8 first_row_filter[5] = +{ + F_none, F_sub, F_none, F_avg_first, F_paeth_first +}; + +static int paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +// create the png data from post-deflated data +static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, uint32 x, uint32 y) +{ + stbi *s = a->s; + uint32 i,j,stride = x*out_n; + int k; + int img_n = s->img_n; // copy it into a local for later + assert(out_n == s->img_n || out_n == s->img_n+1); + if (stbi_png_partial) y = 1; + a->out = (uint8 *) malloc(x * y * out_n); + if (!a->out) return e("outofmem", "Out of memory"); + if (!stbi_png_partial) { + if (s->img_x == x && s->img_y == y) { + if (raw_len != (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } + } + for (j=0; j < y; ++j) { + uint8 *cur = a->out + stride*j; + uint8 *prior = cur - stride; + int filter = *raw++; + if (filter > 4) return e("invalid filter","Corrupt PNG"); + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + // handle first pixel explicitly + for (k=0; k < img_n; ++k) { + switch (filter) { + case F_none : cur[k] = raw[k]; break; + case F_sub : cur[k] = raw[k]; break; + case F_up : cur[k] = raw[k] + prior[k]; break; + case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; + case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break; + case F_avg_first : cur[k] = raw[k]; break; + case F_paeth_first: cur[k] = raw[k]; break; + } + } + if (img_n != out_n) cur[img_n] = 255; + raw += img_n; + cur += out_n; + prior += out_n; + // this is a little gross, so that we don't switch per-pixel or per-component + if (img_n == out_n) { + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; + } + #undef CASE + } else { + assert(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + return 1; +} + +static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n, int interlaced) +{ + uint8 *final; + int p; + int save; + if (!interlaced) + return create_png_image_raw(a, raw, raw_len, out_n, a->s->img_x, a->s->img_y); + save = stbi_png_partial; + stbi_png_partial = 0; + + // de-interlacing + final = (uint8 *) malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + if (!create_png_image_raw(a, raw, raw_len, out_n, x, y)) { + free(final); + return 0; + } + for (j=0; j < y; ++j) + for (i=0; i < x; ++i) + memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*out_n, + a->out + (j*x+i)*out_n, out_n); + free(a->out); + raw += (x*out_n+1)*y; + raw_len -= (x*out_n+1)*y; + } + } + a->out = final; + + stbi_png_partial = save; + return 1; +} + +static int compute_transparency(png *z, uint8 tc[3], int out_n) +{ + stbi *s = z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + assert(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int expand_palette(png *a, uint8 *palette, int len, int pal_img_n) +{ + uint32 i, pixel_count = a->s->img_x * a->s->img_y; + uint8 *p, *temp_out, *orig = a->out; + + p = (uint8 *) malloc(pixel_count * pal_img_n); + if (p == NULL) return e("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + free(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi_unpremultiply_on_load = 0; +static int stbi_de_iphone_flag = 0; + +void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi_unpremultiply_on_load = flag_true_if_should_unpremultiply; +} +void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi_de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi_de_iphone(png *z) +{ + stbi *s = z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + assert(s->img_out_n == 4); + if (stbi_unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + uint8 a = p[3]; + uint8 t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +static int parse_png_file(png *z, int scan, int req_comp) +{ + uint8 palette[1024], pal_img_n=0; + uint8 has_trans=0, tc[3]; + uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, iphone=0; + stbi *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!check_png_header(s)) return 0; + + if (scan == SCAN_type) return 1; + + for (;;) { + chunk c = get_chunk_header(s); + switch (c.type) { + case PNG_TYPE('C','g','B','I'): + iphone = stbi_de_iphone_flag; + skip(s, c.length); + break; + case PNG_TYPE('I','H','D','R'): { + int depth,color,comp,filter; + if (!first) return e("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); + s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); + s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); + depth = get8(s); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); + color = get8(s); if (color > 6) return e("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); + comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG"); + filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG"); + interlace = get8(s); if (interlace>1) return e("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + if (scan == SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return e("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case PNG_TYPE('P','L','T','E'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = get8u(s); + palette[i*4+1] = get8u(s); + palette[i*4+2] = get8u(s); + palette[i*4+3] = 255; + } + break; + } + + case PNG_TYPE('t','R','N','S'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (z->idata) return e("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = get8u(s); + } else { + if (!(s->img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); + if (c.length != (uint32) s->img_n*2) return e("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (uint8) get16(s); // non 8-bit images will be larger + } + break; + } + + case PNG_TYPE('I','D','A','T'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); + if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; } + if (ioff + c.length > idata_limit) { + uint8 *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); + z->idata = p; + } + if (!getn(s, z->idata+ioff,c.length)) return e("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case PNG_TYPE('I','E','N','D'): { + uint32 raw_len; + if (first) return e("first not IHDR", "Corrupt PNG"); + if (scan != SCAN_load) return 1; + if (z->idata == NULL) return e("no IDAT","Corrupt PNG"); + z->expanded = (uint8 *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !iphone); + if (z->expanded == NULL) return 0; // zlib should set error + free(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!create_png_image(z, z->expanded, raw_len, s->img_out_n, interlace)) return 0; + if (has_trans) + if (!compute_transparency(z, tc, s->img_out_n)) return 0; + if (iphone && s->img_out_n > 2) + stbi_de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!expand_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + free(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return e("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX chunk not known"; + invalid_chunk[0] = (uint8) (c.type >> 24); + invalid_chunk[1] = (uint8) (c.type >> 16); + invalid_chunk[2] = (uint8) (c.type >> 8); + invalid_chunk[3] = (uint8) (c.type >> 0); + #endif + return e(invalid_chunk, "PNG not supported: unknown chunk type"); + } + skip(s, c.length); + break; + } + // end of chunk, read and skip CRC + get32(s); + } +} + +static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + if (parse_png_file(p, SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + free(p->out); p->out = NULL; + free(p->expanded); p->expanded = NULL; + free(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + png p; + p.s = s; + return do_png(&p, x,y,comp,req_comp); +} + +static int stbi_png_test(stbi *s) +{ + int r; + r = check_png_header(s); + stbi_rewind(s); + return r; +} + +static int stbi_png_info_raw(png *p, int *x, int *y, int *comp) +{ + if (!parse_png_file(p, SCAN_header, 0)) { + stbi_rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi_png_info(stbi *s, int *x, int *y, int *comp) +{ + png p; + p.s = s; + return stbi_png_info_raw(&p, x, y, comp); +} + +// Microsoft/Windows BMP image + +static int bmp_test(stbi *s) +{ + int sz; + if (get8(s) != 'B') return 0; + if (get8(s) != 'M') return 0; + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + get32le(s); // discard data offset + sz = get32le(s); + if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; + return 0; +} + +static int stbi_bmp_test(stbi *s) +{ + int r = bmp_test(s); + stbi_rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *out; + unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (get8(s) != 'B' || get8(s) != 'M') return epuc("not BMP", "Corrupt BMP"); + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + offset = get32le(s); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = get16le(s); + s->img_y = get16le(s); + } else { + s->img_x = get32le(s); + s->img_y = get32le(s); + } + if (get16le(s) != 1) return epuc("bad BMP", "bad BMP"); + bpp = get16le(s); + if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = get32le(s); + if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); + get32le(s); // discard sizeof + get32le(s); // discard hres + get32le(s); // discard vres + get32le(s); // discard colorsused + get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + get32le(s); + get32le(s); + get32le(s); + get32le(s); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xffu << 16; + mg = 0xffu << 8; + mb = 0xffu << 0; + ma = 0xffu << 24; + fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 + } else { + mr = 31u << 10; + mg = 31u << 5; + mb = 31u << 0; + } + } else if (compress == 3) { + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return epuc("bad BMP", "bad BMP"); + } + } else + return epuc("bad BMP", "bad BMP"); + } + } else { + assert(hsz == 108); + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + ma = get32le(s); + get32le(s); // discard color space + for (i=0; i < 12; ++i) + get32le(s); // discard color space parameters + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) malloc(target * s->img_x * s->img_y); + if (!out) return epuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + if (hsz != 12) get8(s); + pal[i][3] = 255; + } + skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (s->img_x + 1) >> 1; + else if (bpp == 8) width = s->img_x; + else { free(out); return epuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=get8(s),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { free(out); return epuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = high_bit(mr)-7; rcount = bitcount(mr); + gshift = high_bit(mg)-7; gcount = bitcount(mr); + bshift = high_bit(mb)-7; bcount = bitcount(mr); + ashift = high_bit(ma)-7; acount = bitcount(mr); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + int a; + out[z+2] = get8u(s); + out[z+1] = get8u(s); + out[z+0] = get8u(s); + z += 3; + a = (easy == 2 ? get8(s) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } else { + for (i=0; i < (int) s->img_x; ++i) { + uint32 v = (bpp == 16 ? get16le(s) : get32le(s)); + int a; + out[z++] = (uint8) shiftsigned(v & mr, rshift, rcount); + out[z++] = (uint8) shiftsigned(v & mg, gshift, gcount); + out[z++] = (uint8) shiftsigned(v & mb, bshift, bcount); + a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } + skip(s, pad); + } + } + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} + +static stbi_uc *stbi_bmp_load(stbi *s,int *x, int *y, int *comp, int req_comp) +{ + return bmp_load(s, x,y,comp,req_comp); +} + + +// Targa Truevision - TGA +// by Jonathan Dummer + +static int tga_info(stbi *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp; + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if( sz > 1 ) { + stbi_rewind(s); + return 0; // only RGB or indexed allowed + } + sz = get8u(s); // image type + // only RGB or grey allowed, +/- RLE + if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; + skip(s,9); + tga_w = get16le(s); + if( tga_w < 1 ) { + stbi_rewind(s); + return 0; // test width + } + tga_h = get16le(s); + if( tga_h < 1 ) { + stbi_rewind(s); + return 0; // test height + } + sz = get8(s); // bits per pixel + // only RGB or RGBA or grey allowed + if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { + stbi_rewind(s); + return 0; + } + tga_comp = sz; + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp / 8; + return 1; // seems to have passed everything +} + +int stbi_tga_info(stbi *s, int *x, int *y, int *comp) +{ + return tga_info(s, x, y, comp); +} + +static int tga_test(stbi *s) +{ + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if ( sz > 1 ) return 0; // only RGB or indexed allowed + sz = get8u(s); // image type + if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + get16(s); // discard palette start + get16(s); // discard palette length + get8(s); // discard bits per palette color entry + get16(s); // discard x origin + get16(s); // discard y origin + if ( get16(s) < 1 ) return 0; // test width + if ( get16(s) < 1 ) return 0; // test height + sz = get8(s); // bits per pixel + if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed + return 1; // seems to have passed everything +} + +static int stbi_tga_test(stbi *s) +{ + int res = tga_test(s); + stbi_rewind(s); + return res; +} + +static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = get8u(s); + int tga_indexed = get8u(s); + int tga_image_type = get8u(s); + int tga_is_RLE = 0; + int tga_palette_start = get16le(s); + int tga_palette_len = get16le(s); + int tga_palette_bits = get8u(s); + int tga_x_origin = get16le(s); + int tga_y_origin = get16le(s); + int tga_width = get16le(s); + int tga_height = get16le(s); + int tga_bits_per_pixel = get8u(s); + int tga_inverted = get8u(s); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + unsigned char trans_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if ( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA + } + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) + { + tga_bits_per_pixel = tga_palette_bits; + } + + // tga info + *x = tga_width; + *y = tga_height; + if ( (req_comp < 1) || (req_comp > 4) ) + { + // just use whatever the file was + req_comp = tga_bits_per_pixel / 8; + *comp = req_comp; + } else + { + // force a new number of components + *comp = tga_bits_per_pixel/8; + } + tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); + if (!tga_data) return epuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + skip(s, tga_offset ); + // do I need to load a palette? + if ( tga_indexed ) + { + // any data to skip? (offset usually = 0) + skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); + if (!tga_palette) return epuc("outofmem", "Out of memory"); + if (!getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { + free(tga_data); + free(tga_palette); + return epuc("bad palette", "Corrupt TGA"); + } + } + // load the data + trans_data[0] = trans_data[1] = trans_data[2] = trans_data[3] = 0; + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE chunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = get8u(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = get8u(s); + if ( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = get8u(s); + } + } + // convert raw to the intermediate format + switch (tga_bits_per_pixel) + { + case 8: + // Luminous => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 16: + // Luminous,Alpha => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[1]; + break; + case 24: + // BGR => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 32: + // BGRA => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[3]; + break; + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + // convert to final format + switch (req_comp) + { + case 1: + // RGBA => Luminance + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + break; + case 2: + // RGBA => Luminance,Alpha + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + tga_data[i*req_comp+1] = trans_data[3]; + break; + case 3: + // RGBA => RGB + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + break; + case 4: + // RGBA => RGBA + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + tga_data[i*req_comp+3] = trans_data[3]; + break; + } + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * req_comp; + int index2 = (tga_height - 1 - j) * tga_width * req_comp; + for (i = tga_width * req_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + free( tga_palette ); + } + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} + +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return tga_load(s,x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +static int psd_test(stbi *s) +{ + if (get32(s) != 0x38425053) return 0; // "8BPS" + else return 1; +} + +static int stbi_psd_test(stbi *s) +{ + int r = psd_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + uint8 *out; + + // Check identifier + if (get32(s) != 0x38425053) // "8BPS" + return epuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (get16(s) != 1) + return epuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) + return epuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = get32(s); + w = get32(s); + + // Make sure the depth is 8 bits. + if (get16(s) != 8) + return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (get16(s) != 3) + return epuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + skip(s,get32(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + skip(s, get32(s) ); + + // Skip the reserved data. + skip(s, get32(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = get16(s); + if (compression > 1) + return epuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) malloc(4 * w*h); + if (!out) return epuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = get8u(s); + p += 4; + len--; + } + } else if (len > 128) { + uint8 val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = get8u(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + for (i = 0; i < pixelCount; i++) + *p = get8u(s), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + out = convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} + +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return psd_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +static int pic_is4(stbi *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int pic_test(stbi *s) +{ + int i; + + if (!pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + get8(s); + + if (!pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} pic_packet_t; + +static stbi_uc *pic_readval(stbi *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (at_eof(s)) return epuc("bad file","PIC file too short"); + dest[i]=get8u(s); + } + } + + return dest; +} + +static void pic_copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *pic_load2(stbi *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + pic_packet_t packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return epuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + + act_comp |= packet->channel; + + if (at_eof(s)) return epuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return epuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return epuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=get8u(s); + if (at_eof(s)) return epuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (uint8) left; + + if (!pic_readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = get8(s), i; + if (at_eof(s)) return epuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + int i; + + if (count==128) + count = get16(s); + else + count -= 127; + if (count > left) + return epuc("bad file","scanline overrun"); + + if (!pic_readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return epuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *pic_load(stbi *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + get8(s); + + x = get16(s); + y = get16(s); + if (at_eof(s)) return epuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return epuc("too large", "Image too large to decode"); + + get32(s); //skip `ratio' + get16(s); //skip `fields' + get16(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!pic_load2(s,x,y,comp, result)) { + free(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi_pic_test(stbi *s) +{ + int r = pic_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return pic_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb +typedef struct stbi_gif_lzw_struct { + int16 prefix; + uint8 first; + uint8 suffix; +} stbi_gif_lzw; + +typedef struct stbi_gif_struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags; + uint8 pal[256][4]; + uint8 lpal[256][4]; + stbi_gif_lzw codes[4096]; + uint8 *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi_gif; + +static int gif_test(stbi *s) +{ + int sz; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') return 0; + sz = get8(s); + if (sz != '9' && sz != '7') return 0; + if (get8(s) != 'a') return 0; + return 1; +} + +static int stbi_gif_test(stbi *s) +{ + int r = gif_test(s); + stbi_rewind(s); + return r; +} + +static void stbi_gif_parse_colortable(stbi *s, uint8 pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + pal[i][3] = transp ? 0 : 255; + } +} + +static int stbi_gif_header(stbi *s, stbi_gif *g, int *comp, int is_info) +{ + uint8 version; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') + return e("not GIF", "Corrupt GIF"); + + version = get8u(s); + if (version != '7' && version != '9') return e("not GIF", "Corrupt GIF"); + if (get8(s) != 'a') return e("not GIF", "Corrupt GIF"); + + failure_reason = ""; + g->w = get16le(s); + g->h = get16le(s); + g->flags = get8(s); + g->bgindex = get8(s); + g->ratio = get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi_gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi_gif_info_raw(stbi *s, int *x, int *y, int *comp) +{ + stbi_gif g; + if (!stbi_gif_header(s, &g, comp, 1)) { + stbi_rewind( s ); + return 0; + } + if (x) *x = g.w; + if (y) *y = g.h; + return 1; +} + +static void stbi_out_gif_code(stbi_gif *g, uint16 code) +{ + uint8 *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi_out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static uint8 *stbi_process_gif_raster(stbi *s, stbi_gif *g) +{ + uint8 lzw_cs; + int32 len, code; + uint32 first; + int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi_gif_lzw *p; + + lzw_cs = get8u(s); + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (code = 0; code < clear; code++) { + g->codes[code].prefix = -1; + g->codes[code].first = (uint8) code; + g->codes[code].suffix = (uint8) code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (int32) get8(s) << valid_bits; + valid_bits += 8; + } else { + int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + skip(s, len); + while ((len = get8(s)) > 0) + skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return epuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return epuc("too many codes", "Corrupt GIF"); + p->prefix = (int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return epuc("illegal code in raster", "Corrupt GIF"); + + stbi_out_gif_code(g, (uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return epuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi_fill_gif_background(stbi_gif *g) +{ + int i; + uint8 *c = g->pal[g->bgindex]; + // @OPTIMIZE: write a dword at a time + for (i = 0; i < g->w * g->h * 4; i += 4) { + uint8 *p = &g->out[i]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static uint8 *stbi_gif_load_next(stbi *s, stbi_gif *g, int *comp, int req_comp) +{ + int i; + uint8 *old_out = 0; + + if (g->out == 0) { + if (!stbi_gif_header(s, g, comp,0)) return 0; // failure_reason set by stbi_gif_header + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + stbi_fill_gif_background(g); + } else { + // animated-gif-only path + if (((g->eflags & 0x1C) >> 2) == 3) { + old_out = g->out; + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + memcpy(g->out, old_out, g->w*g->h*4); + } + } + + for (;;) { + switch (get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int32 x, y, w, h; + uint8 *o; + + x = get16le(s); + y = get16le(s); + w = get16le(s); + h = get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return epuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi_gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (uint8 *) g->lpal; + } else if (g->flags & 0x80) { + for (i=0; i < 256; ++i) // @OPTIMIZE: reset only the previous transparent + g->pal[i][3] = 255; + if (g->transparent >= 0 && (g->eflags & 0x01)) + g->pal[g->transparent][3] = 0; + g->color_table = (uint8 *) g->pal; + } else + return epuc("missing color table", "Corrupt GIF"); + + o = stbi_process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (req_comp && req_comp != 4) + o = convert_format(o, 4, req_comp, g->w, g->h); + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (get8(s) == 0xF9) { // Graphic Control Extension. + len = get8(s); + if (len == 4) { + g->eflags = get8(s); + get16le(s); // delay + g->transparent = get8(s); + } else { + skip(s, len); + break; + } + } + while ((len = get8(s)) != 0) + skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (uint8 *) 1; + + default: + return epuc("unknown code", "Corrupt GIF"); + } + } +} + +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *u = 0; + stbi_gif g={0}; + + u = stbi_gif_load_next(s, &g, comp, req_comp); + if (u == (void *) 1) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + } + + return u; +} + +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp) +{ + return stbi_gif_info_raw(s,x,y,comp); +} + + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int hdr_test(stbi *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi_hdr_test(stbi* s) +{ + int r = hdr_test(s); + stbi_rewind(s); + return r; +} + +#define HDR_BUFLEN 1024 +static char *hdr_gettoken(stbi *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) get8(z); + + while (!at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == HDR_BUFLEN-1) { + // flush to end of line + while (!at_eof(z) && get8(z) != '\n') + ; + break; + } + c = (char) get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return epf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return epf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = strtol(token, NULL, 10); + + *x = width; + *y = height; + + *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + getn(s, rgbe, 4); + hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = get8(s); + c2 = get8(s); + len = get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + uint8 rgbe[4]; + rgbe[0] = (uint8) c1; + rgbe[1] = (uint8) c2; + rgbe[2] = (uint8) len; + rgbe[3] = (uint8) get8u(s); + hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + free(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= get8(s); + if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = get8u(s); + if (count > 128) { + // Run + value = get8u(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = get8u(s); + } + } + } + for (i=0; i < width; ++i) + hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + free(scanline); + } + + return hdr_data; +} + +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return hdr_load(s,x,y,comp,req_comp); +} + +static int stbi_hdr_info(stbi *s, int *x, int *y, int *comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { + stbi_rewind( s ); + return 0; + } + + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi_rewind( s ); + return 0; + } + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *y = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *x = strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +static int stbi_bmp_info(stbi *s, int *x, int *y, int *comp) +{ + int hsz; + if (get8(s) != 'B' || get8(s) != 'M') { + stbi_rewind( s ); + return 0; + } + skip(s,12); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) { + stbi_rewind( s ); + return 0; + } + if (hsz == 12) { + *x = get16le(s); + *y = get16le(s); + } else { + *x = get32le(s); + *y = get32le(s); + } + if (get16le(s) != 1) { + stbi_rewind( s ); + return 0; + } + *comp = get16le(s) / 8; + return 1; +} + +static int stbi_psd_info(stbi *s, int *x, int *y, int *comp) +{ + int channelCount; + if (get32(s) != 0x38425053) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 1) { + stbi_rewind( s ); + return 0; + } + skip(s, 6); + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) { + stbi_rewind( s ); + return 0; + } + *y = get32(s); + *x = get32(s); + if (get16(s) != 8) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 3) { + stbi_rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi_pic_info(stbi *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + pic_packet_t packets[10]; + + skip(s, 92); + + *x = get16(s); + *y = get16(s); + if (at_eof(s)) return 0; + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi_rewind( s ); + return 0; + } + + skip(s, 8); + + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + act_comp |= packet->channel; + + if (at_eof(s)) { + stbi_rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi_rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} + +static int stbi_info_main(stbi *s, int *x, int *y, int *comp) +{ + if (stbi_jpeg_info(s, x, y, comp)) + return 1; + if (stbi_png_info(s, x, y, comp)) + return 1; + if (stbi_gif_info(s, x, y, comp)) + return 1; + if (stbi_bmp_info(s, x, y, comp)) + return 1; + if (stbi_psd_info(s, x, y, comp)) + return 1; + if (stbi_pic_info(s, x, y, comp)) + return 1; + #ifndef STBI_NO_HDR + if (stbi_hdr_info(s, x, y, comp)) + return 1; + #endif + // test tga last because it's a crappy test! + if (stbi_tga_info(s, x, y, comp)) + return 1; + return e("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = fopen(filename, "rb"); + int result; + if (!f) return e("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi s; + long pos = ftell(f); + start_file(&s, f); + r = stbi_info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_info_main(&s,x,y,comp); +} + +int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi_info_main(&s,x,y,comp); +} + +#endif // STBI_HEADER_FILE_ONLY + +/* + revision history: + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-uint8 to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.e. Janez (U+017D)emva) + 1.21 fix use of 'uint8' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 2008-08-02 + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 first released version +*/ diff --git a/Help/directui license.txt b/Help/directui license.txt new file mode 100644 index 00000000..ea08b50d --- /dev/null +++ b/Help/directui license.txt @@ -0,0 +1,14 @@ +DirectUI - UI Library + +Written by Bjarke Viksoe (bjarke@viksoe.dk) +Copyright (c) 2006-2007 Bjarke Viksoe. + +This code may be used in compiled form in any way you desire. These +source files may be redistributed by any means PROVIDING it is +not sold for profit without the authors written consent, and +providing that this notice and the authors name is included. + +This file is provided "as is" with no expressed or implied warranty. +The author accepts no liability if it causes any damage to you or your +computer whatsoever. It's free, so don't hassle me about it. +Beware of bugs. \ No newline at end of file diff --git a/Help/duilib license.txt b/Help/duilib license.txt new file mode 100644 index 00000000..6b93edf6 --- /dev/null +++ b/Help/duilib license.txt @@ -0,0 +1,9 @@ +Copyright (c) 2010-2011, duilib develop team(www.duilib.com).All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met. + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git "a/Help/\345\261\236\346\200\247\345\210\227\350\241\250.xml" "b/Help/\345\261\236\346\200\247\345\210\227\350\241\250.xml" new file mode 100644 index 00000000..cadcbc4c --- /dev/null +++ "b/Help/\345\261\236\346\200\247\345\210\227\350\241\250.xml" @@ -0,0 +1,1244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/Help/\346\204\237\350\260\242.txt" "b/Help/\346\204\237\350\260\242.txt" new file mode 100644 index 00000000..aaea1c8a --- /dev/null +++ "b/Help/\346\204\237\350\260\242.txt" @@ -0,0 +1,6 @@ +ߣ +Bjarke Viksoe (bjarke@viksoe.dk) +'L. + +ѣ +λ \ No newline at end of file diff --git "a/Help/\347\211\210\346\235\203\350\257\264\346\230\216.txt" "b/Help/\347\211\210\346\235\203\350\257\264\346\230\216.txt" new file mode 100644 index 00000000..0df6fcdb --- /dev/null +++ "b/Help/\347\211\210\346\235\203\350\257\264\346\230\216.txt" @@ -0,0 +1,5 @@ + duilibһwindowsѿԴĽ⣬ѭbsdЭ飬޸ĺʹãҵĿʹduilibװмdirectui license.txtduilib license.txtļĵеĻἰduilibԸ⣬԰Ŀͽͼ͵wangchyz@gmail.comǻվʹðϼ֡ + duilib⸽demoͼƬԴռԻ𺦹˾Ȩ棬ϵǣǽɾ + л֧֡ + + duilib \ No newline at end of file diff --git a/bin/skin/GameBox/Combo_nor.bmp b/bin/skin/GameBox/Combo_nor.bmp new file mode 100644 index 00000000..3cf8cb87 Binary files /dev/null and b/bin/skin/GameBox/Combo_nor.bmp differ diff --git a/bin/skin/GameBox/Combo_over.bmp b/bin/skin/GameBox/Combo_over.bmp new file mode 100644 index 00000000..1da067c5 Binary files /dev/null and b/bin/skin/GameBox/Combo_over.bmp differ diff --git a/bin/skin/GameBox/ad_one.png b/bin/skin/GameBox/ad_one.png new file mode 100644 index 00000000..b6993aeb Binary files /dev/null and b/bin/skin/GameBox/ad_one.png differ diff --git a/bin/skin/GameBox/ad_two.png b/bin/skin/GameBox/ad_two.png new file mode 100644 index 00000000..fd30b2b3 Binary files /dev/null and b/bin/skin/GameBox/ad_two.png differ diff --git a/bin/skin/GameBox/bg.png b/bin/skin/GameBox/bg.png new file mode 100644 index 00000000..6d3e94c7 Binary files /dev/null and b/bin/skin/GameBox/bg.png differ diff --git a/bin/skin/GameBox/button_hover.png b/bin/skin/GameBox/button_hover.png new file mode 100644 index 00000000..ab93d5d9 Binary files /dev/null and b/bin/skin/GameBox/button_hover.png differ diff --git a/bin/skin/GameBox/button_normal.png b/bin/skin/GameBox/button_normal.png new file mode 100644 index 00000000..56b988b4 Binary files /dev/null and b/bin/skin/GameBox/button_normal.png differ diff --git a/bin/skin/GameBox/button_pushed.png b/bin/skin/GameBox/button_pushed.png new file mode 100644 index 00000000..9323aee1 Binary files /dev/null and b/bin/skin/GameBox/button_pushed.png differ diff --git a/bin/skin/GameBox/check.png b/bin/skin/GameBox/check.png new file mode 100644 index 00000000..b7a93d1b Binary files /dev/null and b/bin/skin/GameBox/check.png differ diff --git a/bin/skin/GameBox/cloud.png b/bin/skin/GameBox/cloud.png new file mode 100644 index 00000000..0e1e005a Binary files /dev/null and b/bin/skin/GameBox/cloud.png differ diff --git a/bin/skin/GameBox/combo.png b/bin/skin/GameBox/combo.png new file mode 100644 index 00000000..1d75f1a4 Binary files /dev/null and b/bin/skin/GameBox/combo.png differ diff --git a/bin/skin/GameBox/findedit.png b/bin/skin/GameBox/findedit.png new file mode 100644 index 00000000..d410f26f Binary files /dev/null and b/bin/skin/GameBox/findedit.png differ diff --git a/bin/skin/GameBox/fold_hover.bmp b/bin/skin/GameBox/fold_hover.bmp new file mode 100644 index 00000000..473dfbf5 Binary files /dev/null and b/bin/skin/GameBox/fold_hover.bmp differ diff --git a/bin/skin/GameBox/fold_hover.png b/bin/skin/GameBox/fold_hover.png new file mode 100644 index 00000000..ccedb900 Binary files /dev/null and b/bin/skin/GameBox/fold_hover.png differ diff --git a/bin/skin/GameBox/fold_nor.bmp b/bin/skin/GameBox/fold_nor.bmp new file mode 100644 index 00000000..d6cd6aa5 Binary files /dev/null and b/bin/skin/GameBox/fold_nor.bmp differ diff --git a/bin/skin/GameBox/fold_nor.png b/bin/skin/GameBox/fold_nor.png new file mode 100644 index 00000000..85091e73 Binary files /dev/null and b/bin/skin/GameBox/fold_nor.png differ diff --git a/bin/skin/GameBox/fold_pushed.bmp b/bin/skin/GameBox/fold_pushed.bmp new file mode 100644 index 00000000..4c9ec179 Binary files /dev/null and b/bin/skin/GameBox/fold_pushed.bmp differ diff --git a/bin/skin/GameBox/fold_pushed.png b/bin/skin/GameBox/fold_pushed.png new file mode 100644 index 00000000..6e4658a4 Binary files /dev/null and b/bin/skin/GameBox/fold_pushed.png differ diff --git a/bin/skin/GameBox/gamebk.jpg b/bin/skin/GameBox/gamebk.jpg new file mode 100644 index 00000000..c3e5309e Binary files /dev/null and b/bin/skin/GameBox/gamebk.jpg differ diff --git a/bin/skin/GameBox/gameicons.png b/bin/skin/GameBox/gameicons.png new file mode 100644 index 00000000..c80655d7 Binary files /dev/null and b/bin/skin/GameBox/gameicons.png differ diff --git a/bin/skin/GameBox/gameitem.xml b/bin/skin/GameBox/gameitem.xml new file mode 100644 index 00000000..c0013ead --- /dev/null +++ b/bin/skin/GameBox/gameitem.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/bin/skin/GameBox/header_bk.png b/bin/skin/GameBox/header_bk.png new file mode 100644 index 00000000..65da82e3 Binary files /dev/null and b/bin/skin/GameBox/header_bk.png differ diff --git a/bin/skin/GameBox/icon.png b/bin/skin/GameBox/icon.png new file mode 100644 index 00000000..0dfbcdbd Binary files /dev/null and b/bin/skin/GameBox/icon.png differ diff --git a/bin/skin/GameBox/icon1.png b/bin/skin/GameBox/icon1.png new file mode 100644 index 00000000..f2433a98 Binary files /dev/null and b/bin/skin/GameBox/icon1.png differ diff --git a/bin/skin/GameBox/left_header.jpg b/bin/skin/GameBox/left_header.jpg new file mode 100644 index 00000000..e5933988 Binary files /dev/null and b/bin/skin/GameBox/left_header.jpg differ diff --git a/bin/skin/GameBox/loading.gif b/bin/skin/GameBox/loading.gif new file mode 100644 index 00000000..1d45176d Binary files /dev/null and b/bin/skin/GameBox/loading.gif differ diff --git a/bin/skin/GameBox/menu_bk.bmp b/bin/skin/GameBox/menu_bk.bmp new file mode 100644 index 00000000..e47e7db7 Binary files /dev/null and b/bin/skin/GameBox/menu_bk.bmp differ diff --git a/bin/skin/GameBox/menu_bk.png b/bin/skin/GameBox/menu_bk.png new file mode 100644 index 00000000..b4970a28 Binary files /dev/null and b/bin/skin/GameBox/menu_bk.png differ diff --git a/bin/skin/GameBox/progress_back.png b/bin/skin/GameBox/progress_back.png new file mode 100644 index 00000000..899fa194 Binary files /dev/null and b/bin/skin/GameBox/progress_back.png differ diff --git a/bin/skin/GameBox/progress_fore.png b/bin/skin/GameBox/progress_fore.png new file mode 100644 index 00000000..9bf8cd8b Binary files /dev/null and b/bin/skin/GameBox/progress_fore.png differ diff --git a/bin/skin/GameBox/scrollbar.bmp b/bin/skin/GameBox/scrollbar.bmp new file mode 100644 index 00000000..42be6a08 Binary files /dev/null and b/bin/skin/GameBox/scrollbar.bmp differ diff --git a/bin/skin/GameBox/search.xml b/bin/skin/GameBox/search.xml new file mode 100644 index 00000000..982cf37a --- /dev/null +++ b/bin/skin/GameBox/search.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/bin/skin/GameBox/search_bk.png b/bin/skin/GameBox/search_bk.png new file mode 100644 index 00000000..507586f7 Binary files /dev/null and b/bin/skin/GameBox/search_bk.png differ diff --git a/bin/skin/GameBox/search_btn.png b/bin/skin/GameBox/search_btn.png new file mode 100644 index 00000000..8c1f9533 Binary files /dev/null and b/bin/skin/GameBox/search_btn.png differ diff --git a/bin/skin/GameBox/searchitem.xml b/bin/skin/GameBox/searchitem.xml new file mode 100644 index 00000000..e9801c4a --- /dev/null +++ b/bin/skin/GameBox/searchitem.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/bin/skin/GameBox/shortcut.xml b/bin/skin/GameBox/shortcut.xml new file mode 100644 index 00000000..94f450e9 --- /dev/null +++ b/bin/skin/GameBox/shortcut.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/bin/skin/GameBox/shortcutwnd.xml b/bin/skin/GameBox/shortcutwnd.xml new file mode 100644 index 00000000..f6939fb3 --- /dev/null +++ b/bin/skin/GameBox/shortcutwnd.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/bin/skin/GameBox/skin.xml b/bin/skin/GameBox/skin.xml new file mode 100644 index 00000000..2b7ee530 --- /dev/null +++ b/bin/skin/GameBox/skin.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + +