Skip to main content
gamedev.net
🔒 Locked

Question

Started by Nedelin 1 reply 1.1k views
Original Post
Nedelin
Nedelin
Halo, for the last few days I've been struggling with the windows GDI. I am a beginner in this sphere and I will appreciate it if you give me some clues on my task. This is what I am trying to do: I have a NIDAQ device (an analog to digital converter for USB). I am trying to display the signals, which I receive from it. So as a beginning I have constructed a simple window and use the whole one to display the signal. The signal is actually a 1-D array. At first I displayed the signal without any double buffering, but I got constant flicker. So I decided that I will need a double buffer. And here is where my knowledge is inconsistent. Are there any guidelines for me to follow? Do I need to turn the 1-D array to 2-D bitmap and if yes how should I do that? How do I double-buffer a 1-D array? What I try to do in my code now is turning the 1-D array to a bitmap. I have (I think I have actually) created a HDC and a memory replication of it, as well as a bitmap. I use function LineDDA and its callback LineDDAProc to draw the lines between the points and store them in the memory buffer. Then I use BitBlt to display the buffer. Is this a correct approach? Can you tell me where I might make mistake? Thank you in advance. Greetings, Nedelin. Here is my code. The comments are not written in English.

#include <windows.h>
#include "resource.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "NIDAQmx.h"

const char g_szClassName[] = "myWindowClass";
const int ID_TIMER1 = 1;//timer za pzpisvane na dannite
const int ID_TIMER2 = 2;//timer za generirane na danni ot funkciqta signal_generator

#define X 560//razmer na displeq po x
#define Y 280//razmer na displeq po y
#define MAX_RANGE 11.0

#define DAQ_len 500 //tova e dalginata na

TaskHandle taskHandle;//inicializaciq na DAQ

int daq_len = DAQ_len;//promenliva s dalginata na dannite polu4eni ot DAQs 

//masivite, v  koito se pazqt dannite ot DAQ trqbva da sa s promenliva dalgina
double signal_val[DAQ_len];//masiv s dannite na signala
double time_val[DAQ_len];//masiv s dalginata na vremeto
POINT s[X];//signal


//funkciq za promqna na razmera na masiva s danni. Sledqt se vhodnite danni i tqhnata dalgina.
//Ako displeqt ima po-malka dalgina ot vhodnite danni, izpisvam samo tazi 4ast ot dannite, koqto se pobira v nego.
//Ako displeqt ima po-golqma dalgina ot dannite, koito polu4avam, slepvam starite i novite danni.
void resize(RECT *size)
//void resize(double data, int x, int y)
{
	int i,len;
	double dif_y,dif_x,step;
	len=(sizeof(signal_val))/(sizeof(double));
	dif_x=(double)(size->right - size->left);//namiram obhvata na poleto po x
	dif_y=(double)(size->bottom - size->top);//namiram obhvata na poleto po y
	step=dif_x/(double)len;
	for(i=0;i<len;i++)
	{
		s.x=i;//poziciq po x
		s.y=(INT16)(((signal_val/MAX_RANGE)*(0.95*dif_y/2))+dif_y/2);
	}
}

//inicializirane na DAQ
UINT32 DAQ_init(void)
{
	#define DAQmxErrChk(functionCall) if( DAQmxFailed(error=(functionCall)) ) goto Error; else

	int32 error=0;
	TaskHandle taskHandle=0;
	char errBuff[2048]={'\0'};

	/*********************************************/
	// DAQmx Configure Code
	/*********************************************/
	DAQmxErrChk (DAQmxCreateTask("",&taskHandle));
	DAQmxErrChk (DAQmxCreateAIVoltageChan(taskHandle,"Dev1/ai0","",DAQmx_Val_Cfg_Default,-10.0,10.0,DAQmx_Val_Volts,NULL));
	DAQmxErrChk (DAQmxCfgSampClkTiming(taskHandle,"",10000.0,DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,daq_len));

Error:
	if( DAQmxFailed(error) )
	{
		DAQmxGetExtendedErrorInfo(errBuff,2048);
		return 0;
	}
	/*if( taskHandle!=0 )  {
		// DAQmx Stop Code
		DAQmxStopTask(taskHandle);
		DAQmxClearTask(taskHandle);
	}*/
	return taskHandle;
}


void CALLBACK LineDDAProc(int x, int y, LPARAM lpData)
{
	HDC in = (HDC) lpData;
	POINT cur;
	GetCurrentPositionEx(in, &cur);
	if(cur.x==X)
	{
		cur.x=0;//vra6tam se v na4aloto na koordinatnata sistema
		cur.y=0;
		MoveToEx(in, cur.x, cur.y, NULL);//premestvam se na poso4enata poziciq
	}
	else
	{
		cur.x=s[cur.x+1].x;//x koordinata na sledva6tata to4ka
		cur.y=s[cur.x+1].y;//y koordinata na sledva6tata to4ka
		MoveToEx(in, cur.x, cur.y, NULL);//premestvam se na poso4enata poziciq
		SetPixel(in,s[cur.x].x,s[cur.x].y,0xaa);//izpisvam piksela
		//LineTo(in,s[cur.x].x,s[cur.x].y);//izpisvam momentnata to4ka
	}
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	switch(msg)
	{
		case WM_CREATE:
		{
			UINT ret;
			ret = SetTimer(hwnd, ID_TIMER1, 50, NULL);//puskam timer
			if(ret == 0)
				MessageBox(hwnd, "Could not SetTimer()!", "Error", MB_OK | MB_ICONEXCLAMATION);
			ret = SetTimer(hwnd, ID_TIMER2, 2000, NULL);//puskam timer
			if(ret == 0)
				MessageBox(hwnd, "Could not SetTimer()!", "Error", MB_OK | MB_ICONEXCLAMATION);
		}
		break;

		case WM_CLOSE:
			DestroyWindow(hwnd);
		break;

		case WM_PAINT:
		{
			HDC hdc = BeginPaint(hwnd, &ps);
            EndPaint(hwnd, &ps);
		}
		break;

		case WM_TIMER:
		{
			HDC hdc = GetDC(hwnd); //hdc kam ekrana
			HDC buffer = CreateCompatibleDC(hdc); //hdc kam pametta
			HBITMAP BitmapBuf;

			RECT size;//stktura s razmerite na poleto
			GetClientRect(hwnd,&size);//vzimam razmerite na poleto

			BitmapBuf = CreateCompatibleBitmap(hdc,size.right-size.left,size.top-size.bottom);//bitmap bufer

			SelectObject(buffer, BitmapBuf);

			//pravq proverka ot koi timer idva saob6tenieto
			switch(wParam)
			{
			case 1:
				{//pro4itam dannite sabrani ot DAQ
					INT32 read=0;
					DAQmxReadAnalogF64(taskHandle,daq_len,10.0,DAQmx_Val_GroupByChannel,signal_val,daq_len,&read,NULL);
								
					resize(&size);//orazmerqvam dannite za da se poberat v ekrana

					LineDDA(size.left,size.top,size.right,size.bottom,LineDDAProc,(long)buffer);//izpisvam novite danni v pametta

					FillRect(hdc, &size, (HBRUSH) (COLOR_WINDOW+1));//iz4istvam sadarganieto na poleto, poplavm s bql cvqt

					read=BitBlt(hdc,0,0,size.right,size.bottom,buffer,0,0,SRCCOPY);//zapisvam elementite
				}
				break;

			case 2://novo iskane na danni
				DAQmxStartTask(taskHandle);//novo zadanie za danni
				break;
			}

			DeleteDC(buffer);
			DeleteObject(BitmapBuf);
			ReleaseDC(hwnd, hdc);

		}
		break;

		case WM_DESTROY:
			KillTimer(hwnd, ID_TIMER1);
			KillTimer(hwnd, ID_TIMER2);
			//SPIRANE NA DAQ//////////
			DAQmxStopTask(taskHandle);
			DAQmxClearTask(taskHandle);
			//////////////////////////	
			PostQuitMessage(0);
		break;

		default:
			return DefWindowProc(hwnd, msg, wParam, lParam);
	}
	return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX wc;
	HWND hwnd;
	MSG Msg;

	wc.cbSize		 = sizeof(WNDCLASSEX);
	wc.style		 = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc	 = WndProc;
	wc.cbClsExtra	 = 0;
	wc.cbWndExtra	 = 0;
	wc.hInstance	 = hInstance;
	wc.hIcon		 = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor		 = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = g_szClassName;
	wc.hIconSm		 = LoadIcon(NULL, IDI_APPLICATION);

	if(!RegisterClassEx(&wc))
	{
		MessageBox(NULL, "Window Registration Failed!", "Error!",
			MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}

	hwnd = CreateWindowEx(
		WS_EX_CLIENTEDGE,
		g_szClassName,
		"An Animation Program",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT, X, Y,
		NULL, NULL, hInstance, NULL);

	if(hwnd == NULL)
	{
		MessageBox(NULL, "Window Creation Failed!", "Error!",
			MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	//inicializirane na DAQ
	taskHandle=DAQ_init();
	if(0==taskHandle)
		MessageBox(NULL, "DAQ init FAILED!", "Error!",MB_ICONEXCLAMATION | MB_OK);

	while(GetMessage(&Msg, NULL, 0, 0) > 0)
	{	
		TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	}
	return Msg.wParam;
}

Nedelin
Nedelin
I forgot to mention, that when I run the code, I've sent, I can see nothing displayed on the window. I am connected to the NIDAQ and receive data from it, but it is never shown on the screen.
Buckeye
Buckeye
A better approach might be to create the HBITMAP as a global variable. After you draw the bitmap with the data, don't BitBlt to the window. Instead, call InvalidateRect(NULL) which will cause a WM_PAINT message to be sent to the window.

In the WM_PAINT portion of your procedure, bitblt the bitmap to the paint dc created. This method also has the advantage of always displaying the latest data, even if a portion of the window has been overwritten by another application.

The flickering is probably due to the background of the window being erased before the bitmap is drawn. If your bitmap is the size of the window, you don't need to erase the background as the bitmap will draw the background. Add a WM_ERASEBKGND case to your procedure and just return 0.
case WM_ERASEBKGND:   return 0;
Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly. You don't forget how to play when you grow old; you grow old when you forget how to play.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.