Original Post
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;
}