/* Filename : plot_prime.c * * Draw prime number islands. * * Author : Craig Dunn * Email : craig@craigsarea.com * Website : www.craigsarea.com * Date Created : 16 February 2005 * * No guarantee is given with this code. Use it at your own risk. *****************************************************************/ #include #include #include #include #include #include "plot_primeres.h" typedef unsigned char U8; typedef unsigned short U16; typedef unsigned int U32; typedef int S32; #define DEF_ORIGIN_X 100 #define DEF_ORIGIN_Y 100 #define DEF_WIDTH 1024 #define DEF_HEIGHT 768 #define BG_RED 32 #define BG_GREEN 32 #define BG_BLUE 135 #define UPDATE_INTERVAL 10000 /* Global Variables. *****************************************************************/ HINSTANCE hInst; HWND hwndMain; U8 *buffer; HDC temp_dc; HBITMAP bitmap; HBITMAP old_bitmap; HANDLE finished_event; HANDLE draw_event; HANDLE plot_event; char prime_list[MAX_PATH]; U32 width; U32 height; U32 old_height; S32 origin_x; S32 origin_y; /* Function Prototypes. *****************************************************************/ int GetFileName (char *buffer, int buflen, BOOL b_open); void plot (char *prime_list); void save_image (char *filename); BOOL init_plot (void); LRESULT CALLBACK MainWndProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam); LRESULT CALLBACK DlgSizeProc (HWND hDlg,UINT msg,WPARAM wParam,LPARAM lParam); /* Function : GetFileName * * Open either a save as or a load dialog box. *****************************************************************/ int GetFileName(char *buffer, int buflen, BOOL b_open) { char tmpfilter[40]; int i = 0; OPENFILENAME ofn; memset(&ofn,0,sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hInstance = GetModuleHandle(NULL); ofn.hwndOwner = GetActiveWindow(); ofn.lpstrFile = buffer; ofn.nMaxFile = buflen; if(b_open) ofn.lpstrTitle = "Open"; else ofn.lpstrTitle = "Save"; ofn.nFilterIndex = 2; ofn.lpstrDefExt = "txt"; strcpy(buffer,"*.txt"); ofn.Flags = 539678; ofn.lpstrFilter = NULL; if(b_open) return GetOpenFileName(&ofn); else return GetSaveFileName(&ofn); } /* Function : plot * * Performs a mod on the prime number list and then updates a * height map. * * This function can take a little while to complete. To allow * the height map to be drawn to the screen while the ploting is * taking place, this function is run as a seperate thread. *****************************************************************/ void plot(char *prime_list) { FILE *fptr; int x, y; U32 prime; char string[100]; U32 count; HDC hdc; ResetEvent(finished_event); ResetEvent(draw_event); x = origin_x; y = origin_y; count = 0; /* Check the buffers are okay. */ if(buffer == NULL || temp_dc == NULL) { MessageBox(NULL, "The buffers are null!", "Disaster", MB_OK); return; } /* Open the list of prime numbers. */ fptr = fopen(prime_list, "rb"); if(fptr == NULL) { MessageBox(NULL, "Couldn't open the prime number list!", "Disaster", MB_OK); return; } SetWindowText(hwndMain, "Plotting..."); /* Keep reading prime numbers from the list until the end of file is reached. */ while(!feof(fptr)) { fscanf(fptr, "%s", string); prime = atoi(string); /* Do the mod and move accordingly. */ switch(prime % 5) { case 1: y-=1; break; case 2: x+=1; break; case 3: y+=1; break; case 4: x-=1; break; default: break; } /* If the mod location is still on the screen, plot it. */ if((y >= 0 && y < height) && (x >= 0 && x < width)) { /* Each height point is only 8 bits. So, don't overflow. */ if(buffer[x + (y * width)] < 255) { buffer[x + (y * width)]++; /* Draw the height in a shade of green. */ SetPixel(temp_dc, x, y, RGB(0, buffer[x + (y * width)], 0)); } } /* Update the screen each time a certain number of primes has been read. */ count++; if(count >= UPDATE_INTERVAL) { count = 0; SetEvent(draw_event); ResetEvent(plot_event); InvalidateRect(hwndMain, NULL, FALSE); WaitForSingleObject(plot_event, INFINITE); } } SetWindowText(hwndMain, "Plot Prime"); SetEvent(finished_event); fclose(fptr); InvalidateRect(hwndMain, NULL, FALSE); } /* Function : save_image * * Save the height map image to a bitmap file. *****************************************************************/ void save_image(char *filename) { BITMAPFILEHEADER bmp_file_header; BITMAPINFOHEADER bmp_info_header; RGBQUAD rgb_quad; FILE *file_ptr; U8 g; U32 x, y; /* Open the destination file. */ file_ptr = fopen(filename, "wb"); if(file_ptr == NULL) { MessageBox(NULL, "Couldn't open the destination file!", "Disaster", MB_OK); return; } /* Setup the file header. */ bmp_file_header.bfType = ('M' << 8) + 'B'; bmp_file_header.bfSize = width * height; bmp_file_header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER ) + (256 * sizeof(RGBQUAD)); /* Setup the info header. */ bmp_info_header.biSize = sizeof(BITMAPINFOHEADER ); bmp_info_header.biWidth = width; bmp_info_header.biHeight = height; bmp_info_header.biPlanes = 1; bmp_info_header.biBitCount = 8; bmp_info_header.biCompression = BI_RGB; bmp_info_header.biSizeImage = 0; bmp_info_header.biXPelsPerMeter = 0; bmp_info_header.biYPelsPerMeter = 0; bmp_info_header.biClrUsed = 0; bmp_info_header.biClrImportant = 0; fwrite(&bmp_file_header, sizeof(BITMAPFILEHEADER), 1, file_ptr); fwrite(&bmp_info_header, sizeof(BITMAPINFOHEADER), 1, file_ptr); /* Write the palette. */ rgb_quad.rgbRed = 32; rgb_quad.rgbGreen = 32; rgb_quad.rgbBlue = 135; rgb_quad.rgbReserved = 0; fwrite(&rgb_quad, sizeof(RGBQUAD), 1, file_ptr); for(g=0; g < 255; g++) { rgb_quad.rgbRed = 0; rgb_quad.rgbGreen = g + 1; rgb_quad.rgbBlue = 0; rgb_quad.rgbReserved = 0; fwrite(&rgb_quad, sizeof(RGBQUAD), 1, file_ptr); } /* Draw the horizontal origin line. */ memset(&buffer[width * origin_y], 1, width); /* Draw the vertical origin line. */ for(y=0; y < height; y++) buffer[(width * y) + origin_x] = 1; /* Write the height map to the BMP file. Bitmaps are stored upside down. */ for(y=height; y > 0; y--) { fwrite(&buffer[width * (y-1)], width, 1, file_ptr); } fclose(file_ptr); } /* Function : init_plot * * Because the size of the height map can change the setup maybe * repeated several times. *****************************************************************/ BOOL init_plot(void) { int y; RECT rect; HBRUSH brush; HDC hdc; /* Free any previous buffers. */ if(buffer != NULL) { free(buffer); buffer = NULL; } /* Check the width and height are valid. */ if(width == 0 || height == 0) { MessageBox(NULL, "The width or height is set to zero!", "Disaster", MB_OK); return FALSE; } /* Create the new buffer. */ buffer = (U8*)calloc(width * height * sizeof(U8), sizeof(U8)); if(buffer == NULL) { MessageBox(NULL, "Can't allocate the buffer!", "Disaster", MB_OK); return FALSE; } /* Delete any previous screen buffers. */ if(temp_dc != NULL) { SelectObject(temp_dc, old_bitmap); DeleteObject(bitmap); DeleteDC(temp_dc); } /* Create an off screen buffer for the drawing. */ hdc = GetDC(hwndMain); temp_dc = CreateCompatibleDC(hdc); bitmap = CreateCompatibleBitmap(hdc, width, height); old_bitmap = SelectObject(temp_dc, bitmap); ReleaseDC(hwndMain, hdc); /* Set the background colour. */ brush = CreateSolidBrush(RGB(BG_RED, BG_GREEN, BG_BLUE)); rect.top = 0; rect.bottom = height; rect.left = 0; rect.right = width; FillRect(temp_dc, &rect, brush); /* Draw the 2 origin lines. */ MoveToEx(temp_dc, origin_x, 0, NULL); LineTo(temp_dc, origin_x, height); MoveToEx(temp_dc, 0, origin_y, NULL); LineTo(temp_dc, width, origin_y); DeleteObject(brush); GetWindowRect(hwndMain, &rect); SetScrollRange(hwndMain, SB_HORZ, 0, width - (rect.right), TRUE); SetScrollRange(hwndMain, SB_VERT, 0, height - (rect.bottom), TRUE); return TRUE; } /* Message handler for the size dialog box. *****************************************************************/ LRESULT CALLBACK DlgSizeProc(HWND hDlg,UINT msg,WPARAM wParam,LPARAM lParam) { switch(msg) { case WM_INITDIALOG: SetDlgItemInt(hDlg, IDE_WIDTH, width, FALSE); SetDlgItemInt(hDlg, IDE_HEIGHT, height, FALSE); SetDlgItemInt(hDlg, IDE_ORIGINX, origin_x, TRUE); SetDlgItemInt(hDlg, IDE_ORIGINY, origin_y, TRUE); break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDOK: width = GetDlgItemInt(hDlg, IDE_WIDTH, NULL, FALSE); height = GetDlgItemInt(hDlg, IDE_HEIGHT, NULL, FALSE); origin_x = GetDlgItemInt(hDlg, IDE_ORIGINX, NULL, TRUE); origin_y = GetDlgItemInt(hDlg, IDE_ORIGINY, NULL, TRUE); init_plot(); InvalidateRect(hwndMain, NULL, FALSE); EndDialog(hDlg, LOWORD(wParam)); return TRUE; case IDCANCEL: EndDialog(hDlg, LOWORD(wParam)); return TRUE; } } return 0; } /* The main message hanlder. *****************************************************************/ LRESULT CALLBACK MainWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; DWORD dwID; HANDLE array[2]; U32 y; char image_name[MAX_PATH]; SCROLLINFO scroll_info; static int scroll_vert_x; static int scroll_vert_y; switch(msg) { /* Initialise everything. */ case WM_CREATE: old_height = DEF_HEIGHT; height = DEF_HEIGHT; width = DEF_WIDTH; origin_x = DEF_ORIGIN_X; origin_y = DEF_ORIGIN_Y; scroll_vert_x = 0; scroll_vert_y = 0; temp_dc = NULL; buffer = NULL; finished_event = CreateEvent(NULL, TRUE, TRUE, NULL); draw_event = CreateEvent(NULL, TRUE, TRUE, NULL); plot_event = CreateEvent(NULL, TRUE, TRUE, NULL); init_plot(); break; /* Handle the menu. */ case WM_COMMAND: switch(wParam) { case IDM_NEW: init_plot(); InvalidateRect(hwndMain, NULL, FALSE); break; case IDM_PLOT: CreateThread(NULL, 0, plot, prime_list, 0, &dwID); break; case IDM_OPEN: GetFileName(prime_list, MAX_PATH, TRUE); break; case IDM_SAVE: if(GetFileName(image_name, MAX_PATH, FALSE) != 0) save_image(image_name); break; case IDM_SETUP: DialogBox(hInst, MAKEINTRESOURCE(DLG_SIZE), hwndMain, (DLGPROC)DlgSizeProc); break; case IDM_EXIT: PostMessage(hwnd,WM_CLOSE,0,0); break; } break; case WM_SIZE: SetScrollRange(hwndMain, SB_HORZ, 0, width - LOWORD(lParam), TRUE); SetScrollRange(hwndMain, SB_VERT, 0, height - HIWORD(lParam), TRUE); break; case WM_HSCROLL: scroll_info.cbSize = sizeof(SCROLLINFO); scroll_info.fMask = SIF_TRACKPOS; GetScrollInfo(hwndMain, SB_HORZ, &scroll_info); SetScrollPos(hwndMain, SB_HORZ, scroll_info.nTrackPos, TRUE); scroll_vert_x = scroll_info.nTrackPos; InvalidateRect(hwndMain, NULL, FALSE); break; case WM_VSCROLL: scroll_info.cbSize = sizeof(SCROLLINFO); scroll_info.fMask = SIF_TRACKPOS; GetScrollInfo(hwndMain, SB_VERT, &scroll_info); SetScrollPos(hwndMain, SB_VERT, scroll_info.nTrackPos, TRUE); scroll_vert_y = scroll_info.nTrackPos; InvalidateRect(hwndMain, NULL, FALSE); break; /* Draw to the screen. */ case WM_PAINT: BeginPaint(hwnd, &ps); hdc = GetDC(hwnd); array[0] = finished_event; array[1] = draw_event; if(WaitForMultipleObjects(2, array, FALSE, 1) != WAIT_TIMEOUT) { BitBlt(hdc, 0, 0, width, height, temp_dc, scroll_vert_x, scroll_vert_y, SRCCOPY); ResetEvent(draw_event); } SetEvent(plot_event); ReleaseDC(hwnd, hdc); EndPaint(hwnd, &ps); break; /* Clean up and exit. */ case WM_DESTROY: if(temp_dc != NULL) { SelectObject(temp_dc, old_bitmap); DeleteObject(bitmap); DeleteDC(temp_dc); } /* Free any previous buffers. */ if(buffer != NULL) { free(buffer); buffer = NULL; } PostQuitMessage(0); break; default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; } /* Main entry point. *****************************************************************/ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) { MSG msg; WNDCLASS wc; hInst = hInstance; memset(&wc,0,sizeof(WNDCLASS)); wc.style = CS_HREDRAW|CS_VREDRAW |CS_DBLCLKS ; wc.lpfnWndProc = (WNDPROC)MainWndProc; wc.hInstance = hInst; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszClassName = "plot_primeWndClass"; wc.lpszMenuName = MAKEINTRESOURCE(IDMAINMENU); wc.hCursor = LoadCursor(NULL,IDC_ARROW); wc.hIcon = LoadIcon(NULL,IDI_APPLICATION); if (!RegisterClass(&wc)) return 0; hwndMain = CreateWindow("plot_primeWndClass","plot_prime", WS_MINIMIZEBOX | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_MAXIMIZEBOX | WS_CAPTION | WS_BORDER | WS_SYSMENU | WS_THICKFRAME | WS_HSCROLL | WS_VSCROLL, CW_USEDEFAULT,0,CW_USEDEFAULT,0, NULL, NULL, hInst, NULL); ShowWindow(hwndMain, SW_SHOW); while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }