
// BKOscWnd-D2D.cpp : файл реализации
//

#include "stdafx.h"
#include "BKOscWnd.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#endif


template<class Interface>
inline void
SafeRelease(
    Interface **ppInterfaceToRelease
)
{
	if (*ppInterfaceToRelease != NULL)
	{
		(*ppInterfaceToRelease)->Release();
		(*ppInterfaceToRelease) = NULL;
	}
}

// диалоговое окно CBKOscWnd

CBKOscWnd::CBKOscWnd()
	: m_cwndParent(NULL)
	, m_hwndScreen(NULL)  // Screen View window handle
	, m_pDirect2dFactory(NULL)
	, m_pRenderTarget(NULL)
	, m_pLightSlateGrayBrush(NULL)
	, m_pWhiteBrush(NULL)
	, m_pGreenBrush(NULL)
	, m_inBuf(NULL)
	, m_inLen(0)
{
}

CBKOscWnd::~CBKOscWnd()
{
}

BEGIN_MESSAGE_MAP(CBKOscWnd, CWnd)
	ON_WM_CREATE()
	ON_WM_DESTROY()
	ON_WM_SIZE()
	ON_WM_ERASEBKGND()
END_MESSAGE_MAP()


// обработчики сообщений CScreen
HRESULT CBKOscWnd::CreateFactory()
{
	return D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
}


HRESULT CBKOscWnd::ScreenView_Init()
{
	RECT rc; //размеры окна оконного режима
	::GetClientRect(m_hwndScreen, &rc);
	D2D1_SIZE_U size = D2D1::SizeU(
	                       rc.right - rc.left,
	                       rc.bottom - rc.top
	                   );
	m_rectWnd.left = 0.0f;
	m_rectWnd.top = 0.0f;
	m_rectWnd.right = (FLOAT)size.width;
	m_rectWnd.bottom = (FLOAT)size.height;
	// Create a Direct2D factory.
	HRESULT hr = CreateFactory();

	if (SUCCEEDED(hr))
	{
		D2D1_RENDER_TARGET_PROPERTIES rtprop = D2D1::RenderTargetProperties();
//      D2D1_PIXEL_FORMAT pixelFormat = D2D1::PixelFormat(
//                                          DXGI_FORMAT_B8G8R8A8_UNORM, //поддерживается только _UNORM
//                                          D2D1_ALPHA_MODE_IGNORE
//                                      );
//
//      rtprop.pixelFormat = pixelFormat;
//      m_pDirect2dFactory->GetDesktopDpi(&rtprop.dpiX, &rtprop.dpiY);
		//с D2D1_RENDER_TARGET_TYPE_HARDWARE работает только DXGI_FORMAT_R8G8B8A8
		//DXGI_FORMAT_B8G8R8A8 работает со всеми типами, _HARDWARE _SOFTWARE, _DEFAULT
		rtprop.type = D2D1_RENDER_TARGET_TYPE_HARDWARE;
		// Create a Direct2D render target.
		hr = m_pDirect2dFactory->CreateHwndRenderTarget(
		         rtprop,
		         D2D1::HwndRenderTargetProperties(m_hwndScreen, size),
		         &m_pRenderTarget
		     );

		if (FAILED(hr))
		{
			//если не удалось включить хардварное ускорение
			rtprop.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
			hr = m_pDirect2dFactory->CreateHwndRenderTarget(
			         rtprop,
			         D2D1::HwndRenderTargetProperties(m_hwndScreen, size),
			         &m_pRenderTarget
			     );
			//попробуем то, что предлагается по умолчанию.
		}

		if (SUCCEEDED(hr))
		{
			// Create a gray brush.
			hr = m_pRenderTarget->CreateSolidColorBrush(
			         D2D1::ColorF(D2D1::ColorF::LightSlateGray),
			         &m_pLightSlateGrayBrush
			     );
		}

		if (SUCCEEDED(hr))
		{
			// Create a blue brush.
			hr = m_pRenderTarget->CreateSolidColorBrush(
			         D2D1::ColorF(D2D1::ColorF::White),
			         &m_pWhiteBrush
			     );
		}

		if (SUCCEEDED(hr))
		{
			// Create a blue brush.
			hr = m_pRenderTarget->CreateSolidColorBrush(
			         D2D1::ColorF(D2D1::ColorF::Lime),
			         &m_pGreenBrush
			     );
		}
	}

	return hr;
}

void CBKOscWnd::ScreenView_Done()
{
	SafeRelease(&m_pDirect2dFactory);
	SafeRelease(&m_pRenderTarget);
	SafeRelease(&m_pLightSlateGrayBrush);
	SafeRelease(&m_pWhiteBrush);
	SafeRelease(&m_pGreenBrush);
}


BOOL CBKOscWnd::PreCreateWindow(CREATESTRUCT &cs)
{
	m_cwndParent = FromHandle(cs.hwndParent);
	cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
	return CWnd::PreCreateWindow(cs);
}

// обработчики сообщений CBKOscWnd

int CBKOscWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
	{
		return -1;
	}

	m_hwndScreen = GetSafeHwnd();

	if ((m_hwndScreen != NULL))
	{
		if (SUCCEEDED(ScreenView_Init()))
		{
			return 0;
		}
	}

	return TRUE;  // возврат значения TRUE, если фокус не передан элементу управления
}



void CBKOscWnd::OnDestroy()
{
	ScreenView_Done();
	CWnd::OnDestroy();
}

void CBKOscWnd::OnSize(UINT nType, int cx, int cy)
{
	CWnd::OnSize(nType, cx, cy);

	if (m_pRenderTarget)
	{
		// Note: This method can fail, but it's okay to ignore the
		// error here, because the error will be returned again
		// the next time EndDraw is called.
		m_pRenderTarget->Resize(D2D1::SizeU(cx, cy));
	}
}

BOOL CBKOscWnd::OnEraseBkgnd(CDC *pDC)
{
	return TRUE;
}

void CBKOscWnd::SetBuffer(BYTE *pBuffer, int buffer_len)
{
	m_inBuf = reinterpret_cast<SAMPLE_TYPE *>(pBuffer);
	m_inLen = buffer_len / SAMPLE_LENGTH; //длина буфера в сэмплах
}

void CBKOscWnd::display()
{
	if (m_lockBusy.IsLocked())
	{
		return;
	}

	m_lockBusy.Lock();
	m_pRenderTarget->BeginDraw();
	//очищаем экран
	m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::Black));
	D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize();
	FLOAT scalex = rtSize.width / (FLOAT)m_inLen;
	FLOAT half = rtSize.height / 2.0f;
	FLOAT scaley = half / FLOAT_BASE;
	//рисуем разделительную линию между каналами
	m_pRenderTarget->DrawLine(
	    D2D1::Point2F(0.0f, half),
	    D2D1::Point2F(rtSize.width, half),
	    m_pLightSlateGrayBrush
	    //,0.5f
	);
	D2D1_POINT_2F pntbgnL = D2D1::Point2F(0.0f, rtSize.height / 4.0f);
	D2D1_POINT_2F pntendL = D2D1::Point2F(rtSize.width, rtSize.height / 4.0f);
	D2D1_POINT_2F pntbgnR = D2D1::Point2F(0.0f, rtSize.height * 0.75f);
	D2D1_POINT_2F pntendR = D2D1::Point2F(rtSize.width, rtSize.height * 0.75f);
	//рисуем линии центров каналов.
	m_pRenderTarget->DrawLine(pntbgnL, pntendL, m_pWhiteBrush);
	m_pRenderTarget->DrawLine(pntbgnR, pntendR, m_pWhiteBrush);
	int j = 0;

	for (int i = 0; i < m_inLen; i++)
	{
		//рисуем левый канал -32768..32767 => 0 .. height/2
		pntendR.x = pntendL.x = (FLOAT)i * scalex;
		pntendL.y = half - ((FLOAT)m_inBuf[j++] + FLOAT_BASE_HALF) * scaley;
		m_pRenderTarget->DrawLine(pntbgnL, pntendL, m_pGreenBrush);
		pntbgnL = pntendL;
		//рисуем правый канал -32768..32767 => height/2 .. height
		pntendR.y = rtSize.height - ((FLOAT)m_inBuf[j++] + FLOAT_BASE_HALF) * scaley;
		m_pRenderTarget->DrawLine(pntbgnR, pntendR, m_pGreenBrush);
		pntbgnR = pntendR;
	}

	HRESULT hr = m_pRenderTarget->EndDraw();
//  if (hr == D2DERR_RECREATE_TARGET)
//  {
//      hr = S_OK;
//      //ScreenView_Done(); - а потом заново переделать ресурсы, кроме m_pDirect2dFactory
//  }
	m_lockBusy.UnLock();
}

