/*
 * OpenClonk, http://www.openclonk.org
 *
 * Copyright (c) 2002, 2004-2007  Sven Eberhardt
 * Copyright (c) 2004-2011  Günther Brammer
 * Copyright (c) 2007  Peter Wortmann
 * Copyright (c) 2008  Matthes Bender
 * Copyright (c) 2009  Nicolas Hake
 * Copyright (c) 2010  Armin Burgmeier
 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
 *
 * Portions might be copyrighted by other authors who have contributed
 * to OpenClonk.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 * See isc_license.txt for full license and disclaimer.
 *
 * "Clonk" is a registered trademark of Matthes Bender.
 * See clonk_trademark_license.txt for full license.
 */

/* Direct3D implementation of NewGfx */

#include "C4Include.h"
#include <C4DrawD3D.h>

#include <C4DrawD3DShader.h>

#include <C4App.h>
#include <C4Markup.h>
#include <C4Window.h>
#include <C4Config.h>

#ifdef USE_DIRECTX

#include <C4windowswrapper.h>
#include <stdio.h>
#include <math.h>
#include <limits.h>

CStdD3D::CStdD3D(bool fSoftware)
{
	this->fSoftware = fSoftware;
	Default();
	// global ptr
	pD3D = this;
}

CStdD3D::~CStdD3D()
{
	Clear();
	pD3D=NULL;
}

void CStdD3D::Default()
{
	C4Draw::Default();
	SceneOpen=false;
	lpD3D=NULL;
	lpDevice=NULL;
	pVB=pVBClr=pVBClrTex=NULL;
	ZeroMemory(&sfcBmpInfo, sizeof(sfcBmpInfo));
	bltState[0] = bltState[1] = bltState[2] = 0;
	bltBaseState[0] = bltBaseState[1] = bltBaseState[2] = bltBaseState[3] = 0;
	drawSolidState[0] = drawSolidState[1] = 0;
	pSavedState = 0;
	for (int i=0; i<SHIDX_Size; ++i) pShaders[i]=NULL;
}

void CStdD3D::Clear()
{
	NoPrimaryClipper();
	//if (pTexMgr) pTexMgr->IntUnlock(); // cannot do this here or we can't preserve textures across GL reinitialization as required when changing multisampling
	if (lpDevice)
	{
		EndScene();
		DeleteDeviceObjects();
	}
	if (lpD3D)
	{
		if (lpDevice) { lpDevice->Release(); lpDevice=NULL; }
		lpD3D->Release(); lpD3D=NULL;
	}
	SceneOpen=false;
	C4Draw::Clear();
}


/* Direct3D initialization */

bool CStdD3D::PageFlip(C4Rect *pSrcRt, C4Rect *pDstRt, C4Window * pWindow)
{
	RECT SrcRt, DstRt;
	if (pSrcRt)
	{
		SrcRt.left = pSrcRt->x;
		SrcRt.top = pSrcRt->y;
		SrcRt.right = pSrcRt->x + pSrcRt->Wdt;
		SrcRt.bottom = pSrcRt->y + pSrcRt->Hgt;
	}
	if (pDstRt)
	{
		DstRt.left = pDstRt->x;
		DstRt.top = pDstRt->y;
		DstRt.right = pDstRt->x + pDstRt->Wdt;
		DstRt.bottom = pDstRt->y + pDstRt->Hgt;
	}
	// call from gfx thread only!
	if (!pApp || !pApp->AssertMainThread()) return false;
	// safety
	if (!lpDevice) return false;
	// end the scene and present it
	EndScene();
	if (lpDevice->Present(pSrcRt ? &SrcRt : 0, pDstRt ? &DstRt : 0, pWindow ? pWindow->hWindow : 0, NULL) == D3DERR_DEVICELOST)
	{
		if (lpDevice->TestCooperativeLevel() == D3DERR_DEVICELOST) return false;
		if (!RestoreDeviceObjects()) return false;
		lpDevice->Present(NULL, NULL, NULL, NULL);
	}
	return true;
}


bool CStdD3D::BeginScene()
{
	// already open?
	if (SceneOpen) return true;
	// not active?
	if (!Active)
		if (!RestoreDeviceObjects())
			return false;
	// do open
	SceneOpen=(lpDevice->BeginScene() == D3D_OK);
	if (!SceneOpen) return false;
	// clear scene
	//lpDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(rand()%256,rand()%256,rand()%256), 1.0f, 0L );
	// set some def values
	// success
	return true;
}

void CStdD3D::FillBG(DWORD dwClr)
{
	if (lpDevice) lpDevice->Clear( 0, NULL, D3DCLEAR_TARGET, dwClr, 1.0f, 0L );
}

void CStdD3D::EndScene()
{
	// end scene, if open
	if (!SceneOpen) return;
	lpDevice->EndScene();
	SceneOpen=false;
}

bool CStdD3D::UpdateClipper()
{
	// no render target? do nothing
	if (!RenderTarget || !Active) return true;
	// negative/zero?
	int iWdt=Min(iClipX2, RenderTarget->Wdt-1)-iClipX1+1;
	int iHgt=Min(iClipY2, RenderTarget->Hgt-1)-iClipY1+1;
	int iX=iClipX1; if (iX<0) { iWdt+=iX; iX=0; }
	int iY=iClipY1; if (iY<0) { iHgt+=iY; iY=0; }
	if (iWdt<=0 || iHgt<=0)
	{
		ClipAll=true;
		return true;
	}
	ClipAll=false;
	// bound clipper to surface size
	D3DVIEWPORT9 Clipper;
	Clipper.X=iX; Clipper.Y=iY; Clipper.Width=iWdt; Clipper.Height=iHgt;
	Clipper.MinZ=0.0f; Clipper.MaxZ=1.0f;
	// set it
	lpDevice->SetViewport(&Clipper);
	return true;
}


bool CStdD3D::PrepareMaterial(StdMeshMaterial &mat)
{
	// TODO
	return false;
}

bool CStdD3D::PrepareRendering(C4Surface * sfcToSurface)
{
	// call from gfx thread only!
	if (!pApp || !pApp->AssertMainThread()) return false;
	// do not render to inactive fullscreen
	if (!Active && !Editor) return false;
	// device?
	if (!lpDevice) return false;
	// target?
	if (!sfcToSurface) return false;
	// target locked?
	if (sfcToSurface->Locked) return false;
	// target is already set as render target?
	if (sfcToSurface != RenderTarget)
	{
		// target is a render-target?
		if (!sfcToSurface->IsRenderTarget()) return false;
		IDirect3DSurface9 *pDest=sfcToSurface->GetSurface();
		if (!pDest) return false;
		// set target
		EndScene();
		if (lpDevice->SetRenderTarget(0, pDest) != D3D_OK)
		{
			pDest->Release();
			return false;
		}
		RenderTarget=sfcToSurface;
		pDest->Release();
		// new target might need new clipping
		UpdateClipper();
	}
	// start scene, if not already done
	BeginScene();
	// done
	return true;
}

void CStdD3D::PerformBlt(C4BltData &rBltData, C4TexRef *pTex, DWORD dwModClr, bool fMod2, bool fExact)
{
	if (!lpDevice || !pVB) return;

	// globally modulated blit
	int iAdditive = dwBlitMode & C4GFXBLIT_ADDITIVE;

	assert(rBltData.byNumVertices < sizeof(bltClrVertices)/sizeof(*bltClrVertices));
	// write to vertex buffer
	for (int i = 0; i < rBltData.byNumVertices; ++i)
	{
		bltClrVertices[i].x = rBltData.vtVtx[i].ftx;
		bltClrVertices[i].y = rBltData.vtVtx[i].fty;
		if (rBltData.pTransform)
			rBltData.pTransform->TransformPoint(bltClrVertices[i].x, bltClrVertices[i].y);
		// Compensate for D3D's half-a-pixel offset
		// FIXME: Put this into a global transformation matrix or something
		bltClrVertices[i].x -= 0.5;
		bltClrVertices[i].y -= 0.5;
		bltClrVertices[i].tu = rBltData.vtVtx[i].tx;
		bltClrVertices[i].tv = rBltData.vtVtx[i].ty;
		bltClrVertices[i].color = dwModClr;
	}

	bltBaseState[iAdditive + (fMod2 ? C4GFXBLIT_MOD2 : 0)]->Apply();
	lpDevice->SetFVF(D3DFVF_C4CTVERTEX);

	// color mod map by pixel shader?
	CStdD3DShader *pShader = NULL;
	if (fUseClrModMap && HasShaders())
	{
		bool fColoredFoW = pClrModMap->IsColored();
		pShader = pShaders[fMod2 * SHIDX_Mod2 + fColoredFoW * SHIDX_ColoredFoW];
		lpDevice->SetPixelShader(pShader->GetInterface());
		lpDevice->SetTexture(pShader->iInTexIndex, pTex->pTex);
		C4Surface * pModSurface = pClrModMap->GetSurface();
		if (pModSurface->ppTex) lpDevice->SetTexture(pShader->iFoWTexIndex, (*(pModSurface->ppTex))->pTex);
		const float mod_proj[4] = { 1.0f/(pClrModMap->GetResolutionX()* (*(pModSurface->ppTex))->iSizeX),
		                            1.0f/(pClrModMap->GetResolutionY()* (*(pModSurface->ppTex))->iSizeY),
		                            float(pClrModMap->OffX), float(pClrModMap->OffY)
		                          };
		lpDevice->SetPixelShaderConstantF(pShader->iFoWTransformIndex, mod_proj, 1);
		lpDevice->SetSamplerState( pShader->iFoWTexIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR  );
	}
	else
	{
		// set texture
		lpDevice->SetTexture(0, pTex->pTex);
	}
	// override linear filter mode for exact blits
	if (fExact)
	{
		lpDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
		lpDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
	}
	// draw!
	lpDevice->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, rBltData.byNumVertices-2, bltClrVertices, sizeof(bltClrVertices[0]) );
	// done, cleanup
	if (pShader)
	{
		lpDevice->SetPixelShader(NULL);
		lpDevice->SetTexture(pShader->iInTexIndex, NULL);
		lpDevice->SetTexture(pShader->iFoWTexIndex, NULL);
	}
}

void CStdD3D::PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt, DWORD dwPlayerColor, C4BltTransform *pTransform)
{
	// TODO: Implement this
	for (int x = 0; x < twdt; x += 10)
		for (int y = 0; y < thgt; y += 10)
			this->DrawBoxDw(0, tx+x, ty+y, tx+Min(twdt, x+10.0f), ty+Min(thgt, y+10.0f),
			                (((x+y)/10)&1)?0xff00ff:0x00ff00);
}

// get bit depth from surface format
unsigned int Format2BitDepth(D3DFORMAT format)
{
	switch (format)
	{
	case D3DFMT_X1R5G5B5:
	case D3DFMT_R5G6B5:
		return 16;

	case D3DFMT_X8R8G8B8:
	case D3DFMT_A8R8G8B8:
		return 32;

	default:
		// unknown
		return 0;
	}
}

bool CStdD3D::BlitTex2Window(C4TexRef *pTexRef, HDC hdcTarget, RECT &rtFrom, RECT &rtTo)
{
	// lock
	if (!pTexRef->Lock()) return false;
	// get bits
	BYTE *pBits = (BYTE *) pTexRef->texLock.pBits;
	if (!pBits) return false;
	// get size
	int fWdt = rtFrom.right-rtFrom.left;
	int fHgt = rtFrom.bottom-rtFrom.top;
	int tWdt = rtTo.right-rtTo.left;
	int tHgt = rtTo.bottom-rtTo.top;
	// adjust bitmap info
	sfcBmpInfo.bmiHeader.biHeight = pTexRef->iSizeY; sfcBmpInfo.bmiHeader.biWidth = pTexRef->iSizeX;
	// Same size
	if ((fWdt==tWdt) && (fHgt==tHgt))
		return SetDIBitsToDevice(hdcTarget, rtTo.left, rtTo.top, fWdt, fHgt, rtFrom.left, rtFrom.top, 0, fWdt, pBits, &sfcBmpInfo, DIB_RGB_COLORS) > 0;
	// Stretch
	SetMapMode(hdcTarget,MM_TEXT);
	int i = StretchDIBits(hdcTarget, rtTo.left, rtTo.top, tWdt, tHgt, rtFrom.left, rtFrom.top, fWdt, fHgt, pBits, &sfcBmpInfo, DIB_RGB_COLORS, SRCCOPY);
	if (i == GDI_ERROR) return false;
	return true;
}

bool CStdD3D::BlitSurface2Window(C4Surface * sfcSource,
                                 int fX, int fY, int fWdt, int fHgt,
                                 HWND hWnd,
                                 int tX, int tY, int tWdt, int tHgt)
{
	bool fOkay = false;
	if (!sfcSource->Lock()) return false;
	HDC hdcTarget=GetDC(hWnd);
	if (hdcTarget)
	{
		// blit textures
		// blit with basesfc?
		bool fBaseSfc=false;
		if (sfcSource->pMainSfc) if (sfcSource->pMainSfc->ppTex) fBaseSfc=true;
		// calc stretch
		float scaleX=(float) tWdt/fWdt;
		float scaleY=(float) tHgt/fHgt;
		// get involved texture offsets
		int iTexSizeX=sfcSource->iTexSize;
		int iTexSizeY=sfcSource->iTexSize;
		int iTexX=fX/iTexSizeX;
		int iTexY=fY/iTexSizeY;
		int iTexX2=Min((fX+fWdt-1)/iTexSizeX +1, sfcSource->iTexX);
		int iTexY2=Min((fY+fHgt-1)/iTexSizeY +1, sfcSource->iTexY);
		C4TexRef **ppTex=sfcSource->ppTex+iTexY*sfcSource->iTexX+iTexX;
		// blit from all these textures
		C4TexRef **ppTexRow, *pBaseTex=NULL;
		for (int iY=iTexY; iY<iTexY2; ++iY)
		{
			ppTexRow=ppTex;
			for (int iX=iTexX; iX<iTexX2; ++iX)
			{
				// get current blitting offset in texture
				int iBlitX=sfcSource->iTexSize*iX;
				int iBlitY=sfcSource->iTexSize*iY;
				// get new texture source bounds
				RECT fTexBlt;
				fTexBlt.left  = Max(fX-iBlitX, 0);
				fTexBlt.top   = Max(fY-iBlitY, 0);
				fTexBlt.right = Min(fX+fWdt-iBlitX, iTexSizeX);
				fTexBlt.bottom= Min(fY+fHgt-iBlitY, iTexSizeY);
				// get new dest bounds
				RECT tTexBlt;
				tTexBlt.left  = (long) ((fTexBlt.left  +iBlitX-fX)*scaleX+tX);
				tTexBlt.top   = (long) ((fTexBlt.top   +iBlitY-fY)*scaleY+tY);
				tTexBlt.right = (long) ((fTexBlt.right +iBlitX-fX)*scaleX+tX);
				tTexBlt.bottom= (long) ((fTexBlt.bottom+iBlitY-fY)*scaleY+tY);
				// is there a base-surface to be blitted first?
				if (fBaseSfc)
				{
					// then get this surface as same offset as from other surface
					// assuming this is only valid as long as there's no texture management,
					// organizing partially used textures together!
					pBaseTex=*(sfcSource->pMainSfc->ppTex+(ppTex-sfcSource->ppTex));
					BlitTex2Window(pBaseTex, hdcTarget, fTexBlt, tTexBlt);
				}
				// blit this texture
				BlitTex2Window(*ppTex, hdcTarget, fTexBlt, tTexBlt);
				// next col
				++ppTex;
			}
			// next row
			ppTex=ppTexRow+sfcSource->iTexX;
		}
		// done, release DC
		ReleaseDC(hWnd,hdcTarget);
	}
	sfcSource->Unlock();
	return fOkay;
}

bool CStdD3D::FindDisplayMode(unsigned int iXRes, unsigned int iYRes, unsigned int iColorDepth, unsigned int iMonitor)
{
	if (iColorDepth == 32)
		return FindDisplayMode(iXRes, iYRes, D3DFMT_A8R8G8B8, iMonitor) ||
		       FindDisplayMode(iXRes, iYRes, D3DFMT_X8R8G8B8, iMonitor);
	else if (iColorDepth == 16)
		return FindDisplayMode(iXRes, iYRes, D3DFMT_R5G6B5, iMonitor) ||
		       FindDisplayMode(iXRes, iYRes, D3DFMT_A1R5G5B5, iMonitor);
	else
		return false;
}

bool CStdD3D::FindDisplayMode(unsigned int iXRes, unsigned int iYRes, D3DFORMAT format, unsigned int iMonitor)
{
	bool fFound=false;
	D3DDISPLAYMODE dmode;
	// enumerate modes
	int iNumModes=lpD3D->GetAdapterModeCount(iMonitor, format);
	for (int i=0; i<iNumModes; i++)
		if (lpD3D->EnumAdapterModes(iMonitor, format, i, &dmode) == D3D_OK)
		{
			// size is OK?
			if (dmode.Width==iXRes && dmode.Height==iYRes)
			{
				// compare with found one
				if (fFound)
					// try getting a mode that is close to 85Hz, rather than taking the one with highest refresh rate
					// (which may set absurd modes on some devices)
					if (Abs<int>(85-dmode.RefreshRate)>Abs<int>(85-dspMode.RefreshRate))
						// the previous one was better
						continue;
				// choose this one
				fFound=true;
				dspMode=dmode;
			}
		}
	return fFound;
}

bool CStdD3D::SetOutputAdapter(unsigned int iMonitor)
{
	// get associated monitor rect
	HMONITOR hMon = lpD3D->GetAdapterMonitor(iMonitor);
	if (!hMon) return Error("GetAdapterMonitor error");
	MONITORINFO nfo; ZeroMemory(&nfo, sizeof(nfo));
	nfo.cbSize = sizeof(MONITORINFO);
	if (!GetMonitorInfo(hMon, &nfo)) return Error("GetMonitorInfo error");
	return true;
}

bool CStdD3D::CreatePrimarySurfaces(bool Editor, unsigned int iXRes, unsigned int iYRes, int iColorDepth, unsigned int iMonitor)
{
	// FIXME: this needs to work with iXRes==iYRes==-1, which means "use the desktop resolution"
	DebugLog("Init DX");
	DebugLog("  Create Direct3D9...");
	if ((lpD3D=Direct3DCreate9(D3D_SDK_VERSION))==NULL) return Error("  Direct3DCreate9 failure.");
	// set monitor info (Monitor-var and target rect)
	DebugLog("  SetOutput adapter...");
	// FIXME: Do not do resolution changes here, SetVideoMode will take care of that
	if (!SetOutputAdapter(iMonitor))
		return Error("  Output adapter failure.");

	ZeroMemory( &d3dpp, sizeof(d3dpp) );
	// set needed surface type
	switch (iColorDepth)
	{
	case 16: dwSurfaceType=D3DFMT_A4R4G4B4; break;
	case 32: dwSurfaceType=D3DFMT_A8R8G8B8; break;
	}

	HRESULT hr;
	HWND hWindow = pApp->pWindow->hWindow;
	DebugLog("  Create Device...");
	if (!Editor)
	{
		// fullscreen mode
		// pick a display mode
		if (0)
		{
			// "Windowed fullscreen"
			if (lpD3D->GetAdapterDisplayMode(iMonitor, &dspMode) != D3D_OK)
				if (lpD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dspMode) != D3D_OK)
					return Error("Could not get current display mode");
			SetWindowPos(hWindow, HWND_TOP, 0, 0, iXRes,iYRes, SWP_NOOWNERZORDER | SWP_SHOWWINDOW | SWP_NOMOVE);
			d3dpp.Windowed = true;
		}
		else
		{
			// true fullscreen
			if (!FindDisplayMode(iXRes, iYRes, iColorDepth, iMonitor))
				return Error("Display mode not supported");
		}
		// fill present structure
		d3dpp.BackBufferWidth = iXRes;
		d3dpp.BackBufferHeight= iYRes;
		d3dpp.BackBufferFormat= dspMode.Format;
		d3dpp.BackBufferCount = 0;
		d3dpp.SwapEffect      = D3DSWAPEFFECT_FLIP;
		d3dpp.Flags           = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
		hr = lpD3D->CreateDevice(iMonitor, fSoftware ? D3DDEVTYPE_REF : D3DDEVTYPE_HAL, hWindow,
		                         D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE,
		                         &d3dpp, &lpDevice);
	}
	else
	{
		// windowed mode
		// get current display mode
		if (lpD3D->GetAdapterDisplayMode(iMonitor, &dspMode) != D3D_OK)
			if (lpD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dspMode) != D3D_OK)
				return Error("Could not get current display mode");
		d3dpp.Windowed = true;
		d3dpp.BackBufferCount = 1;
		d3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
		d3dpp.BackBufferWidth = dspMode.Width;
		d3dpp.BackBufferHeight= dspMode.Height;
		d3dpp.BackBufferFormat = dspMode.Format;
		d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
		// create primary surface
		hr = lpD3D->CreateDevice(iMonitor, fSoftware ? D3DDEVTYPE_REF : D3DDEVTYPE_HAL, hWindow,
		                         D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE,
		                         &d3dpp, &lpDevice);
		// windowed mode: Try dfault adapter as well
		if (hr != D3D_OK && iMonitor != D3DADAPTER_DEFAULT)
			hr = lpD3D->CreateDevice(D3DADAPTER_DEFAULT, fSoftware ? D3DDEVTYPE_REF : D3DDEVTYPE_HAL, hWindow,
			                         D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE,
			                         &d3dpp, &lpDevice);
	}
	switch (hr)
	{
	case D3DERR_INVALIDCALL: return Error("CreateDevice: D3DERR_INVALIDCALL");
	case D3DERR_NOTAVAILABLE: return Error("CreateDevice: D3DERR_NOTAVAILABLE");
	case D3DERR_OUTOFVIDEOMEMORY: return Error("CreateDevice: D3DERR_OUTOFVIDEOMEMORY");
	case D3DERR_DRIVERINTERNALERROR: return Error("CreateDevice: D3DERR_DRIVERINTERNALERROR");
	case D3D_OK: break;
	default: return Error("CreateDevice: unknown error");
	}

	// device successfully created?
	if (!lpDevice) return Error("CreateDevice: unreported error");
	// store color depth
	byByteCnt=iColorDepth/8;
	PrimarySrfcFormat=d3dpp.BackBufferFormat;
	// create bmi-header
	ZeroMemory(&sfcBmpInfo, sizeof(sfcBmpInfo));
	sfcBmpInfo.bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);
	sfcBmpInfo.bmiHeader.biWidth        = iXRes;
	sfcBmpInfo.bmiHeader.biHeight       = -int(iYRes);
	sfcBmpInfo.bmiHeader.biPlanes       = 1;
	sfcBmpInfo.bmiHeader.biBitCount     = Format2BitDepth(dspMode.Format);
	sfcBmpInfo.bmiHeader.biCompression  = BI_RGB;
	// initialize device-dependent objects
	if (!InitDeviceObjects())
	{
		// cleanup
		DeleteDeviceObjects();
		// failure
		return false;
	}
	// update monitor rect by new screen size
	if (!SetOutputAdapter(iMonitor)) return false;
	// success!
	return true;
}

bool CStdD3D::OnResolutionChanged(unsigned int iXRes, unsigned int iYRes)
{
	assert(lpDevice);
	// NOTE: This should be replaced by using a desktop-size video buffer and
	// just clipping to the current resolution to improve performance
	d3dpp.BackBufferWidth = iXRes;
	d3dpp.BackBufferHeight = iYRes;
	DeleteDeviceObjects();
	HRESULT hr = lpDevice->Reset(&d3dpp);
	InitDeviceObjects();
	RestoreDeviceObjects();
	return hr == D3D_OK;
}

bool CStdD3D::SetVideoMode(unsigned int iXRes, unsigned int iYRes, unsigned int iColorDepthFIXME, unsigned int iMonitor, bool fFullScreen)
{
	// note that this should happen in fullscreen mode only!
	// do not mess with real presetn parameters until resolution change worked
	D3DPRESENT_PARAMETERS d3dpp;
	// reinit window for new resolution
	int iColorDepth = byByteCnt*8;
	ZeroMemory( &d3dpp, sizeof(d3dpp) );
	HRESULT hr;
	HWND hWindow = pApp->pWindow->hWindow;
	// pick a display mode
	if (!fFullScreen)
	{
		// "Windowed fullscreen"
		if (lpD3D->GetAdapterDisplayMode(iMonitor, &dspMode) != D3D_OK)
			if (lpD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dspMode) != D3D_OK)
				return Error("Could not get current display mode");
		SetWindowPos(hWindow, HWND_TOP, 0, 0, iXRes, iYRes, SWP_NOOWNERZORDER | SWP_SHOWWINDOW | SWP_NOMOVE);
		d3dpp.Windowed = true;
	}
	else
	{
		// true fullscreen
		if (!FindDisplayMode(iXRes, iYRes, iColorDepth, iMonitor))
			return Error("Display mode not supported");
	}
	// fill present structure
	d3dpp.BackBufferWidth = iXRes;
	d3dpp.BackBufferHeight= iYRes;
	d3dpp.BackBufferFormat= dspMode.Format;
	d3dpp.BackBufferCount = 0;
	d3dpp.SwapEffect      = D3DSWAPEFFECT_FLIP;
	d3dpp.Flags           = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
	// reset device to these parameters
	DeleteDeviceObjects();
	hr = lpDevice->Reset(&d3dpp);
	if (hr != D3D_OK)
	{
		// reset failed
		if (InitDeviceObjects())
			RestoreDeviceObjects(); // try restore at old resolution
		switch (hr)
		{
		case D3DERR_INVALIDCALL: return Error("    CreateDevice: D3DERR_INVALIDCALL");
		case D3DERR_NOTAVAILABLE: return Error("    CreateDevice: D3DERR_NOTAVAILABLE");
		case D3DERR_OUTOFVIDEOMEMORY: return Error("    CreateDevice: D3DERR_OUTOFVIDEOMEMORY");
		case D3DERR_DRIVERINTERNALERROR: return Error("    CreateDevice: D3DERR_DRIVERINTERNALERROR");
		default: return Error("    CreateDevice: unknown error");
		}
	}
	// device successfully reset! Reflect changes
	this->d3dpp = d3dpp;
	// store color depth
	byByteCnt=iColorDepth/8;
	PrimarySrfcFormat=d3dpp.BackBufferFormat;
	// update bmi-header
	sfcBmpInfo.bmiHeader.biWidth        = iXRes;
	sfcBmpInfo.bmiHeader.biHeight       = -int(iYRes);
	sfcBmpInfo.bmiHeader.biBitCount     = Format2BitDepth(dspMode.Format);
	// reinit device-dependant objects
	InitDeviceObjects();
	RestoreDeviceObjects();
	// update monitor rect by new screen size
	if (!SetOutputAdapter(iMonitor)) return false;
	// success!
	return true;
}

void CStdD3D::PerformPix(C4Surface * sfcDest, float tx, float ty, DWORD dwClr)
{
	// is render target?
	if (!sfcDest->IsRenderTarget())
		// on non-rendertargets, just set using locks
	{
		sfcDest->SetPixDw((int)tx, (int)ty, dwClr);
		return;
	}
	else if (sfcDest->IsLocked())
	{
		DrawPixPrimary(sfcDest, int(tx), int(ty), dwClr);
		return;
	}
	// on a render target, blit as a box (ugh!)
	// set vertex buffer data
	// vertex order:
	// 0=upper left   dwClr1
	// 1=lower left   dwClr3
	// 2=lower right  dwClr4
	// 3=upper right  dwClr2
	float vtx[8];
	vtx[0] = tx     ; vtx[1] = ty;
	vtx[2] = tx     ; vtx[3] = ty+1.0f;
	vtx[4] = tx+1.0f; vtx[5] = ty+1.0f;
	vtx[6] = tx+1.0f; vtx[7] = ty;
	DrawQuadDw(sfcDest, vtx, dwClr, dwClr, dwClr, dwClr);
}

void CStdD3D::DrawPixPrimary(C4Surface * sfcDest, int iX, int iY, DWORD dwClr)
{
	// Must be render target and locked
	if (!sfcDest->IsRenderTarget() || !sfcDest->IsLocked()) return;
	// set according to pixel format
	BYTE *pBits = sfcDest->PrimarySurfaceLockBits;
	int iPitch = sfcDest->PrimarySurfaceLockPitch;
	DWORD *pPix32; WORD *pPix16;
	switch (sfcDest->dwClrFormat)
	{
	case D3DFMT_X1R5G5B5:
		// 16 bit 5-5-5
		pPix16=(WORD *) (pBits+iY*iPitch+iX*2);
		*pPix16=WORD((dwClr & 0x000000f8) >> 3)
		        | WORD((dwClr & 0x0000f800) >> 6)
		        | WORD((dwClr & 0x00f80000) >> 9);
		break;

	case D3DFMT_R5G6B5:
		// 16 bit 5-6-5
		pPix16=(WORD *) (pBits+iY*iPitch+iX*2);
		*pPix16=WORD((dwClr & 0x000000f8) >> 3)
		        | WORD((dwClr & 0x0000fc00) >> 5)
		        | WORD((dwClr & 0x00f80000) >> 8);
		break;

	case D3DFMT_X8R8G8B8:
		// 32 bit
		pPix32=(DWORD *) (pBits+iY*iPitch+iX*4);
		BltAlpha(*pPix32, dwClr);
		break;
	default: assert(false); break; // should not happen
	}
}

void CStdD3D::DrawQuadDw(C4Surface * sfcTarget, float *ipVtx, DWORD dwClr1, DWORD dwClr2, DWORD dwClr3, DWORD dwClr4)
{
	// prepare rendering to target
	if (!PrepareRendering(sfcTarget)) return;
	// set blitting state
	int iAdditive = dwBlitMode & C4GFXBLIT_ADDITIVE;
	drawSolidState[iAdditive]->Apply();
	lpDevice->SetRenderState( D3DRS_DESTBLEND,  iAdditive ? D3DBLEND_ONE : D3DBLEND_INVSRCALPHA );
	lpDevice->SetStreamSource(0, pVBClr, 0, sizeof(C4CLRVERTEX));
	lpDevice->SetFVF(D3DFVF_C4CLRVERTEX);
	// 2do: manual clipping?
	// set vertex buffer data
	float fX1 = (float) ipVtx[0] - 0.5f;
	float fY1 = (float) ipVtx[1] - 0.5f;
	float fX2 = (float) ipVtx[2] - 0.5f;
	float fY2 = (float) ipVtx[3] - 0.5f;
	float fX3 = (float) ipVtx[4] - 0.5f;
	float fY3 = (float) ipVtx[5] - 0.5f;
	float fX4 = (float) ipVtx[6] - 0.5f;
	float fY4 = (float) ipVtx[7] - 0.5f;
	// apply color modulation
	if (BlitModulated)
	{
		ModulateClr(dwClr1, BlitModulateClr);
		ModulateClr(dwClr2, BlitModulateClr);
		ModulateClr(dwClr3, BlitModulateClr);
		ModulateClr(dwClr4, BlitModulateClr);
	}
	// set vertices
	clrVertices[0].x = fX1; clrVertices[0].y = fY1; clrVertices[0].color=dwClr1;
	clrVertices[1].x = fX2; clrVertices[1].y = fY2; clrVertices[1].color=dwClr2;
	clrVertices[2].x = fX3; clrVertices[2].y = fY3; clrVertices[2].color=dwClr3;
	clrVertices[3].x = fX3; clrVertices[3].y = fY3; clrVertices[3].color=dwClr3;
	clrVertices[4].x = fX4; clrVertices[4].y = fY4; clrVertices[4].color=dwClr4;
	clrVertices[5].x = fX1; clrVertices[5].y = fY1; clrVertices[5].color=dwClr1;
	if (fUseClrModMap)
		for (int i = 0; i < 6; ++i) ModulateClr(clrVertices[i].color, pClrModMap->GetModAt((int)clrVertices[i].x, (int)clrVertices[i].y));
	// copy into vertex buffer
	VOID* pVertices;
	if ( pVBClr->Lock(0, sizeof(clrVertices), &pVertices, 0) != D3D_OK ) return;
	memcpy(pVertices, clrVertices, sizeof(clrVertices));
	pVBClr->Unlock();
	// draw
	lpDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 2 );
}

void CStdD3D::PerformLine(C4Surface * sfcTarget, float x1, float y1, float x2, float y2, DWORD dwClr, float width)
{
	// FIXME: zoom width
	//dwClr |= 0xf0000000;
	// render target?
	if (sfcTarget->IsRenderTarget())
		if (!sfcTarget->IsLocked())
		{
			// draw as primitive
			if (!PrepareRendering(sfcTarget)) return;
			// set blitting state - use src alpha channel als opacity because some systems cannot antialias otherwise
			int iAdditive = dwBlitMode & C4GFXBLIT_ADDITIVE;
			drawSolidState[iAdditive]->Apply();
			lpDevice->SetRenderState( D3DRS_DESTBLEND,  iAdditive ? D3DBLEND_ONE : D3DBLEND_INVSRCALPHA );
			lpDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
			lpDevice->SetRenderState( D3DRS_ANTIALIASEDLINEENABLE, true );
			lpDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, true );
			lpDevice->SetStreamSource(0, pVBClr, 0, sizeof(C4CLRVERTEX));
			lpDevice->SetFVF(D3DFVF_C4CLRVERTEX);
			// set vertex buffer data
			float fX1 = (float) x1;// - 0.5f;
			float fY1 = (float) y1;// - 0.5f;
			float fX2 = (float) x2;// + 0.5f;
			float fY2 = (float) y2;// + 0.5f;
			clrVertices[0].x = fX1; clrVertices[0].y = fY1; clrVertices[0].color=dwClr;
			clrVertices[1].x = fX2; clrVertices[1].y = fY2; clrVertices[1].color=dwClr;
			clrVertices[2].x = fX2; clrVertices[2].y = fY2; clrVertices[2].color=dwClr;
			clrVertices[3].x = fX1; clrVertices[3].y = fY1; clrVertices[3].color=dwClr;
			if (fUseClrModMap)
				for (int i = 0; i < 4; ++i) ModulateClr(clrVertices[i].color, pClrModMap->GetModAt((int)clrVertices[i].x, (int)clrVertices[i].y));
			// copy into vertex buffer
			VOID* pVertices;
			if ( pVBClr->Lock(0, sizeof(clrVertices[0])*4, &pVertices, 0) != D3D_OK ) return;
			memcpy(pVertices, clrVertices, sizeof(clrVertices[0])*4);
			pVBClr->Unlock();
			// draw - actually two lines, to ensure that the last pixels are drawn, too...
			lpDevice->DrawPrimitive(D3DPT_LINELIST, 0, 2 );
		}
		else
		{
			if (fUseClrModMap)
				ModulateClr(dwClr, pClrModMap->GetModAt((int)((x1+x2)/2), (int)((y1+y2)/2)));
			if (Abs(x1 - x2) > Abs(y1 - y2))
			{
				// flip line direction
				if (x2 < x1)
				{
					float tmp = x2; x2 = x1; x1 = tmp;
					tmp = y2; y2 = y1; y1 = tmp;
				}
				// round coordinates
				int32_t iX1 = BoundBy<int32_t>(int32_t(x1+.5f),0,sfcTarget->Wdt-1),
				              iX2 = BoundBy<int32_t>(int32_t(x2+.5f),0,sfcTarget->Wdt-1),
				                    iY1 = BoundBy<int32_t>(int32_t(y1+.5f),0,sfcTarget->Hgt-1),
				                          iY2 = BoundBy<int32_t>(int32_t(y2+.5f),0,sfcTarget->Hgt-1),
				                                iDX = iX2 - iX1;
				// single pixel case?
				if (!iDX) { DrawPixPrimary(sfcTarget, iX1, iY1, dwClr); return; }
				// calculate gradient
				uint32_t g = ((uint32_t(Abs(iY2 - iY1)) << 16) / uint32_t(iX2 - iX1)) << 16;
				// alpha divisor
				uint32_t alpha = dwClr >> 24;
				if (alpha == 0) return; // invisible line
				uint32_t div = (uint32_t(1 << 24) / alpha) << 8;
				DWORD dwClrBase = dwClr & 0x00FFFFFF;
				// current position
				uint32_t sp = 0;
				if (y2 > y1)
					for (int32_t iX = 0, iY = 0; ; iX++)
					{
						// draw pixels
						DWORD dwClr1 = dwClrBase + ((alpha + sp / div) << 24),
						               dwClr2 = dwClrBase + ((255 - sp / div) << 24);
						DrawPixPrimary(sfcTarget, iX1+iX, iY1+iY,   dwClr1);
						if (sp) DrawPixPrimary(sfcTarget, iX1+iX, iY1+iY+1, dwClr2);
						if (iX * 2 >= iDX) break;
						DrawPixPrimary(sfcTarget, iX2-iX, iY2-iY,   dwClr1);
						if (sp) DrawPixPrimary(sfcTarget, iX2-iX, iY2-iY-1, dwClr2);
						if (iX * 2 + 1 >= iDX) break;
						// next pixel
						sp += g; if (sp < g) iY++;
					}
				else
					for (int32_t iX = 0, iY = 0; ; iX++)
					{
						// draw pixels
						DWORD dwClr1 = dwClrBase + ((alpha + sp / div) << 24),
						               dwClr2 = dwClrBase + ((255 - sp / div) << 24);
						DrawPixPrimary(sfcTarget, iX1+iX, iY1+iY,   dwClr1);
						if (sp) DrawPixPrimary(sfcTarget, iX1+iX, iY1+iY-1, dwClr2);
						if (iX * 2 >= iDX) break;
						DrawPixPrimary(sfcTarget, iX2-iX, iY2-iY,   dwClr1);
						if (sp) DrawPixPrimary(sfcTarget, iX2-iX, iY2-iY+1, dwClr2);
						if (iX * 2 + 1 >= iDX) break;
						// next pixel
						sp += g; if (sp < g) iY--;
					}
			}
			else
			{
				// flip line direction
				if (y2 < y1)
				{
					float tmp = y2; y2 = y1; y1 = tmp;
					tmp = x2; x2 = x1; x1 = tmp;
				}
				// calculate gradient
				uint32_t g = uint32_t( Abs(x2 - x1) / (y2 - y1) * 4294967296.0f );
				// round coordinates
				int32_t iX1 = BoundBy<int32_t>(int32_t(x1+.5f),0,sfcTarget->Wdt-1),
				              iX2 = BoundBy<int32_t>(int32_t(x2+.5f),0,sfcTarget->Wdt-1),
				                    iY1 = BoundBy<int32_t>(int32_t(y1+.5f),0,sfcTarget->Hgt-1),
				                          iY2 = BoundBy<int32_t>(int32_t(y2+.5f),0,sfcTarget->Hgt-1),
				                                iDY = iY2 - iY1;
				// single pixel case?
				if (!iDY) { DrawPixPrimary(sfcTarget, iX1, iY1, dwClr); return; }
				// alpha divisor
				uint32_t alpha = dwClr >> 24;
				if (alpha == 0) return; // invisible line
				uint32_t div = (uint32_t(1 << 24) / alpha) << 8;
				DWORD dwClrBase = dwClr & 0x00FFFFFF;
				// current position
				uint32_t sp = 0;
				if (x2 > x1)
					for (int32_t iY = 0, iX = 0; ; iY++)
					{
						// draw pixels
						DWORD dwClr1 = dwClrBase + ((alpha + sp / div) << 24),
						               dwClr2 = dwClrBase + ((255 - sp / div) << 24);
						DrawPixPrimary(sfcTarget, iX1+iX,   iY1+iY, dwClr1);
						if (sp) DrawPixPrimary(sfcTarget, iX1+iX+1, iY1+iY, dwClr2);
						if (iY * 2 >= iDY) break;
						DrawPixPrimary(sfcTarget, iX2-iX,   iY2-iY, dwClr1);
						if (sp) DrawPixPrimary(sfcTarget, iX2-iX-1, iY2-iY, dwClr2);
						if (iY * 2 + 1 >= iDY) break;
						// next pixel
						sp += g; if (sp < g) iX++;
					}
				else
					for (int32_t iY = 0, iX = 0; ; iY++)
					{
						// draw pixels
						DWORD dwClr1 = dwClrBase + ((alpha + sp / div) << 24),
						               dwClr2 = dwClrBase + ((255 - sp / div) << 24);
						DrawPixPrimary(sfcTarget, iX1+iX,   iY1+iY, dwClr1);
						if (sp) DrawPixPrimary(sfcTarget, iX1+iX-1, iY1+iY, dwClr2);
						if (iY * 2 >= iDY) break;
						DrawPixPrimary(sfcTarget, iX2-iX,   iY2-iY, dwClr1);
						if (sp) DrawPixPrimary(sfcTarget, iX2-iX+1, iY2-iY, dwClr2);
						if (iY * 2 + 1 >= iDY) break;
						// next pixel
						sp += g; if (sp < g) iX--;
					}
			}
		}
	else /* if(!sfcTarget->IsRenderTarget()) */
	{
		if (!LockSurfaceGlobal(sfcTarget)) return;
		ForLine((int)x1,(int)y1,(int)x2,(int)y2,&DLineSPixDw,dwClr);
		UnLockSurfaceGlobal(sfcTarget);
	}
}

bool CStdD3D::InitDeviceObjects()
{
	D3DCAPS9 d3dCaps;
	pD3D->lpDevice->GetDeviceCaps(&d3dCaps);
	MaxTexSize = d3dCaps.MaxTextureWidth;
	bool fSuccess=true;
	// Create shaders
	for (int i=0; i<SHIDX_Size; ++i) if (pShaders[i]) { delete pShaders[i]; pShaders[i]=NULL; }
	if (Config.Graphics.EnableShaders)
	{
		if (!InitShaders())
		{
			Error("    Shader initizlization failed - reverting to no-shader-mode.");
		}
	}
	// restore everything
	return fSuccess && RestoreDeviceObjects();
}

bool CStdD3D::InitShaders()
{
	// safety?
	if (!lpDevice) return false;
	return false;
	// create all desired shaders. Fail and delete them all if any one fails.
	for (int i=0; i<SHIDX_Size; ++i) pShaders[i] = new CStdD3DShader();
	bool fOK = true;
	for (int i=0; i<SHIDX_Size; ++i)
	{
		if (!pShaders[i]->Init(lpDevice, !!(i&SHIDX_Mod2), !!(i&SHIDX_ColoredFoW))) { fOK = false; break; }
	}
	if (!fOK) for (int i=0; i<SHIDX_Size; ++i) if (pShaders[i]) { delete pShaders[i]; pShaders[i]=NULL; }
	return fOK;
}

bool CStdD3D::RestoreDeviceObjects()
{
	// any device?
	if (!lpDevice) return false;
	// delete any previous objects
	InvalidateDeviceObjects();
	// if the device is in a lost state: reset it
	if (lpDevice->TestCooperativeLevel() == D3DERR_DEVICENOTRESET) lpDevice->Reset(&d3dpp);
	// couldn't reset?
	if (lpDevice->TestCooperativeLevel() != D3D_OK) return false;
	bool fSuccess=true;
	// restore primary/back
	/*  IDirect3DSurface9 *pPrimarySfc;
	  if (lpDevice->GetRenderTarget(0, &pPrimarySfc) != D3D_OK)
	    { Error("Could not get primary surface"); fSuccess=false; }
	  RenderTarget=lpPrimary;
	  lpPrimary->AttachSfc(pPrimarySfc,0,0);
	  lpPrimary->dwClrFormat=PrimarySrfcFormat;
	  lpPrimary->byBytesPP=Format2BitDepth(PrimarySrfcFormat)/8;*/
	// create vertex buffer
	if ( lpDevice->CreateVertexBuffer(sizeof(bltVertices), 0, D3DFVF_C4VERTEX, D3DPOOL_DEFAULT, &pVB, NULL) != D3D_OK ) return false;
	// fill initial data for vertex buffer
	int i;
	for (i=0; i<6; ++i)
	{
		bltVertices[i].z=0.9f;
		bltVertices[i].rhw=1.0f;
	}
	// create solid color vertex buffer
	if ( lpDevice->CreateVertexBuffer(sizeof(clrVertices), 0, D3DFVF_C4CLRVERTEX, D3DPOOL_DEFAULT, &pVBClr, NULL) != D3D_OK ) return false;
	// fill initial data for vertex buffer
	for (i=0; i<6; ++i)
	{
		clrVertices[i].z=0.9f;
		clrVertices[i].rhw=1.0f;
	}
	// create color-texblit vertices
	if ( lpDevice->CreateVertexBuffer(sizeof(bltClrVertices), 0, D3DFVF_C4CTVERTEX, D3DPOOL_DEFAULT, &pVBClrTex, NULL) != D3D_OK ) return false;
	// fill initial data for vertex buffer
	for (i=0; i<6; ++i)
	{
		bltClrVertices[i].z=0.9f;
		bltClrVertices[i].rhw=1.0f;
	}
	// create state blocks
	CreateStateBlock(&bltState[0], false, false, false, false, false);
	CreateStateBlock(&bltState[1], true, false, false, false, false);
	CreateStateBlock(&bltState[2], true, false, false, true, false);
	CreateStateBlock(&drawSolidState[0], false, false, false, false, false);
	CreateStateBlock(&drawSolidState[1], false, false, false, true, false);
	CreateStateBlock(&bltBaseState[0], true, false, true, false, false);
	CreateStateBlock(&bltBaseState[1], true, false, true, true, false);
	CreateStateBlock(&bltBaseState[2], true, false, true, false, true);
	CreateStateBlock(&bltBaseState[3], true, false, true, true, true);
	// activate if successful
	Active=fSuccess;
	// restore gamma if active
	if (Active) EnableGamma();
	// reset blit states
	dwBlitMode = 0;
	// done
	return Active;
}

template<class IF> static inline void SafeRelease(IF *&pInterface)
{
	if (pInterface)
		pInterface->Release();
	pInterface = 0;
}

bool CStdD3D::InvalidateDeviceObjects()
{
	bool fSuccess=true;
	// clear gamma
	DisableGamma();
	// deactivate
	Active=false;
	// release state blocks
	SafeRelease(bltState[0]);
	SafeRelease(bltState[1]);
	SafeRelease(bltState[2]);
	SafeRelease(drawSolidState[0]);
	SafeRelease(bltBaseState[0]);
	SafeRelease(bltBaseState[1]);
	SafeRelease(drawSolidState[1]);
	SafeRelease(bltBaseState[2]);
	SafeRelease(bltBaseState[3]);
	SafeRelease(pSavedState);
	// release vertex buffer
	SafeRelease(pVBClr);
	SafeRelease(pVB);
	SafeRelease(pVBClrTex);
	return fSuccess;
}

bool CStdD3D::DeleteDeviceObjects()
{
	InvalidateDeviceObjects();
	bool fSuccess=true;
	NoPrimaryClipper();
	// del main surfaces
	RenderTarget=NULL;
	for (int i=0; i<SHIDX_Size; ++i) if (pShaders[i]) { delete pShaders[i]; pShaders[i]=NULL; }
	return fSuccess;
}

bool CStdD3D::CreateStateBlock(IDirect3DStateBlock9 **pBlock, bool fTransparent, bool fSolid, bool fBaseTex, bool fAdditive, bool fMod2)
{
	// begin capturing
	lpDevice->BeginStateBlock();
	// set states
	lpDevice->SetRenderState( D3DRS_LIGHTING, false );
	lpDevice->SetRenderState( D3DRS_MULTISAMPLEANTIALIAS, false ); // no antialiasing
	lpDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, true );
	lpDevice->SetRenderState( D3DRS_SRCBLEND,   D3DBLEND_SRCALPHA );
	lpDevice->SetRenderState( D3DRS_DESTBLEND,  fAdditive ? D3DBLEND_ONE : D3DBLEND_INVSRCALPHA );
	/*  // This isn't necessary, as a>=0 is equal to no alpha testing anyway
	  lpDevice->SetRenderState( D3DRS_ALPHATESTENABLE,  true );
	  lpDevice->SetRenderState( D3DRS_ALPHAREF,         0x00 );
	  lpDevice->SetRenderState( D3DRS_ALPHAFUNC,  D3DCMP_GREATEREQUAL );
	*/
	lpDevice->SetRenderState( D3DRS_FILLMODE,   D3DFILL_SOLID );
	lpDevice->SetRenderState( D3DRS_CULLMODE,   D3DCULL_NONE );
	lpDevice->SetRenderState( D3DRS_ZENABLE,          false );
	lpDevice->SetRenderState( D3DRS_STENCILENABLE,    false );
	lpDevice->SetRenderState( D3DRS_CLIPPING,         true );
	lpDevice->SetRenderState( D3DRS_ANTIALIASEDLINEENABLE,    false );
	lpDevice->SetRenderState( D3DRS_CLIPPLANEENABLE,  false );
	lpDevice->SetRenderState( D3DRS_VERTEXBLEND,      false );
	lpDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, false );
	lpDevice->SetRenderState( D3DRS_FOGENABLE,        false );
	if (!fBaseTex)
	{
		lpDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_SELECTARG1 );
		lpDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );
		lpDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, fSolid ? D3DTA_DIFFUSE : D3DTA_TEXTURE );
		lpDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, fSolid ? D3DTA_DIFFUSE : D3DTA_TEXTURE );
	}
	else
	{
		lpDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   fMod2 ? D3DTOP_ADDSIGNED2X : D3DTOP_MODULATE );
		lpDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
		lpDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
		lpDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
		lpDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
		lpDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
	}
	lpDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_DISABLE );
	lpDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_DISABLE );
	lpDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	lpDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
	lpDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE );
	lpDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 );
	lpDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
	lpDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU , D3DTADDRESS_CLAMP );
	lpDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV , D3DTADDRESS_CLAMP );
	// capture
	return lpDevice->EndStateBlock(pBlock) == D3D_OK;
}

void CStdD3D::SetTexture()
{
}

void CStdD3D::ResetTexture()
{
	if (Active) lpDevice->SetTexture(0, NULL);
}

bool CStdD3D::ApplyGammaRamp(D3DGAMMARAMP &ramp, bool fForce)
{
	if (!lpDevice || (!Active && !fForce)) return false;
	lpDevice->SetGammaRamp(0, D3DSGR_CALIBRATE, &ramp);
	return true;
}

bool CStdD3D::SaveDefaultGammaRamp(D3DGAMMARAMP &ramp)
{
	if (!lpDevice) return false;
	lpDevice->GetGammaRamp(0, &ramp);
	return true;
}

void CStdD3D::TaskOut()
{
	InvalidateDeviceObjects();
}

void CStdD3D::TaskIn()
{
	RestoreDeviceObjects();
}

CStdD3D *pD3D=NULL;

#endif // USE_DIRECTX

