Skip to content

Instantly share code, notes, and snippets.

@pabloko
Last active September 29, 2022 16:34
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pabloko/9655e4fc0b5b931594b3f9883202f3f6 to your computer and use it in GitHub Desktop.
Save pabloko/9655e4fc0b5b931594b3f9883202f3f6 to your computer and use it in GitHub Desktop.
D3D9 Overlay with IE and alpha

D3D9 Overlay with IE and alpha

Many times working with Direct3D9, you want to build a simple GUI to overlay over your graphics, many libraries does this in some way but at some point you may want to use a web browser and options are much more limited. CEF is great, but heavy and IPC can be a real pain to handle, ultralight stopped supporting x86 arch... few other webui libs out there, they all have its pros and cons but we lost the sense of simplicity we wanted on first hand.

Then theres MSHTML (Internet Explorer) that is shipped on every windows os, enough versatile and fast to be used as overlay. Shamefully for Microsoft, this control never supported transparent background, even on .net webforms/WPF webbrowser depends from MSHTML and always cast a background.

This demo explores two ways to overcome this long standing issue and get 1bit alpha or 255bit alpha by color difference interpolation.

  • 1bit alpha: Uses green rgb(0,255,0) background that is simply keyed, can be used on boxed UI with less resource usage, font antialiasing will produce weird edges if text its outside a opaque box.
  • 255bit alpha: Uses transparent background, internally captured as black and white frames, color difference is interpolated to create an alpha channel, the result is a perfect overlay, but can be more resource hungry.

Demo

Demo executable: https://mega.nz/file/oUhjECaR#EnqjigSNJznTZIvOZ-B7F7NZXCaGiLic0tbqmVxa1xQ

/*
Direct3D9 Internet explorer overlay with alpha (color interpolation)
Modes:
- 1 bit alpha: Always set the background to rgb(0,255,0) <<green>> toggles alpha byte
- 256 bit alpha: Use transparent background, renderer takes 2 shots with black and white background,
alpha result from difference interpolation
Todo:
- Optimize render to use SIMD extensions for pixel manipulation (or using an HLSL pixel shader?)
- Separate IE execution thread and sync frame drawing (fast_mutex) (test/rw js api threadsafeness)
*/
//-- use 1bit alpha / 255bit alpha
#define BIT255ALPHA
#define IEONSEPARATETHREAD
#pragma region Includes
#include <SDKDDKVer.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <ole2.h>
#include <string>
#include <oleidl.h>
#include <vector>
#include <comdef.h>
#include <Mshtml.h>
#include <Exdisp.h>
#include <mshtmhst.h>
#include <mshtmdid.h>
#include <exdispid.h>
#pragma endregion
#pragma region Variables
#define SAFE_RELEASE(p) if(p) { (p)->Release(); (p) = NULL; }
#define SAFE_FREE(p) if(p) { free (p); (p) = NULL; }
#define MAX_LOADSTRING 100
#define WIDTH 700
#define HEIGHT 510
// Variables globales:
HINSTANCE hInst; // Instancia actual
WCHAR szTitle[MAX_LOADSTRING]; // Texto de la barra de título
WCHAR szWindowClass[MAX_LOADSTRING]; // nombre de clase de la ventana principal
HINSTANCE g_hinst = 0;
HWND g_wnd = 0;
// Direct3D structures
IDirect3D9 * g_D3D = NULL;
IDirect3DDevice9 * g_D3DDev = NULL;
D3DPRESENT_PARAMETERS g_D3Dpp;
IDirect3DTexture9 *tWebPNG;
ID3DXSprite *sWebPNG;
BOOL quit = FALSE;
#pragma endregion
#pragma region Helpers
void InitD3D() {
// Set viewport
D3DVIEWPORT9 vp = { 0, 0, g_D3Dpp.BackBufferWidth, g_D3Dpp.BackBufferHeight, 0, 1 };
g_D3DDev->SetViewport(&vp);
// Set D3D matrices
D3DMATRIX matId = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
g_D3DDev->SetTransform(D3DTS_WORLD, &matId);
g_D3DDev->SetTransform(D3DTS_VIEW, &matId);
D3DMATRIX matProj = { (float)g_D3Dpp.BackBufferHeight / g_D3Dpp.BackBufferWidth, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
g_D3DDev->SetTransform(D3DTS_PROJECTION, &matProj);
// Disable lighting and culling
g_D3DDev->SetRenderState(D3DRS_LIGHTING, FALSE);
g_D3DDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
g_D3DDev->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
}
wchar_t* _convert_UTF8_to_wchar(const char *str_utf8) {
wchar_t *str_w = NULL;
if (str_utf8) {
int str_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
str_utf8, -1, NULL, 0);
if (str_w_len > 0) {
str_w = (wchar_t*)malloc(str_w_len * sizeof(wchar_t));
if (str_w) {
if (MultiByteToWideChar(CP_UTF8, 0, str_utf8, -1, str_w,
str_w_len) == 0) {
free(str_w);
return NULL;
}
}
}
}
return str_w;
}
char* _convert_wchar_to_UTF8(const wchar_t *str_w) {
char *str_utf8 = NULL;
if (str_w) {
int str_utf8_len = WideCharToMultiByte(CP_UTF8, 0, str_w, -1, NULL,
0, NULL, NULL);
if (str_utf8_len > 0) {
str_utf8 = (char*)malloc(str_utf8_len * sizeof(wchar_t));
if (str_utf8) {
if (WideCharToMultiByte(CP_UTF8, 0, str_w, -1, str_utf8, str_utf8_len,
NULL, FALSE) == 0) {
free(str_utf8);
return NULL;
}
}
}
}
return str_utf8;
}
#pragma endregion
#pragma region IEBHO
/*Provide a virtual to dispatch (ie)JScall->BHO->Player*/
class CJSCallback {
public:
virtual void Callback(DISPPARAMS *pDispParams, VARIANT *pVarResult) {};
};
/*IE BHO implementing interfaces needed for interop.
IOleClientSite: needed by ie, moniker and object setup
IOleInPlaceFrame: needed by ie, implements IOleWindow and IOleInPlaceUIWindow too. Also used to get HWND of ole host, needed for passing events
IOleInPlaceSite: needed by ie, provides windows hosting. unfortunately MSHTML dont query IOleInPlaceSiteWindowless and also no alpha.
We trick this using IViewObject or OleDraw and using a hidden window, while alpha cannot still be used, its not needed here.
IDocHostUIHandler: used to hide context menus
IDocHostShowUI: used to capture and hide dialog messages
IDispatch: used for javascript "window.external" interface binding and to handle an advised DWebBrowserEvents2 event sink
*/
class CWBClientSite : public IOleClientSite, public IOleInPlaceFrame, public IOleInPlaceSite, public IDocHostUIHandler, public IDispatch, public IDocHostShowUI, public IOleInPlaceSiteWindowless, public IOleInPlaceSiteEx {
public:
CWBClientSite() : m_pRef(0) {}
CWBClientSite(HWND hwnd, CJSCallback* mp) {
m_hWindow = hwnd;
m_mp = mp;
}
// IUnknown methods
virtual HRESULT STDMETHODCALLTYPE QueryInterface(
/* [in] */ REFIID riid,
/* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) {
if (!memcmp(&riid, &IID_IUnknown, sizeof(GUID))) {
*ppvObject = reinterpret_cast<IUnknown *> (this);
}
else if (!memcmp(&riid, &IID_IOleClientSite, sizeof(GUID))) {
*ppvObject = (IOleClientSite *)this;
}
else if (!memcmp(&riid, &IID_IOleInPlaceSite, sizeof(GUID))) {
*ppvObject = (IOleInPlaceSite *)this;
}
else if (!memcmp(&riid, &IID_IDispatch, sizeof(GUID))) {
*ppvObject = (IDispatch *)this;
}
else if (!memcmp(&riid, &IID_IDocHostUIHandler, sizeof(GUID))) {
*ppvObject = (IDocHostUIHandler *)this;
}
else if (!memcmp(&riid, &IID_IDocHostShowUI, sizeof(GUID))) {
*ppvObject = (IDocHostShowUI *)this;
}
else if (!memcmp(&riid, &IID_IOleInPlaceSiteWindowless, sizeof(GUID))) {
*ppvObject = (IOleInPlaceSiteWindowless *)this;
}
else if (!memcmp(&riid, &IID_IOleInPlaceSiteEx, sizeof(GUID))) {
*ppvObject = (IOleInPlaceSiteEx *)this;
}
else {
*ppvObject = 0;
return(E_NOINTERFACE);
}
return(S_OK);
};
virtual ULONG STDMETHODCALLTYPE AddRef(void) {
return InterlockedIncrement(&m_pRef);
};
virtual ULONG STDMETHODCALLTYPE Release(void) {
int tmp = InterlockedDecrement(&m_pRef);
/*if (tmp == 0)
delete this;*/
//TODO: this is shit. crashes when "delete this" indicates that maybe theres a bad use of AddRef.
//pls further investigate it. AFAIK IDispatch should be referenced?
return tmp;
};
// IOleClientSite methods
virtual HRESULT STDMETHODCALLTYPE SaveObject(void) {
return E_NOTIMPL;
};
virtual HRESULT STDMETHODCALLTYPE GetMoniker(
/* [in] */ DWORD dwAssign,
/* [in] */ DWORD dwWhichMoniker,
/* [out] */ IMoniker **ppmk) {
return E_NOTIMPL;
};
virtual HRESULT STDMETHODCALLTYPE GetContainer(
/* [out] */ IOleContainer **ppContainer) {
*ppContainer = NULL;
return E_NOINTERFACE;
};
virtual HRESULT STDMETHODCALLTYPE ShowObject(void) {
return NOERROR;
};
virtual HRESULT STDMETHODCALLTYPE OnShowWindow(
/* [in] */ BOOL fShow) {
return E_NOTIMPL;
};
virtual HRESULT STDMETHODCALLTYPE RequestNewObjectLayout(void) {
return E_NOTIMPL;
};
// IOleWindow methods
virtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE GetWindow(
/* [out] */ HWND *phwnd) {
*phwnd = m_hWindow;
return S_OK;
};
virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(
/* [in] */ BOOL fEnterMode) {
return E_NOTIMPL;
};
// IOleInPlaceUIWindow methods
virtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE GetBorder(
/* [out] */ LPRECT lprectBorder) {
return E_NOTIMPL;
};
virtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE RequestBorderSpace(
/* [unique][in] */ LPCBORDERWIDTHS pborderwidths) {
return E_NOTIMPL;
};
virtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE SetBorderSpace(
/* [unique][in] */ LPCBORDERWIDTHS pborderwidths) {
return E_NOTIMPL;
};
virtual HRESULT STDMETHODCALLTYPE SetActiveObject(
/* [unique][in] */ IOleInPlaceActiveObject *pActiveObject,
/* [unique][string][in] */ LPCOLESTR pszObjName) {
if (pActiveObject == NULL) return S_OK;
HWND wnd = NULL;
pActiveObject->GetWindow(&wnd);
//Fake a js callback to inform player of hwnd
if (m_mp && wnd != NULL) {
DISPPARAMS pdisp;
pdisp.cArgs = 2;
pdisp.rgvarg = new VARIANT[2];
pdisp.rgvarg[1].vt = VT_I4;
pdisp.rgvarg[1].intVal = GWLP_HWNDPARENT;
pdisp.rgvarg[0].vt = VT_I4;
pdisp.rgvarg[0].intVal = (int)wnd;
m_mp->Callback(&pdisp, NULL);
delete[] pdisp.rgvarg;
}
SetWindowLong(wnd, GWL_EXSTYLE, WS_EX_NOACTIVATE);
SetWindowLong(wnd, GWL_STYLE, WS_EX_NOACTIVATE);
/*HWND k = GetWindow(wnd, GW_CHILD);
k = GetWindow(k, GW_CHILD);
*/
return S_OK;
};
// IOleInPlaceFrame methods
virtual HRESULT STDMETHODCALLTYPE InsertMenus(
/* [in] */ HMENU hmenuShared,
/* [out][in] */ LPOLEMENUGROUPWIDTHS lpMenuWidths) {
return E_NOTIMPL;
};
virtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE SetMenu(
/* [in] */ HMENU hmenuShared,
/* [in] */ HOLEMENU holemenu,
/* [in] */ HWND hwndActiveObject) {
return S_OK;
};
virtual HRESULT STDMETHODCALLTYPE RemoveMenus(
/* [in] */ HMENU hmenuShared) {
return E_NOTIMPL;
};
virtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE SetStatusText(
/* [unique][in] */ LPCOLESTR pszStatusText) {
return S_OK;
};
virtual HRESULT STDMETHODCALLTYPE EnableModeless(
/* [in] */ BOOL fEnable) {
return S_OK;
};
virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator(
/* [in] */ LPMSG lpmsg,
/* [in] */ WORD wID) {
return E_NOTIMPL;
};
// IOleInPlaceSite methods
virtual HRESULT STDMETHODCALLTYPE CanInPlaceActivate(void) {
return S_OK;
};
virtual HRESULT STDMETHODCALLTYPE OnInPlaceActivate(void) {
return S_OK;
};
virtual HRESULT STDMETHODCALLTYPE OnUIActivate(void) {
return S_OK;
};
virtual HRESULT STDMETHODCALLTYPE GetWindowContext(
/* [out] */ IOleInPlaceFrame **ppFrame,
/* [out] */ IOleInPlaceUIWindow **ppDoc,
/* [out] */ LPRECT lprcPosRect,
/* [out] */ LPRECT lprcClipRect,
/* [out][in] */ LPOLEINPLACEFRAMEINFO lpFrameInfo) {
*ppFrame = (IOleInPlaceFrame *)this;
*ppDoc = 0;
lpFrameInfo->fMDIApp = FALSE;
lpFrameInfo->hwndFrame = m_hWindow;
lpFrameInfo->haccel = 0;
lpFrameInfo->cAccelEntries = 0;
GetClientRect(lpFrameInfo->hwndFrame, lprcPosRect);
GetClientRect(lpFrameInfo->hwndFrame, lprcClipRect);
return S_OK;
};
virtual HRESULT STDMETHODCALLTYPE Scroll(
/* [in] */ SIZE scrollExtant) {
return E_NOTIMPL;
};
virtual HRESULT STDMETHODCALLTYPE OnUIDeactivate(
/* [in] */ BOOL fUndoable) {
return S_OK;
};
virtual HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate(void) {
return S_OK;
};
virtual HRESULT STDMETHODCALLTYPE DiscardUndoState(void) {
return E_NOTIMPL;
};
virtual HRESULT STDMETHODCALLTYPE DeactivateAndUndo(void) {
return E_NOTIMPL;
};
virtual HRESULT STDMETHODCALLTYPE OnPosRectChange(
/* [in] */ LPCRECT lprcPosRect) {
return S_OK;
};
//IDispatch
virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) {
*pctinfo = 0;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid,
ITypeInfo **ppTInfo) {
return E_FAIL;
}
//Fake dispid for "window.external.cb(xxx)" javascript object
#define DISPID_USERCALL_CB 667788
//Called by IE Javascript when using "window.external", will ask a dispid for given name to be used on Invoke
virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid,
LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) {
//LOGW(L"%s", rgszNames[0]);
HRESULT hr = S_OK;
for (int i = 0; i < cNames; i++) {
//If name is "cb" send correct dispid to Invoke
if (rgszNames[i][0] == L'c' && rgszNames[i][1] == L'b') {
rgDispId[i] = DISPID_USERCALL_CB;
}
else {
rgDispId[i] = DISPID_UNKNOWN;
hr = DISP_E_UNKNOWNNAME;
}
}
return hr;
}
//Javascript Calls and DWebBrowserEvents2 sink will be processed here
virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid,
LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,
EXCEPINFO *pExcepInfo, UINT *puArgErr) {
//LOG("%d", dispIdMember);
switch (dispIdMember) {
case DISPID_USERCALL_CB: {
if (m_mp)
m_mp->Callback(pDispParams, pVarResult);
} break;
case DISPID_DOCUMENTCOMPLETE: {
if (m_mp) {
DISPPARAMS pdisp;
pdisp.cArgs = 2;
pdisp.rgvarg = new VARIANT[2];
pdisp.rgvarg[1].vt = VT_I4;
pdisp.rgvarg[1].intVal = DISPID_DOCUMENTCOMPLETE;
pdisp.rgvarg[0].vt = VT_I4;
pdisp.rgvarg[0].intVal = DISPID_DOCUMENTCOMPLETE;
m_mp->Callback(&pdisp, NULL);
delete[] pdisp.rgvarg;
}
} break;
case DISPID_AMBIENT_DLCONTROL:
//LOG("DISPID_AMBIENT_DLCONTROL");
pVarResult->vt = VT_I4;
pVarResult->lVal = DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_BGSOUNDS | DLCTL_SILENT | DLCTL_URL_ENCODING_ENABLE_UTF8 | DLCTL_NO_BEHAVIORS;
break;
default:
return DISP_E_MEMBERNOTFOUND;
}
return S_OK;
}
//IDocHostUiHandler
virtual HRESULT STDMETHODCALLTYPE ShowContextMenu(DWORD dwID, POINT *ppt,
IUnknown *pcmdtReserved, IDispatch *pdispReserved) {
//if (dwID == 0) //hide all context menus (rbutton)
return S_OK;
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetHostInfo(DOCHOSTUIINFO *pInfo) {
pInfo->dwFlags = pInfo->dwFlags |
DOCHOSTUIFLAG_DISABLE_HELP_MENU |
DOCHOSTUIFLAG_NO3DBORDER |
DOCHOSTUIFLAG_SCROLL_NO |
DOCHOSTUIFLAG_NO3DOUTERBORDER |
DOCHOSTUIFLAG_DPI_AWARE |
DOCHOSTUIFLAG_USE_WINDOWLESS_SELECTCONTROL |
DOCHOSTUIFLAG_OVERRIDEBEHAVIORFACTORY |
DOCHOSTUIFLAG_NOTHEME;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE ShowUI(DWORD dwID,
IOleInPlaceActiveObject *pActiveObject,
IOleCommandTarget *pCommandTarget, IOleInPlaceFrame *pFrame,
IOleInPlaceUIWindow *pDoc) {
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE HideUI() {
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE UpdateUI() {
return S_OK;
}
/*virtual HRESULT STDMETHODCALLTYPE EnableModeless(BOOL fEnable) {
return S_OK;
}*/
virtual HRESULT STDMETHODCALLTYPE OnDocWindowActivate(BOOL fActivate) {
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE OnFrameWindowActivate(BOOL fActivate) {
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE ResizeBorder(LPCRECT prcBorder,
IOleInPlaceUIWindow *pUIWindow, BOOL fRameWindow) {
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator(LPMSG lpMsg,
const GUID *pguidCmdGroup, DWORD nCmdID) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetOptionKeyPath(LPOLESTR *pchKey,
DWORD dw) {
return S_FALSE;
}
virtual HRESULT STDMETHODCALLTYPE GetDropTarget(IDropTarget *pDropTarget,
IDropTarget **ppDropTarget) {
return S_FALSE;
}
virtual HRESULT STDMETHODCALLTYPE GetExternal(IDispatch **ppDispatch) {
*ppDispatch = this;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE TranslateUrl(DWORD dwTranslate,
OLECHAR *pchURLIn, OLECHAR **ppchURLOut) {
*ppchURLOut = 0;
return S_FALSE;
}
virtual HRESULT STDMETHODCALLTYPE FilterDataObject(IDataObject *pDO,
IDataObject **ppDORet) {
*ppDORet = 0;
return S_FALSE;
}
//IDocHostShowUI
virtual HRESULT STDMETHODCALLTYPE ShowMessage(HWND hwnd,
LPOLESTR lpstrText, LPOLESTR lpstrCaption, DWORD dwType,
LPOLESTR lpstrHelpFile, DWORD dwHelpContext, LRESULT *plResult) {
*plResult = IDCANCEL;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE ShowHelp(HWND hwnd, LPOLESTR pszHelpFile,
UINT uCommand, DWORD dwData, POINT ptMouse,
IDispatch *pDispatchObjectHit) {
return S_OK;
}
//IOleInPlaceSiteWindowless
HRESULT STDMETHODCALLTYPE CanWindowlessActivate(void) {
return S_OK;
}
HRESULT STDMETHODCALLTYPE GetCapture(void) {
return S_OK;
}
HRESULT STDMETHODCALLTYPE SetCapture(
/* [in] */ BOOL fCapture) {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE GetFocus(void) {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE SetFocus(
/* [in] */ BOOL fFocus) {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE GetDC(
/* [unique][in] */ __RPC__in_opt LPCRECT pRect,
/* [in] */ DWORD grfFlags,
/* [out] */ __RPC__deref_out_opt HDC *phDC) {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE ReleaseDC(
/* [in] */ __RPC__in HDC hDC) {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE InvalidateRect(
/* [unique][in] */ __RPC__in_opt LPCRECT pRect,
/* [in] */ BOOL fErase) {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE InvalidateRgn(
/* [in] */ __RPC__in HRGN hRGN,
/* [in] */ BOOL fErase) {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE ScrollRect(
/* [in] */ INT dx,
/* [in] */ INT dy,
/* [in] */ __RPC__in LPCRECT pRectScroll,
/* [in] */ __RPC__in LPCRECT pRectClip) {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE AdjustRect(
/* [out][in] */ __RPC__inout LPRECT prc) {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE OnDefWindowMessage(
/* [annotation][in] */
_In_ UINT msg,
/* [annotation][in] */
_In_ WPARAM wParam,
/* [annotation][in] */
_In_ LPARAM lParam,
/* [out] */ __RPC__out LRESULT *plResult) {
return E_NOTIMPL;
}
//IOleInPlaceSiteEx
HRESULT STDMETHODCALLTYPE OnInPlaceActivateEx(
/* [out] */ __RPC__out BOOL *pfNoRedraw,
/* [in] */ DWORD dwFlags) {
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnInPlaceDeactivateEx(
/* [in] */ BOOL fNoRedraw) {
return S_OK;
}
HRESULT STDMETHODCALLTYPE RequestUIActivate(void) {
return S_OK;
}
HWND m_hWindow;
long m_pRef;
CJSCallback* m_mp;
};
#pragma endregion
#pragma region CWebBrowserIE
class CWebBrowserIE {
public:
CWebBrowserIE(CJSCallback* mp) : m_pBrowserObj(NULL), m_pWebBrowser2(NULL), m_mp(mp) {
m_width = 1, m_height = 1;
m_hwnd = CreateWindowExW(WS_EX_NOACTIVATE, L"static", L"", WS_POPUP | WS_OVERLAPPED | WS_CHILD | WS_EX_NOACTIVATE,
0, 0, m_width, m_height,
g_wnd, NULL, g_hinst, NULL);
ShowWindow(m_hwnd, SW_HIDE);
//EmbedBrowserObject(m_hwnd);
m_pHtmlDocument = NULL;
m_pViewObj = NULL;
};
~CWebBrowserIE(void) {
m_pHtmlDocument->Release();
if (m_pBrowserObj != nullptr)
UnEmbedBrowserObject();
if (m_hwnd != nullptr)
DestroyWindow(m_hwnd);
if (m_hOldBitmap != nullptr)
DeleteObject(m_hOldBitmap);
if (m_hBitmap != nullptr)
DeleteObject(m_hBitmap);
if (m_hBitmap != nullptr)
DeleteDC(m_hMemDC);
};
IWebBrowser2* GetWebBrowser2Ptr() {
return m_pWebBrowser2;
}
IOleObject* GetOlePtr() {
return m_pBrowserObj;
}
void UnEmbedBrowserObject() {
if (m_pBrowserObj) {
IConnectionPointContainer *cpc = 0;
m_pBrowserObj->QueryInterface(IID_IConnectionPointContainer, (void**)&cpc);
if (cpc != 0) {
IConnectionPoint *cp = 0;
cpc->FindConnectionPoint(DIID_DWebBrowserEvents2, &cp);
if (cp != 0) {
cp->Unadvise(m_cookie);
cp->Release();
}
cpc->Release();
}
m_pBrowserObj->Close(OLECLOSE_NOSAVE);
m_pBrowserObj->Release();
m_pBrowserObj = nullptr;
return;
}
};
HRESULT EmbedBrowserObject(HWND hwnd) {
HRESULT hr = S_OK;
RECT rect;
CWBClientSite *pClientSite = new CWBClientSite(hwnd, m_mp);
//if (!OleCreate(CLSID_WebBrowser, IID_IOleObject, OLERENDER_DRAW, 0, (IOleClientSite *)pClientSite, &Storage, (void**)&m_pBrowserObj))
hr = CoCreateInstance(CLSID_WebBrowser, NULL, CLSCTX_INPROC_SERVER, IID_IOleObject, (void**)&m_pBrowserObj);
if (hr == S_OK) {
hr = m_pBrowserObj->SetClientSite((IOleClientSite *)pClientSite);
/*OleSetContainedObject(m_pBrowserObj, TRUE);
RECT rc;
GetClientRect(hwnd, &rc);
m_pBrowserObj->DoVerb(OLEIVERB_SHOW, 0, pClientSite, 0, hwnd, &rc);*/
hr = m_pBrowserObj->SetHostNames(L"IEKKKONTROL", 0);
GetClientRect(hwnd, &rect);
if (!OleSetContainedObject((IUnknown *)m_pBrowserObj, TRUE)) {
if (!m_pBrowserObj->DoVerb(OLEIVERB_SHOW, NULL, (IOleClientSite *)pClientSite, -1, hwnd, &rect)) {
if (!m_pBrowserObj->QueryInterface(IID_IWebBrowser2, (void**)&m_pWebBrowser2)) {
m_pWebBrowser2->put_Left(0);
m_pWebBrowser2->put_Top(0);
m_pWebBrowser2->put_Width(rect.right - rect.left);
m_pWebBrowser2->put_Height(rect.bottom - rect.top);
bbits = (unsigned char*)malloc((rect.right - rect.left)*(rect.bottom - rect.top) * 5);
#ifdef BIT255ALPHA
wbits = (unsigned char*)malloc((rect.right - rect.left)*(rect.bottom - rect.top) * 5);
#endif
m_pWebBrowser2->put_Silent(VARIANT_TRUE);
//Advise events
bool connected = false;
IConnectionPointContainer *cpc = 0;
m_pBrowserObj->QueryInterface(IID_IConnectionPointContainer, (void**)&cpc);
if (cpc != 0) {
IConnectionPoint *cp = 0;
cpc->FindConnectionPoint(DIID_DWebBrowserEvents2, &cp);
if (cp != 0) {
cp->Advise((IDispatch*)pClientSite, &m_cookie);
cp->Release();
connected = true;
}
cpc->Release();
}
//m_pWebBrowser2->Release();
return hr;
}
}
}
if (pClientSite)
delete pClientSite;
UnEmbedBrowserObject();
return -3;
}
return -2;
};
HRESULT DisplayURL(const wchar_t* url) {
HRESULT hr = S_OK;
VARIANT varMyURL;
varMyURL.vt = VT_BSTR;
varMyURL.bstrVal = SysAllocString(url);
hr = m_pWebBrowser2->Navigate2(&varMyURL, 0, 0, 0, 0);
SysFreeString(varMyURL.bstrVal);
return hr;
};
HRESULT DisplayHTMLPage(const wchar_t* htmlContents) {
HRESULT hr = S_OK;
VARIANT varMyURL;
VariantInit(&varMyURL);
varMyURL.vt = VT_BSTR;
varMyURL.bstrVal = SysAllocString(L"about:blank");
hr = m_pWebBrowser2->Navigate2(&varMyURL, 0, 0, 0, 0);
VariantClear(&varMyURL);
IDispatch* p_doc;
m_pWebBrowser2->get_Document(&p_doc);
BSTR pb = SysAllocString(htmlContents);
IPersistStreamInit *psi;
hr = p_doc->QueryInterface(IID_IPersistStreamInit, (void**)&psi);
HGLOBAL hMem = ::GlobalAlloc(GPTR, wcslen(pb) * sizeof(wchar_t));
::GlobalLock(hMem);
::CopyMemory(hMem, (LPCTSTR)pb, wcslen(pb) * sizeof(wchar_t));
IStream* stream = NULL;
hr = ::CreateStreamOnHGlobal(hMem, FALSE, &stream);
if (SUCCEEDED(hr)) {
hr = psi->Load(stream);
stream->Release();
}
::GlobalUnlock(hMem);
::GlobalFree(hMem);
psi->Release();
SysFreeString(pb);
p_doc->Release();
return hr;
};
HRESULT Render(D3DLOCKED_RECT* lr, RECT re) {
HRESULT hr = S_OK;
if (m_isFullscreen) return hr;
int ww = re.right - re.left;
int hh = re.bottom - re.top;
if (IsWindow(m_hwnd) && (ww != m_width || hh != m_height)) {
m_width = ww, m_height = hh;
MoveWindow(m_hwnd, 0, 0, ww, hh, TRUE);
m_pWebBrowser2->put_Width(ww);
m_pWebBrowser2->put_Height(hh);
IOleInPlaceObject *iole = NULL;
hr = m_pBrowserObj->QueryInterface(IID_IOleInPlaceObject, (void**)&iole);
if (iole != NULL) {
iole->SetObjectRects(&re, &re);
iole->Release();
}
if (m_hBitmap) {
DeleteObject(m_hBitmap);
/*m_hBitmap = CreateCompatibleBitmap(GetDC(m_hwnd), ww, hh);*/
m_hBitmap = NULL;
}
free(bbits);
bbits = (unsigned char*)malloc(ww*hh * 5);
#ifdef BIT255ALPHA
free(wbits);
wbits = (unsigned char*)malloc(ww*hh * 5);
#endif
m_bmi.biSize = sizeof(BITMAPINFOHEADER);
m_bmi.biPlanes = 1;
m_bmi.biBitCount = 32;
m_bmi.biWidth = m_width;
m_bmi.biHeight = -m_height;
m_bmi.biCompression = BI_RGB;
m_bmi.biSizeImage = m_width * m_height;
}
if (!m_hMemDC)
m_hMemDC = CreateCompatibleDC(GetDC(m_hwnd));
if (!m_hBitmap) {
m_hBitmap = CreateCompatibleBitmap(GetDC(m_hwnd), ww, hh);
}
m_hOldBitmap = (HBITMAP)SelectObject(m_hMemDC, m_hBitmap);
if (m_pViewObj == NULL)
m_pWebBrowser2->QueryInterface(IID_IViewObject, (void**)&m_pViewObj);
if (m_pHtmlDocument == NULL) {
IDispatch* docdisp;
m_pWebBrowser2->get_Document(&docdisp);
if (docdisp == NULL)
return -1;
docdisp->QueryInterface(IID_IHTMLDocument2, (void**)&m_pHtmlDocument);
docdisp->Release();
}
VARIANT cvt;
cvt.vt = VT_I4;
RECTL rect;
rect.bottom = re.bottom;
rect.top = re.top;
rect.left = re.left;
rect.right = re.right;
unsigned char* destBuffer = static_cast<unsigned char*>(lr->pBits);
//-- black pass
cvt.intVal = 0x000000;
m_pHtmlDocument->put_bgColor(cvt);
m_pViewObj->Draw(DVASPECT_CONTENT, -1, 0, 0, m_hMemDC, m_hMemDC, &rect, &rect, 0, 0);
GetDIBits(m_hMemDC, m_hBitmap, 0, m_height, bbits, (BITMAPINFO*)&m_bmi, DIB_RGB_COLORS);
#ifdef BIT255ALPHA
//-- white pass
cvt.intVal = 0xFFFFFF;
m_pHtmlDocument->put_bgColor(cvt);
m_pViewObj->Draw(DVASPECT_CONTENT, -1, 0, 0, m_hMemDC, m_hMemDC, &rect, &rect, 0, 0);
GetDIBits(m_hMemDC, m_hBitmap, 0, m_height, wbits, (BITMAPINFO*)&m_bmi, DIB_RGB_COLORS);
#endif
//hr = ::OleDraw(m_pWebBrowser2, DVASPECT_CONTENT, m_hMemDC, &re);
int rnorm = 0, gnorm = 0, bnorm = 0, c = 0, cc = 0, pdif = lr->Pitch - (ww * 4);
for (int cy = 0; cy < hh; cy++) {
memcpy(&destBuffer[cc], &bbits[c], ww * 4);
for (int cx = 0; cx < ww; cx++) {
#ifdef BIT255ALPHA
//-- no min/max since white pass will be always be > black pass
rnorm = wbits[c + 0] - bbits[c + 0];
gnorm = wbits[c + 1] - bbits[c + 1];
bnorm = wbits[c + 2] - bbits[c + 2];
//-- 255 bits alpha from black/white interpolation
destBuffer[cc + 3] = 255 - ((rnorm + gnorm + bnorm) / 3);
#else
//-- 1 bit alpha with 1 pass, set background to "rgb(0,255,0)"
destBuffer[cc + 3] = (bbits[c + 0] == 0 && bbits[c + 2] == 0 && bbits[c + 1] == 255) ? 0 : 255;
#endif
c += 4;
cc += 4;
}
cc += pdif;
}
m_hBitmap = (HBITMAP)SelectObject(m_hMemDC, m_hOldBitmap);
return hr;
}
void CreateFakeDocument(const char* doc) {
BSTR html = _convert_UTF8_to_wchar(doc);
DisplayHTMLPage(html);
delete[] html;
}
HRESULT SendCommand(int a, VARIANT* pRes) {
return SendCommand(a, NULL, pRes);
}
HRESULT SendCommand(int a) {
return SendCommand(a, NULL, NULL);
}
HRESULT SendCommand(int a, int b, VARIANT* pRes) {
HRESULT hr = S_OK;
IDispatch* p_doc;
hr = m_pWebBrowser2->get_Document(&p_doc);
if (hr != S_OK) return hr;
IHTMLDocument2* doc;
hr = p_doc->QueryInterface(IID_IHTMLDocument2, (void**)&doc);
if (hr != S_OK) return hr;
IHTMLWindow2* win;
hr = doc->get_parentWindow(&win);
IDispatchEx *winEx;
win->QueryInterface(&winEx);
win->Release();
doc->Release();
p_doc->Release();
if (winEx == NULL)
return E_FAIL;
DISPID dispid;
BSTR idname = SysAllocString(L"onCommand");
hr = winEx->GetDispID(idname, fdexNameEnsure, &dispid);
SysFreeString(idname);
if (FAILED(hr))
return E_FAIL;
DISPID namedArgs[] = { DISPID_PROPERTYPUT,DISPID_PROPERTYPUT };
DISPPARAMS params;
params.rgvarg = new VARIANT[2];
params.rgvarg[0].intVal = b;
params.rgvarg[0].vt = VT_I4;
params.rgvarg[1].intVal = a;
params.rgvarg[1].vt = VT_I4;
params.rgdispidNamedArgs = namedArgs;
params.cArgs = 2;
params.cNamedArgs = 2;
hr = winEx->InvokeEx(dispid, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, pRes, NULL, NULL);
delete[] params.rgvarg;
if (FAILED(hr))
return E_FAIL;
winEx->Release();
return hr;
}
HRESULT SetFullScreen(BOOL isFullscreen) {
HRESULT hr = S_OK;
if (isFullscreen) {
m_isFullscreen = TRUE;
m_width = GetSystemMetrics(SM_CXSCREEN);
m_height = GetSystemMetrics(SM_CYSCREEN);
RECT re;
re.top = 0, re.left = 0, re.right = m_width, re.bottom = m_height;
MoveWindow(m_hwnd, 0, 0, m_width, m_height, TRUE);
m_pWebBrowser2->put_Width(m_width);
m_pWebBrowser2->put_Height(m_height);
IOleInPlaceObject *iole = NULL;
hr = m_pBrowserObj->QueryInterface(IID_IOleInPlaceObject, (void**)&iole);
if (iole != NULL) {
iole->SetObjectRects(&re, &re);
iole->Release();
}
ShowWindow(m_hwnd, SW_SHOW);
LONG lExStyle = GetWindowLong(m_hwnd, GWL_EXSTYLE);
lExStyle &= ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_NOACTIVATE);
SetWindowLong(m_hwnd, GWL_EXSTYLE, lExStyle);
SetActiveWindow(m_hwnd);
}
else {
m_isFullscreen = FALSE;
ShowWindow(m_hwnd, SW_HIDE);
}
return hr;
}
private:
IOleObject * m_pBrowserObj;
IWebBrowser2 * m_pWebBrowser2;
IHTMLDocument2* m_pHtmlDocument;
IViewObject* m_pViewObj;
HWND m_hwnd;
int m_width, m_height;
HDC m_hMemDC;
HBITMAP m_hBitmap, m_hOldBitmap;
BOOL m_isFullscreen;
int m_temp_width, m_temp_height;
CJSCallback* m_mp;
DWORD m_cookie;
long m_LastIERender;
BITMAPINFOHEADER m_bmi;
D3DLOCKED_RECT* m_lockedrect;
RECT m_rect;
unsigned char* bbits = NULL;
#ifdef BIT255ALPHA
unsigned char* wbits = NULL;
#endif
};
#pragma endregion
#pragma region CWebBrowser
class CWebBrowser : public CJSCallback {
public:
CWebBrowser() {
m_pTexture = NULL;
m_pSprite = NULL;
m_width = 0;
m_height = 0;
#ifdef IEONSEPARATETHREAD
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)IEThread, this, NULL, NULL);
Sleep(500);
#else
m_webView = new CWebBrowserIE(this);
#endif
};
#ifdef IEONSEPARATETHREAD
static int IEThread(CWebBrowser* cwb) {
cwb->m_webView = new CWebBrowserIE(cwb);
while (cwb->m_webView) {
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
quit = true;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(30);
}
return 0;
}
#endif
~CWebBrowser() {
SAFE_RELEASE(m_pTexture);
SAFE_RELEASE(m_pSprite);
delete m_webView;
};
void Callback(DISPPARAMS *pDispParams, VARIANT *pVarResult) {
if (pDispParams->rgvarg[1].vt == VT_I4 && pDispParams->rgvarg[1].intVal == GWLP_HWNDPARENT) {
m_ieHWND = (HWND)pDispParams->rgvarg[0].intVal;
}
if (pDispParams->rgvarg[0].vt == VT_I4 && pDispParams->rgvarg[1].vt == VT_BSTR) {
wprintf(L"js cb: %s - %d\n", pDispParams->rgvarg[1].bstrVal, pDispParams->rgvarg[0].intVal);
m_webView->SendCommand(1337, 1337, NULL);
}
};
void Destroy() {
if (m_webView) {
m_webView->UnEmbedBrowserObject();
}
};
bool CreateInstance(int width, int height, IDirect3DDevice9* g_D3DDev) {
if (width == 0 || height == 0) return false;
m_width = width;
m_height = height;
m_webView->EmbedBrowserObject(g_wnd);
#ifdef BIT255ALPHA
m_webView->CreateFakeDocument("<html><meta http-equiv=\"X-UA-Compatible\" content=\"IE=11\" /><head><script>function onCommand(arg, arg1){document.write(arg +\" | \"+ arg1);}</script><style>body {background-image: url(https://www.nicepng.com/png/full/101-1010964_sl-powerpoint-moving-animation-background.png);background-size:cover;} #options{ width:230px;height:160px;margin:40px;background:rgba(100,100,100,.5);border:3px solid #555; padding:15px; }</style></head><body><img src='https://media.tenor.com/images/fa589462d8f94bb96b2de02fa8bca505/tenor.gif' style='float:right; width:200px;' /><div id='options'><h3>IE Overlay</h3>Test Test Test Test Test Test Test Test Test Test Test Test Test<br/><select><option>Capture device 1</option><option>Capture device 2</option><option>Capture device 3</option><option>Capture device 4</option><option>Capture device 5</option></select></div><center><h1>This is an overlay</h1><br/>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</center><button onclick=\"window.external.cb('very mong',1337)\">Do js call</button></body></html>");
#else
m_webView->CreateFakeDocument("<html><meta http-equiv=\"X-UA-Compatible\" content=\"IE=11\" /><head><script>function onCommand(arg, arg1){document.write(arg +\" | \"+ arg1);}</script><style>body {background-color:rgb(0,255,0);} #options{ width:230px;height:160px;margin:40px;background:rgba(100,100,100,1);border:3px solid #555; padding:15px; }</style></head><body><div id='options'><h3>IE Overlay</h3>Test Test Test Test Test Test Test Test Test Test Test Test Test<br/><select><option>Capture device 1</option><option>Capture device 2</option><option>Capture device 3</option><option>Capture device 4</option><option>Capture device 5</option></select></div><center><h1>This is an overlay</h1><br/>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</center><button onclick=\"window.external.cb('very mong',1337)\">Do js call</button></body></html>");
#endif
return true;
};
void TextureInit(IDirect3DDevice9* g_D3DDev) {
D3DXCreateTexture(g_D3DDev, m_width, m_height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pTexture);
D3DXCreateSprite(g_D3DDev, &m_pSprite);
};
void TextureInvalidate() {
SAFE_RELEASE(m_pTexture);
SAFE_RELEASE(m_pSprite);
};
void Render() {
if (m_webView && m_pTexture && m_pSprite) {
int gtt = GetTickCount();
if (m_LastIERender + (80) < gtt) {
m_LastIERender = gtt;
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = m_width;
rect.bottom = m_height;
D3DLOCKED_RECT lockedRect;
HRESULT lock = m_pTexture->LockRect(0, &lockedRect, NULL, D3DLOCK_DISCARD | D3DLOCK_DONOTWAIT);
if (SUCCEEDED(lock))
{
m_webView->Render(&lockedRect, rect);
m_pTexture->UnlockRect(0);
}
}
D3DXMATRIX mat2;
D3DXVECTOR2 axisWeb = D3DXVECTOR2(0.0f, 0.0f);
D3DXMatrixTransformation2D(&mat2, NULL, 0.0f, NULL, NULL, 0.0f, &axisWeb);
m_pSprite->Begin(D3DXSPRITE_ALPHABLEND);
m_pSprite->SetTransform(&mat2);
m_pSprite->Draw(m_pTexture, NULL, NULL, NULL, 0xFFFFFFFF);
m_pSprite->End();
}
};
bool ProcessControl(HWND wnd, UINT umsg, WPARAM wparam, LPARAM lparam) {
if (umsg == WM_LBUTTONDOWN || umsg == WM_LBUTTONUP || umsg == WM_RBUTTONDOWN || umsg == WM_RBUTTONUP || umsg == WM_MOUSEMOVE) {
if (m_iectlHWND == NULL) {
m_iectlHWND = GetWindow(m_ieHWND, GW_CHILD);
m_iectlHWND = GetWindow(m_iectlHWND, GW_CHILD);
}
SendMessage(m_iectlHWND, umsg, wparam, lparam);
}
return false;
};
void ResizeView(int width, int height) {
m_width = width;
m_height = height;
};
CWebBrowserIE* m_webView;
int m_width, m_height;
HWND m_ieHWND;
HWND m_iectlHWND;
IDirect3DTexture9 * m_pTexture;
ID3DXSprite * m_pSprite;
long m_LastIERender;
};
#pragma endregion
CWebBrowser* g_webview = NULL;
ATOM MyRegisterClass(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
OleInitialize(NULL);
g_hinst = hInstance;
swprintf_s(szWindowClass, L"DxIEOverlay");
swprintf_s(szTitle, L"D3D9IEOverlayAlpha");
MyRegisterClass(hInstance);
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, WIDTH, HEIGHT, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
return FALSE;
g_wnd = hWnd;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
g_D3D = Direct3DCreate9(D3D_SDK_VERSION);
if (!g_D3D)
return FALSE;
ZeroMemory(&g_D3Dpp, sizeof(D3DPRESENT_PARAMETERS));
g_D3Dpp.Windowed = TRUE;
g_D3Dpp.BackBufferCount = 1;
g_D3Dpp.SwapEffect = D3DSWAPEFFECT_FLIP;
g_D3Dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
//g_D3Dpp.BackBufferFormat = D3DFMT_UNKNOWN;
g_D3Dpp.hDeviceWindow = hWnd;
g_D3Dpp.EnableAutoDepthStencil = TRUE;
g_D3Dpp.AutoDepthStencilFormat = D3DFMT_D16;
g_D3Dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
HRESULT hr = g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &g_D3Dpp, &g_D3DDev);
if (FAILED(hr)) {
g_D3D->Release();
g_D3D = NULL;
return FALSE;
}
struct Vertex {
float x, y, z;
DWORD color;
};
Vertex vertices[2002];
int numSec = 1000; // number of strip sections
float color[] = { 1, 0, 0 }; // strip color
unsigned int bgColor = D3DCOLOR_ARGB(0, 228, 228, 228); // background color
InitD3D();
g_webview = new CWebBrowser();
g_webview->CreateInstance(WIDTH, HEIGHT, g_D3DDev);
g_webview->TextureInit(g_D3DDev);
DWORD t0 = GetTickCount();
while (!quit) {
if (!IsIconic(g_wnd)) {
g_D3DDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, bgColor, 1.0f, 0);
g_D3DDev->BeginScene();
float s, t = (float)(GetTickCount() - t0) / 1000.0f;
for (int i = 0; i <= numSec; ++i) {
s = (float)i / 100;
vertices[2 * i + 0].x = 0.05f + 0.7f*cosf(2.0f*s + 5.0f*t);
vertices[2 * i + 1].x = vertices[2 * i + 0].x + (0.25f + 0.1f*cosf(s + t));
vertices[2 * i + 0].y = vertices[2 * i + 1].y = 0.7f*(0.7f + 0.3f*sinf(s + t))*sinf(1.5f*s + 3.0f*t);
vertices[2 * i + 0].z = vertices[2 * i + 1].z = 0;
s = (float)i / numSec;
vertices[2 * i + 0].color = vertices[2 * i + 1].color =
D3DCOLOR_XRGB((int)(255 * color[0] * s), (int)(255 * color[1] * s), (int)(255 * color[2] * s));
}
g_D3DDev->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE);
g_D3DDev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2 * numSec, vertices, sizeof(Vertex)); // draw strip
g_webview->Render();
g_D3DDev->EndScene();
g_D3DDev->Present(NULL, NULL, NULL, NULL);
}
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
quit = true;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(1000/60);
}
g_webview->Destroy();
g_D3DDev->Release();
g_D3DDev = NULL;
g_D3D->Release();
g_D3D = NULL;
OleUninitialize();
return 0;
}
ATOM MyRegisterClass(HINSTANCE hInstance){
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = NULL;
return RegisterClassExW(&wcex);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_CHAR:
if (wParam == VK_ESCAPE) {
quit = TRUE;
PostQuitMessage(0);
}
break;
case WM_SIZE:
if (g_D3DDev) {
g_D3Dpp.BackBufferWidth = LOWORD(lParam);
g_D3Dpp.BackBufferHeight = HIWORD(lParam);
if (g_D3Dpp.BackBufferWidth>0 && g_D3Dpp.BackBufferHeight>0) {
g_webview->TextureInvalidate();
HRESULT hr = g_D3DDev->Reset(&g_D3Dpp);
if (hr == D3D_OK) {
InitD3D();
g_webview->ResizeView(LOWORD(lParam), HIWORD(lParam));
g_webview->TextureInit(g_D3DDev);
}
}
}
break;
case WM_DESTROY:
quit = TRUE;
PostQuitMessage(0);
break;
}
if (g_webview) g_webview->ProcessControl(hWnd, message, wParam, lParam);
return DefWindowProc(hWnd, message, wParam, lParam);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment