這問題實在看了很多人問,但大多是視窗程式還沒學過,只學到 Console 下之程式設計便問要怎麼畫波形圖。在 Console 下畫不是不能畫,可以說是「不習慣」,也可以說是「不好畫」。

這問題我用四種不同方法解過 -
(1) 直接用 Console Mode 上用 printf / cout 去畫,但這個很麻煩
(2) 調用 Console Function 在 Console Window 上畫
(3) 用 Win32 API 去畫
(4) 用 MFC 去畫

上述方法扣除掉第一項不予置評 (那畫出來實在是太醜了),Console Window 畫實在覺得沒必要,MFC 算久沒再碰了,這裡只用 Win32 慢慢刻。程式碼沒寫得很好,改善空間甚大。

 

#pragma comment(lib, "Winmm.lib") /* using API - PlaySound */
#include <windows.h>
#include <math.h>

// =============================================
// 繪圖函式 視窗位置與大小
#define WINDOW_X0 0
#define WINDOW_Y0 0
#define HEIGHT 300
#define WIDTH 400

// =============================================
// 繪圖相關資訊
#define RATE (10.0) /* 縮放比例*/
#define X0 WIDTH/2
#define Y0 HEIGHT/2
#define Xd (1E-2)
#define START_X (-50.0)

// =============================================
// 自定義 MyPoint struct,雖已有 struct POINT,但 POINT::X POINT::Y 為整數
typedef struct MyPointtag{
    double x;
    double y;
}MyPoint;

// =============================================
//  資料量
#define N 10000
MyPoint *pt; /* 紀錄之資料點, 方便設成全域變數*/

// =============================================
// function declare
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); /* 宣告訊息處理函式 */
void PaintGraphic(HWND ); /* 繪圖函式 */
void SaveAsBmp(HWND); /* ��成bmp */

// =============================================
// 取得輸入資料 , 以 y = sin(x) * x^1/3 , x=START_X step 0.05 產生
void GetInput(unsigned cnt)
{    
    unsigned i=0;
    for(; i!=cnt; ++i){
       pt[i].x = START_X + i*Xd;
       pt[i].y = sqrt(abs(pt[i].x))*sin(pt[i].x); 
    }
}

// =============================================
// 繪圖函式 
void PaintGraphic(HWND hwnd)
{
    HDC hdc;
    PAINTSTRUCT ps;
    unsigned i;
    HPEN hPen,hOldPen; 

    InvalidateRect(hwnd, NULL, TRUE);
    hdc = BeginPaint(hwnd, &ps);

    /*建立畫筆*/
    hPen=CreatePen(PS_SOLID,2,0x00ff00);
    hOldPen=(HPEN)SelectObject(hdc,hPen);


    /*繪出 x 基準線*/
    MoveToEx(hdc, 0, HEIGHT-Y0, NULL);
    LineTo(hdc, WIDTH, HEIGHT-Y0);

    /*繪出 Y 基準線*/
    MoveToEx(hdc, X0, 0, NULL);
    LineTo(hdc, X0, HEIGHT);



    /*重建立另一色畫筆*/
    DeleteObject(hPen);
    hPen=CreatePen(PS_SOLID,2,0xff00ff);
    hOldPen=(HPEN)SelectObject(hdc,hPen);

    for(i=1; i!=N; ++i){
       MoveToEx(hdc, (pt[i-1].x)*RATE+X0, HEIGHT - (pt[i-1].y)*RATE - Y0,NULL);
       LineTo(hdc, (pt[i].x)*RATE+X0,  HEIGHT - (pt[i].y)*RATE - Y0);
    }
    DeleteObject(hPen);
    EndPaint(hwnd, &ps);
}

// =============================================
// 繪圖函式 WinMain 函式, 相當於 Console 之 main 
int WINAPI WinMain(HINSTANCE hInstance, 
                HINSTANCE hPrevInstance,
                PSTR szCmdLine,
                int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("function graphic - vc2008 (by EdisonX, EdisonShih)");
    HWND hwnd;
    MSG msg; 
    WNDCLASS WndClass; 

    /* 配置 N 個資料點 , 並讀入 */
    pt = (MyPoint*)malloc(sizeof(MyPoint)*N);
    GetInput(N);

    WndClass.style = CS_HREDRAW | CS_VREDRAW; /* 視窗大小改變時重繪 */
    WndClass.lpfnWndProc = WndProc; /* 設定此視窗處理之訊息函式, lpfnWndProc 實為函式指標 */
    WndClass.cbClsExtra = 0; /* 通常設0 */
    WndClass.cbWndExtra = 0; /* 通常設0 */
    WndClass.hInstance = hInstance;
    WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); /* 應用程式 Icon */
    WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); /* 滑鼠游標 */
    WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); /* 背景色 */
    WndClass.lpszMenuName = NULL; /* 不設選單(菜單,Menu) */
    WndClass.lpszClassName = szAppName; /* 應用程式名稱 */

    /* 註冊已填好結構之視窗, 若失敗則跳出 */
    if(!RegisterClass(&WndClass)){
       MessageBox(NULL, TEXT("RegisterClass Window Fail."),szAppName, MB_ICONERROR);
       return EXIT_FAILURE;
    }

    /* 建立視窗 */
    hwnd = CreateWindow(szAppName, TEXT("Window Title"),
       // WS_CAPTION | WS_SYSMENU, /* 通常��用 WS_OVERLAPPEDWINDOW, 但此處不允許改變視窗大小 */
       WS_POPUPWINDOW, /* 由於要做截圖,故不繪制外框,截下來就只有圖形 */
       WINDOW_X0,WINDOW_Y0,WIDTH,HEIGHT, /* 使用預設 : CW_USEDEFAULT */
       NULL,NULL,hInstance,NULL); 

    ShowWindow(hwnd, SW_SHOW); /* 顯示此視窗 */
    UpdateWindow(hwnd); /* 直接先更新一次 */

    /* 進入訊息回圈 */
    while( GetMessage(&msg, NULL, 0, 0)){ /* 取得訊息 */
       TranslateMessage(&msg); /* 翻譯 */
       DispatchMessage(&msg); /* 分配 */
    }
    return msg.wParam;
}

// =============================================
// 自定義之 WndProc 訊息處理函式

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    /* 針對截取之訊息做處理 */
    switch(message)
    {
    case WM_RBUTTONDOWN : /* 右鍵時也將進行視窗銷毀 */
    case WM_DESTROY: /* 視窗銷毀 */
       PostQuitMessage(0);
       return 0;
    case WM_PAINT: /* 重繪 */
       PaintGraphic(hwnd );
       return 0;          
    case WM_LBUTTONDOWN: /* 按下滑鼠左鍵時自動��成 bmp */
       SaveAsBmp(hwnd);
       return 0;
    }
    /* 其��未處理之訊息以預設方式處理 */
    return DefWindowProc(hwnd, message, wParam, lParam);
}

// =============================================
// ��成bmp
void SaveAsBmp(HWND hwnd) 
{
    static int number=0;
    char filename[MAX_PATH];
    HDC hdcWindow;
    HDC hdcMemory;
    HBITMAP hBitmap;
    BYTE *lpvBits;
    BITMAPINFO bi;
    BITMAPINFOHEADER bih;
    BITMAPFILEHEADER bifh;
    HANDLE hFile;
    DWORD dwBytesWritten = 0;
    DWORD dwSize;
    int xSize, ySize;
    RECT rect;

    sprintf(filename, "%d.bmp", ++number);
    hdcWindow = GetDC(NULL);

    GetWindowRect(hwnd, &rect);

    xSize = rect.right - rect.left;
    ySize = rect.bottom - rect.top;
    hBitmap = CreateCompatibleBitmap(hdcWindow, xSize, ySize);
    hdcMemory = CreateCompatibleDC(hdcWindow);
    SelectObject(hdcMemory, hBitmap);
    BitBlt(hdcMemory, 0, 0, xSize, ySize, 
       hdcWindow, rect.left, rect.top, SRCCOPY);

    bih.biSize = sizeof(BITMAPINFOHEADER);
    bih.biWidth = xSize;
    bih.biHeight = ySize; 
    bih.biPlanes = 1;
    bih.biBitCount = 32;
    bih.biCompression = BI_RGB;
    bih.biSizeImage = 0;
    bih.biXPelsPerMeter = 0;
    bih.biYPelsPerMeter = 0;
    bih.biClrUsed = 0;
    bih.biClrImportant = 0;

    dwSize = (((xSize * 32 + 31)/32) * 4 * ySize);
    lpvBits = (BYTE*)malloc(dwSize);

    bifh.bfType = 0x4D42;
    bifh.bfSize = sizeof(BITMAPFILEHEADER) + 
       sizeof(BITMAPINFOHEADER) + dwSize;
    bifh.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + 
       (DWORD)sizeof(BITMAPINFOHEADER);
    bifh.bfReserved1 = 0;
    bifh.bfReserved2 = 0;

    bi.bmiHeader = bih;

    GetDIBits(hdcMemory, hBitmap, 0, (UINT)ySize, 
       lpvBits, &bi, DIB_RGB_COLORS);
    hFile = CreateFile(filename, GENERIC_WRITE, 0, 
       NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    WriteFile(hFile, (LPSTR)&bifh, 
       sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)&bih, 
       sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)lpvBits, 
       dwSize, &dwBytesWritten, NULL);

    DeleteDC(hdcMemory);
    DeleteObject(hBitmap);
    CloseHandle(hFile);
    free(lpvBits);
    ReleaseDC(hwnd, hdcWindow);

    /*完成後提示音效*/
    PlaySound(TEXT("Asterisk"), NULL, SND_ALIAS);
}
執行結果圖
1.bmp  

edisonx 發表在 痞客邦 PIXNET 留言(1) 人氣()


留言列表 (1)

發表留言
  • gylinshu
  • 你好: 我目前剛好課業上有此問題,可否寄範例給我參考,謝謝。


    email:gylinshu@yahoo.com.tw
  • 範例看不懂的話,明來再來一次。

    edisonx 於 2015/08/06 11:58 回覆

您尚未登入,將以訪客身份留言。亦可以上方服務帳號登入留言

請輸入暱稱 ( 最多顯示 6 個中文字元 )

請輸入標題 ( 最多顯示 9 個中文字元 )

請輸入內容 ( 最多 140 個中文字元 )

請輸入左方認證碼:

看不懂,換張圖

請輸入驗證碼