So, I've been playing around with C++ for some time now, using various books and web tutorials.
and now I've come to graphics. currently in WinApi.
I've got a window to which i paint text, images or pixels.
But, painting a lot of pixels using SetPixels is simply to slow.
Snippet from my code:
void DrawBitmap(RECT rect, string text) {
HDC buffer = CreateCompatibleDC(device);
HBITMAP BGimage = CreateCompatibleBitmap(device, rect.right - rect.left, rect.bottom - rect.top);
SelectObject(buffer, BGimage);
//Clearing the screen with a full rect
Rectangle(buffer, rect.left, rect.top, rect.right, rect.bottom);
//Sample on making a single pixel at mouseclik, with color 250 on screen.
SetPixelV(buffer, x, y, 250);
int PixelSize = 4;
//SOME HEAVY PIXELS to slow the FPS
/*
for (int i = 0; i < 255; i++) {
for (int k = 0; k < 255; k++) {
SetPixelV(buffer, x + i, y + k, COLORREF RGB(150, i, k));
}
}
*/
//Sample on making some text.
RECT drawRect;
drawRect = { rect.left + 5, rect.top + 5, rect.left + 105, rect.top + 25 };
DrawText(buffer, text.c_str(), text.length(), &drawRect, DT_LEFT);
// counter number to be converted to a string
int timeint = GetTickCount();
ostringstream convert; // stream used for the conversion
convert << "TIME: " << timeint; // insert the textual representation of 'Number' in the characters in the stream
text = convert.str();
drawRect = { rect.left + 5, rect.top + 25, rect.left + 680, rect.top + 45 };
DrawText(buffer, text.c_str(), text.length(), &drawRect, DT_LEFT);
ostringstream convert2; // stream used for the conversion
convert2 << "FPS: " << FPS_calc; // insert the textual representation of 'Number' in the characters in the stream
text = convert2.str();
drawRect = { rect.left + 5, rect.top + 45, rect.left + 680, rect.top + 65 };
DrawText(buffer, text.c_str(), text.length(), &drawRect, DT_LEFT);
//do the dubble buffering
BitBlt(device, 0, 0, rect.right - rect.left, rect.bottom - rect.top, buffer, 0, 0, SRCCOPY);
DeleteDC(buffer);
DeleteObject((HBITMAP)BGimage);
}
now, this works fine, but the //SOME HEAVY PIXELS (currently commented off)
takes up a lot of the speed. then i heard you can lock the image and manipulate the bites.
I simply cannot get my head around that?
I want (for the sample that is) to fill out a 100 by 100 field at the x, y with individual pixels, while avoiding all the costly call functions.
I've tried [https://msdn.microsoft.com/en-us/library/5ey6h79d(v=vs.110).aspx?cs-save-lang=1&cs-lang=cpp#code-snippet-2]
And many other versions of the same. but i cannot seem to get i to work..
does anyone have any idea?
my full working code is here:
#include<Windows.h>
#include<iostream>
#include<time.h>
#include<string>
#include <sstream>
using namespace std;
const string APPTITLE = "GAME LOOP";
const string APPNAME = "GAME LOOP";
HWND window;
HDC device;
bool gameover = false;
POINT p;
int x = 200;
int y = 200;
int startTime;
int currentTime;
int lastTime;
int FPS_calc = 0;
// FORWARD DECLARATIONS
void DrawBitmap(RECT rect, string text);
bool Game_Init();
void Game_Run();
void Game_End();
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow);
LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
string ArrowKey();
void MouseDet(POINT &mp);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
MSG msg;
MyRegisterClass(hInstance);
if (InitInstance(hInstance, nCmdShow) != 1) return GetLastError();
if (!Game_Init()) return 0;
while (!gameover) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Game_Run();
}
Game_End();
return msg.wParam;
}
//////////Functions//////////////
// Main game function
void Game_Run() {
if (gameover == true) return;
RECT rect;
GetClientRect(window, &rect);
//ARROW KEY DETECTOR
string text = ArrowKey();
//MouseDetector sets point if Left mouse key is pressed/held
MouseDet(p);
currentTime = GetTickCount();
FPS_calc = int(1000 / (currentTime-lastTime));
if (lastTime != currentTime) lastTime = currentTime-1;
else lastTime = currentTime-30;
DrawBitmap(rect, text);
}
//Draw function with dubble buffering
void DrawBitmap(RECT rect, string text) {
HDC buffer = CreateCompatibleDC(device);
HBITMAP BGimage = CreateCompatibleBitmap(device, rect.right - rect.left, rect.bottom - rect.top);
SelectObject(buffer, BGimage);
//Clearing the screen with a full rect
Rectangle(buffer, rect.left, rect.top, rect.right, rect.bottom);
//Sample on making a single pixel at mouseclik, with color 250 on screen.
SetPixelV(buffer, x, y, 250);
int PixelSize = 4;
//SOME HEAVY PIXELS to slow the FPS
/*
for (int i = 0; i < 255; i++) {
for (int k = 0; k < 255; k++) {
SetPixelV(buffer, x + i, y + k, COLORREF RGB(150, i, k));
}
}
*/
//Sample on making some text.
RECT drawRect;
drawRect = { rect.left + 5, rect.top + 5, rect.left + 105, rect.top + 25 };
DrawText(buffer, text.c_str(), text.length(), &drawRect, DT_LEFT);
// counter number to be converted to a string
int timeint = GetTickCount();
ostringstream convert; // stream used for the conversion
convert << "TIME: " << timeint; // insert the textual representation of 'Number' in the characters in the stream
text = convert.str();
drawRect = { rect.left + 5, rect.top + 25, rect.left + 680, rect.top + 45 };
DrawText(buffer, text.c_str(), text.length(), &drawRect, DT_LEFT);
ostringstream convert2; // stream used for the conversion
convert2 << "FPS: " << FPS_calc; // insert the textual representation of 'Number' in the characters in the stream
text = convert2.str();
drawRect = { rect.left + 5, rect.top + 45, rect.left + 680, rect.top + 65 };
DrawText(buffer, text.c_str(), text.length(), &drawRect, DT_LEFT);
//do the dubble buffering
BitBlt(device, 0, 0, rect.right - rect.left, rect.bottom - rect.top, buffer, 0, 0, SRCCOPY);
DeleteDC(buffer);
DeleteObject((HBITMAP)BGimage);
}
//initialize value
bool Game_Init() {
//get a random engine
srand(time(NULL));
//getStartTime
startTime = GetTickCount();
currentTime = startTime;
lastTime = currentTime - 1;
FPS_calc = int(1000 / (lastTime - currentTime));
return 1;
}
//End the game, release the window
void Game_End() {
ReleaseDC(window, device);
}
//A window "setup"
ATOM MyRegisterClass(HINSTANCE hInstance) {
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WinProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = APPTITLE.c_str();
wc.hIconSm = NULL;
return RegisterClassEx(&wc);
}
//Creates our visible window
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
window = CreateWindow(APPTITLE.c_str(), APPTITLE.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, NULL, NULL, hInstance, NULL);
if (window == 0) return 0;
ShowWindow(window, nCmdShow);
UpdateWindow(window);
device = GetDC(window);
return 1;
}
LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_DESTROY:
gameover = true;
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
//Test if arrow keys have been typed, return text wit hwich one
string ArrowKey() {
string text = "NONE";
if ((1 << 16) & GetAsyncKeyState(VK_UP))
{
text = "UP";
y--;
}
if ((1 << 16) & GetAsyncKeyState(VK_DOWN))
{
text = "DOWN";
y++;
}
if ((1 << 16) & GetAsyncKeyState(VK_RIGHT))
{
text = "RIGTH";
x++;
}
if ((1 << 16) & GetAsyncKeyState(VK_LEFT))
{
text = "LEFT";
x--;
}
return text;
}
void MouseDet(POINT &mp) {
if ((1 << 16) & GetAsyncKeyState(VK_LBUTTON)) {
GetCursorPos(&mp);
if (ScreenToClient(window, &mp)) {}
x = p.x;
y = p.y;
}
}
If you're just looking to draw a rectangle/square then you can use FillRect to do this.
Related
I wanted to write a turtle in c++, but the angles at the turn are not accurate.
I tried to create a Koch curve, but the result was bad
Hereis a larger picture of this, it looks like the angles are wrong
This is my program:
#include <windows.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include "turtle.h"
#define MAX_LOADSTRING 100
// Global Variables:
#define HIBA_00 TEXT("Error:Program initialisation process.")
HINSTANCE hInstGlob;
int OwniCmdShow;
char szClassName[] = "WindowsApp";
HWND Form1;
LRESULT CALLBACK WndProc0(HWND, UINT, WPARAM, LPARAM);
RECT rc;
HDC hdcMem;
HBITMAP hbmMem, hbmOld;
HBRUSH hbrBkGnd;
HDC hdc;
PAINTSTRUCT ps;
void DB_prepare_puffer(void);
void DB_start_drawing(void);
void DB_end_drawing(void);
void draw(void);
void screen_clear(void);
void print(int n);
int depth = 4;
float length = 1000;
turtle t;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("StdWinClassName");
MSG msg;
WNDCLASS wndclass0;
OwniCmdShow = iCmdShow;
hInstGlob = hInstance;
wndclass0.style = CS_HREDRAW | CS_VREDRAW;
wndclass0.lpfnWndProc = WndProc0;
wndclass0.cbClsExtra = 0;
wndclass0.cbWndExtra = 0;
wndclass0.hInstance = hInstance;
wndclass0.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass0.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass0.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass0.lpszMenuName = NULL;
wndclass0.lpszClassName = TEXT("WIN0");
if (!RegisterClass(&wndclass0))
{
MessageBox(NULL, HIBA_00, TEXT("Program Start"), MB_ICONERROR); return 0;
}
Form1 = CreateWindow(TEXT("WIN0"),
TEXT("Form1"),
(WS_OVERLAPPED | WS_SYSMENU | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX),
0,
0,
1920,
1050,
NULL,
NULL,
hInstance,
NULL);
DB_prepare_puffer();
ShowWindow(Form1, OwniCmdShow);
UpdateWindow(Form1);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc0(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc_lokal;
PAINTSTRUCT ps_lokal;
switch (message) {
case WM_CREATE:
break;
case WM_MOUSEMOVE:
break;
case WM_ERASEBKGND:
return (LRESULT)1;
break;
case WM_PAINT:
hdc_lokal = BeginPaint(hwnd, &ps_lokal);
EndPaint(hwnd, &ps_lokal);
t.init();
draw();
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, message, wParam, lParam);
break;
}
return NULL;
}
void DB_prepare_puffer(void)
{
GetClientRect(Form1, &rc);
hdc = GetDC(Form1);
hbmMem = CreateCompatibleBitmap(hdc, rc.right - rc.left, rc.bottom - rc.top);
}
void DB_start_drawing(void)
{
GetClientRect(Form1, &rc);
hdc = GetDC(Form1);
BeginPaint(Form1, &ps);
hdcMem = CreateCompatibleDC(hdc);
hbmOld = (HBITMAP)SelectObject(hdcMem, hbmMem);
hbrBkGnd = CreateSolidBrush(RGB(255, 255, 255));
FillRect(hdcMem, &rc, hbrBkGnd);
DeleteObject(hbrBkGnd);
}
void DB_end_drawing(void)
{
BitBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
EndPaint(Form1, &ps);
}
void draw(void)
{
DB_start_drawing();
screen_clear();
int i = 0;
depth = 5;
length = 1200;
while (i < depth) {
length = length / 3;
i++;
}
t.right(270);
print(depth);
DB_end_drawing();
}
//************************
//clear screen
//************************
void screen_clear(void)
{
HBRUSH hBrush;
RECT rect;
HDC hdc;
hdc = hdcMem;
hBrush = CreateSolidBrush(RGB(255, 255, 255));
SelectObject(hdc, hBrush);
SetRect(&rect, 0, 0, 1500, 900);
FillRect(hdc, &rect, hBrush);
DeleteObject(hBrush);
}
void print(int m) {
int n = m;
if (n == 0) {
t.forward(length, hdcMem);
}
if (n != 0){
n -= 1;
print(n);
t.left(60);
print(n);
t.right(120);
print(n);
t.left(60);
print(n);
}
}
this is the turtle.h file:
#pragma once
#define _USE_MATH_DEFINES
#include "resource.h"
#include <windows.h>
#include <cmath>
class turtle {
public:
void init();
void right(double _angle);
void left(double _angle);
void forward(int distance, HDC hdc);
double angle = 0;
POINT pos;
protected:
private:
};
And this is the turtle.cpp:
#pragma once
#include "turtle.h"
void turtle::init()
{
pos.x = 40;
pos.y = 800;
angle = 0;
}
void turtle::left(double _angle) {
angle -= _angle;
}
void turtle::right(double _angle) {
angle += _angle;
}
void turtle::forward(int distance, HDC hdc) {
MoveToEx(hdc, pos.x, pos.y, NULL);
double cos_angle = cos(angle * M_PI / (double)180);
double sin_angle = sin(angle * M_PI / (double)180);
POINT endpoint;
endpoint.x = (0 - (double)distance * sin_angle) + pos.x;
endpoint.y = (0 + (double)distance * cos_angle) + pos.y;
LineTo(hdc, endpoint.x, endpoint.y);
pos = endpoint;
}
In Python, however, this code worked (essentially the same as the c++ code):
from turtle import *
color('blue', 'white')
speed(100000000)
penup()
back(600)
pendown()
begin_fill()
depth = 0
length = 300
i = 0
def print(n):
if n == 0:
forward(length )
if n != 0:
n -= 1
print(n)
left(60)
print(n)
right(120)
print(n)
left(60)
print(n)
i = 0
depth = 5
length = 300
while i < depth:
length = length / 3
i += 1
print(depth)
(Result)
I hope you can help me.
Thanks
It will work with this code because the previously calculated position is stored more accurately.
turtle.cpp:
#pragma once
#include "turtle.h"
void turtle::init()
{
pos.x = 40;
pos.y = 800;
angle = 0;
}
void turtle::left(double _angle) {
angle -= _angle;
}
void turtle::right(double _angle) {
angle += _angle;
}
void turtle::forward(double distance, HDC hdc) {
MoveToEx(hdc, pos.x, pos.y, NULL);
double cos_angle = cos(angle * M_PI / (double)180);
double sin_angle = sin(angle * M_PI / (double)180);
DPOINT endpoint;
endpoint.x = (0 - distance * sin_angle) + pos.x;
endpoint.y = (0 + distance * cos_angle) + pos.y;
LineTo(hdc, endpoint.x, endpoint.y);
pos = endpoint;
}
And insert this code to turtle.h:
`
typedef struct DPOINT {
double x;
double y;
};
`
And change the 'pos' variable type to 'DPOINT'
This code in my windowless framework is an area like just button,but some code I wanted to run it only once until the mouse move on the area next time.
All those code are in WM_MOUSEMOVE.
int ClickAreaPtInfo(HWND hWnd, int x, int y, int sizex, int sizey, LPARAM lParam,int &value)
{
POINT pt;
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
RECT rect;
GetClientRect(hWnd, &rect);
RECT rc = { x,y,x + sizex,y + sizey };
if (PtInRect(&rc, pt))
{
value = 1;
return -1;
}
else
{
value = 0;
return -1;
}
return -1;
}
int _CreateMouseEvent(HWND hWnd, int x, int y, int sizex, int sizey, LPARAM lParam,RUNFUN function(), const wchar_t* Panelid, const wchar_t* CtlName)
{
int val = 0;
int msg = 0;
//int ist = 0; int istprev = 0;
RECT winrc;
GetClientRect(hWnd, &winrc);
RECT rc;
RectTypeConvert(rc,x, y, sizex, sizey);
if (Panelid == PanelID)
{
int nst = 1;
//OutputDebugString(L"HOVER!\n");
msg = 1;
ClickAreaPtInfo(hWnd, x, y, sizex, sizey, lParam, val);
if (val == 1)
{
if (ClickMsg == 1) //Click(Get from WM_LBUTTONUP)
{
ClickMsg = 0;
function();
}
else
{
//It must be run for only once until the mouse leave next time,or it will lead to a lot of resource occupation
if (CtlName == L"Button") //HOVER
{
if (nst == 1)
{
HDC hdc = GetDC(hWnd);
CreateSimpleButtonEx(hWnd, hdc, x, y, sizex, sizey, UICOLOR_GREENSEA, 1, ButtonText);
ReleaseDC(hWnd, hdc);
nst = 0;
return 0;
}
else
{
nst = 0;
return 0;
}
return 0;
}
if (CtlName == L"CloseButton") ///HOVER
{
if (nst == 1)
{
HDC hdc = GetDC(hWnd);
CreateRect(hWnd, hdc, x, y, sizex, sizey, UICOLOR_PEACHRED);
PanelDrawCloseBtn(hWnd, hdc, rc.right - 40, 0, 40, 40, 12, UICOLOR_WHITE);
ReleaseDC(hWnd, hdc);
nst = 0;
return 0;
}
else
{
nst = 0;
return 0;
}
return 0;
}
else
{
return 0;
}
}
}
if (val == 0) //Leave
{
nst = 1;
InvalidateRect(hWnd, &rc, 0); //It must be run for only once until the mouse leave next time,or it will lead to a lot of resource(CPU) occupation
}
}
if (Panelid == PrevPanelID)
{
msg = 1;
}
else
{
msg = 0;
}
return 0;
}
then handle CreateMouseEvent in WM_MOUSEMOVE:
case WM_MOUSEMOVE:
{
CreateMouseEvent(hWnd, 20, 60, 140, 40, lParam, test, L"Init",L"Button");
CreateMouseEvent(hWnd, 20, 120, 140, 40, lParam, test3, L"Init", L"Button");
CreateMouseEvent(hWnd, 20, 180, 140, 40, lParam, btn3, L"Init",L"Button");
CreateMouseEvent(hWnd, rc.right - 40, 0, 40, 40, lParam, CloseWindow, L"Init",L"CloseButton");
break;
}
And I will also give a picture to this question!
http://db.vertexstudio.xyz/lnk/PanelPic/debuginf.png
any solution?thanks!
Some of your problems have already been mentioned above.
There are some variables that are not defined in your example, neither type nor value, but I can extrapolate some at least.
Also, in several places, you try to directly compare a C style string pointer (not a std::string!) to another C style string pointer.
if (Panelid == PanelID) is one place
if (CtlName == L"Button") //HOVER === is another
if (CtlName == L"CloseButton") ///HOVER another
You can use something like int _wcsnicmp( const wchar_t *string1,const wchar_t *string2, size_t count); or whatever your compiler supports. Optionally, you could just use std::wstring.
As for the click, you need to save the rect you get with GetClientRect(hWnd, &winrc); It needs persistence, ie saved outside of the function and accessible. Then compare to this rectangle and act accordingly.
The program window wont update when I try to draw a rectangle. It isn't the program not responding because I can still draw the background, but the rectangle wont update and I don't know what to change. There are no errors popping up, and the only warning is:
Warning C28251 Inconsistent annotation for 'WinMain': this instance has no annotations.
This program is in two .cpp files, the first one doesn't create a window (make sure to set that in properties), and the second one does create a window.
Note: the first .cpp file is called render.cpp and is included in the second file.
Here is the code for debugging:
#include <Windows.h>
struct Render_State
{
int width;
int hight;
void* memory;
BITMAPINFO bitmap_info;
};
Render_State render_state;
void render_backround(HWND hwnd, int colour)
{
if (WM_PAINT)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
unsigned int* pixel = (unsigned int*)render_state.memory;
for (int y = 0; y < render_state.hight; y += 1)
{
for (int x = 0; x < render_state.width; x += 1)
{
*pixel++ = colour;
}
}
// render
StretchDIBits(hdc, 0, 0, render_state.width, render_state.hight, 0, 0,
render_state.width,
render_state.hight,
render_state.memory, &render_state.bitmap_info, DIB_RGB_COLORS, SRCCOPY); {}
EndPaint(hwnd, &ps);
}
}
void clear_screen(HWND hwnd, int colour)
{
if (WM_PAINT)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
unsigned int* pixel = (unsigned int*)render_state.memory;
for (int y = 0; y < render_state.hight; y += 1)
{
for (int x = 0; x < render_state.width; x += 1)
{
*pixel++ = colour;
}
}
// render
StretchDIBits(hdc, 0, 0, render_state.width, render_state.hight, 0, 0, render_state.width,
render_state.hight,
render_state.memory, &render_state.bitmap_info, DIB_RGB_COLORS, SRCCOPY); {}
EndPaint(hwnd, &ps);
}
}
void draw_rect(HWND hwnd, int X, int Y, int X2, int Y2, int colour)
{
if (WM_PAINT)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
for (int y = Y; y < Y2; y++)
{
// unsigned int* pixel = (unsigned int*)render_state.memory;
size_t pixel = size_t(render_state.memory) + static_cast<size_t>(X) + static_cast<size_t> (y) * static_cast<size_t> (render_state.width);
for (int x = X; x < X2; x++)
{
pixel += 0xf5500;
}
}
// render
StretchDIBits(hdc, X, Y, X2, Y2, X, Y, render_state.width, render_state.hight,
render_state.memory, &render_state.bitmap_info, DIB_RGB_COLORS, SRCCOPY); {}
EndPaint(hwnd, &ps);
}
}
#include <Windows.h>
bool running = true;
#include "renderer.cpp"
LRESULT CALLBACK windows_callback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
switch (uMsg)
{
case WM_CLOSE:
case WM_DESTROY:
{
PostQuitMessage(0);
}
break;
case WM_SIZE:
{
RECT rect;
GetClientRect(hwnd, &rect);
render_state.width = rect.right - rect.left;
render_state.hight = rect.bottom - rect.top;
int size = render_state.width * render_state.hight * sizeof(unsigned int);
if (render_state.memory) VirtualFree(render_state.memory, 0, MEM_RELEASE);
render_state.memory = VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
render_state.bitmap_info.bmiHeader.biSize = sizeof(render_state.bitmap_info.bmiHeader);
render_state.bitmap_info.bmiHeader.biWidth = render_state.width;
render_state.bitmap_info.bmiHeader.biHeight = render_state.hight;
render_state.bitmap_info.bmiHeader.biPlanes = 1;
render_state.bitmap_info.bmiHeader.biBitCount = 32;
render_state.bitmap_info.bmiHeader.biCompression = BI_RGB;
//render_backround(hwnd, 0xf2000);
//clear_screen(hwnd, 0xff5500);
draw_rect(hwnd, 3, 5, 50, 50, 0xff5500);
}
break;
default:
{
result = DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
return result;
}
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
//compile window
CHAR clsName[] = "test";
WNDCLASSA window_class = {};
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpszClassName = clsName;
window_class.lpfnWndProc = windows_callback;
//register clases
ATOM atom = RegisterClassA(&window_class);
if (0 == atom)
{
DWORD err = GetLastError();
return 1;
}
// create window
HWND window = CreateWindow(clsName, "game", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT,
CW_USEDEFAULT, 720, 360, 0, 0, hInstance, 0);
if (NULL == window)
{
DWORD err = GetLastError();
return 1;
}
MSG message;
HDC hdc = GetDC(window);
// Main message loop:
while (GetMessage(&message, nullptr, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
//simulate
//render
StretchDIBits(hdc, 0, 0, render_state.width, render_state.hight, 0, 0, render_state.width,
render_state.hight,
render_state.memory, &render_state.bitmap_info, DIB_RGB_COLORS, SRCCOPY); {}
}
In the draw_rect function, pixel is an unsigned int type.
for (int x = X; x < X2; ++x)
{
pixel += 0xf5500;
}
pixel += 0xf5500 will only increase the size of pixel, because pixel is not an address.
This is the reason why square cannot be drawn =>render_state.memory has no color data.
You need to declare pixel as a pointer variable. As you declared in the render_backround function.
size_t* pixel = (size_t*)render_state.memory;
for (int y = 0; y < Y2; y += 1)
{
for (int x = 0; x < X2; x += 1)
{
*pixel++ = colour;
}
}
The whole draw_rect code,
void draw_rect(HWND hwnd, int X, int Y, int X2, int Y2, int colour)
{
if (WM_PAINT)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
size_t* pixel = (size_t*)render_state.memory;
for (int y = 0; y < Y2; y += 1)
{
for (int x = 0; x < X2; x += 1)
{
*pixel++ = colour;
}
}
render_state.bitmap_info.bmiHeader.biWidth = X2;
render_state.bitmap_info.bmiHeader.biHeight = Y2;
StretchDIBits(hdc, X, Y, X2, Y2, X, Y, X2, Y2,
render_state.memory, &render_state.bitmap_info, DIB_RGB_COLORS, SRCCOPY);
EndPaint(hwnd, &ps);
}
}
Note: Responsible only for your current code examples. As stated in the comment section, you can re-read the drawing of win32 window, especially the WindowProc callback function.
As stated in comments, the problem is that you are not handling the WM_PAINT message in your windows_callback() at all. You need to do something more like this instead:
renderer.h
#ifndef renderer_H
#define renderer_H
#include <Windows.h>
struct Render_State
{
int width;
int hight;
void* memory;
BITMAPINFO bitmap_info;
};
void render_background(HDC hdc, Render_State &state, int colour);
void clear_screen(HDC hdc, Render_State &state, int colour);
void draw_rect(HDC hdc, Render_State &state, int X, int Y, int X2, int Y2, int colour);
#endif
renderer.cpp
#include "renderer.h"
void render_background(HDC hdc, Render_State &state, int colour)
{
unsigned int* pixel = static_cast<unsigned int*>(state.memory);
for (int y = 0; y < state.hight; ++y)
{
for (int x = 0; x < state.width; ++x)
{
*pixel++ = colour;
}
}
// render
StretchDIBits(hdc,
0, 0, state.width, state.hight,
0, 0, state.width, state.hight,
state.memory, &state.bitmap_info,
DIB_RGB_COLORS, SRCCOPY);
}
void clear_screen(HDC hdc, Render_State &state, int colour)
{
render_background(hdc, state, colour);
}
void draw_rect(HDC hdc, Render_State &state, int X, int Y, int X2, int Y2, int colour)
{
for (int y = Y; y < Y2; ++y)
{
// unsigned int* pixel = static_cast<unsigned int*>(state.memory);
size_t pixel = size_t(state.memory) + static_cast<size_t>(X) + static_cast<size_t>(y) * static_cast<size_t>(state.width);
for (int x = X; x < X2; ++x)
{
pixel += 0xf5500;
}
}
// render
StretchDIBits(hdc,
X, Y, X2, Y2,
X, Y, state.width, state.hight,
state.memory, &state.bitmap_info,
DIB_RGB_COLORS, SRCCOPY);
}
#include <Windows.h>
#include "renderer.h"
bool running = true;
Render_State render_state = {};
LRESULT CALLBACK windows_callback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_SIZE:
{
RECT rect;
GetClientRect(hwnd, &rect);
render_state.width = rect.right - rect.left;
render_state.hight = rect.bottom - rect.top;
int size = render_state.width * render_state.hight * sizeof(unsigned int);
if (render_state.memory) VirtualFree(render_state.memory, 0, MEM_RELEASE);
render_state.memory = VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
render_state.bitmap_info.bmiHeader.biSize = sizeof(render_state.bitmap_info.bmiHeader);
render_state.bitmap_info.bmiHeader.biWidth = render_state.width;
render_state. bitmap_info.bmiHeader.biHeight = render_state.hight;
render_state. bitmap_info.bmiHeader.biPlanes = 1;
render_state. bitmap_info.bmiHeader.biBitCount = 32;
render_state .bitmap_info.bmiHeader.biCompression = BI_RGB;
InvalidateRect(hwnd, NULL, TRUE);
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// render_background(hdc, render_state, white);
// clear_screen(hdc, render_state, 0xff5500);
draw_rect(hdc, render_state, 30, 50, 500, 500, 0xff5500);
EndPaint(hwnd, &ps);
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
//compile window
CHAR clsName[] = "test";
WNDCLASSA window_class = {};
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpszClassName = clsName;
window_class.lpfnWndProc = windows_callback;
//register class
if (!RegisterClassA(&window_class))
{
DWORD err = GetLastError();
return 1;
}
// create window
HWND window = CreateWindow(clsName, "game", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT,
CW_USEDEFAULT, 720, 360, 0, 0, hInstance, 0);
if (!window)
{
DWORD err = GetLastError();
return 1;
}
// Main message loop:
MSG message;
while (GetMessage(&message, nullptr, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
return 0;
}
Hello I am following along with the handmade hero game and I am getting this error: "Unhandled exception at 0x013B56B2 in win32_handmade.exe: 0xC000041D: An unhandled exception was encountered during a user callback."
I am not sure what is causing this because I have the exact same code that he had and when I build in a .bat I get no errors.
Visual Studio is saying that the error is occurring at line 62 which is where the second internal void is at, but the error has been occurring no matter what is there.
#include <windows.h>
#include <stdint.h>
#define internal static
#define local_persist static
#define global_variable static
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
// TODO(casey): This is a global for now.
global_variable bool Running;
global_variable BITMAPINFO BitmapInfo;
global_variable void *BitmapMemory;
global_variable int BitmapWidth;
global_variable int BitmapHeight;
global_variable int BytesPerPixel = 4;
internal void
RenderWeirdGradient(int BlueOffset, int GreenOffset)
{
int Width = BitmapWidth;
int Height = BitmapHeight;
int Pitch = Width * BytesPerPixel;
uint8 *Row = (uint8 *)BitmapMemory;
for (int Y = 0;
Y < BitmapHeight;
++Y)
{
uint32 *Pixel = (uint32 *)Row;
for (int X = 0;
X < BitmapWidth;
++X)
{
uint8 Blue = (X + BlueOffset);
uint8 Green = (Y + GreenOffset);
*Pixel++ = ((Green << 8) | Blue);
}
Row += Pitch;
}
}
internal void
Win32ResizeDIBSection(int Width, int Height)
{
// TODO(casey): Bulletproof this.
// Maybe don't free first, free after, then free first if that fails.
if (BitmapMemory)
{
VirtualFree(BitmapMemory, 0, MEM_RELEASE);
}
BitmapWidth = Width;
BitmapHeight = Height;
BitmapInfo.bmiHeader.biSize = sizeof(BitmapInfo.bmiHeader);
BitmapInfo.bmiHeader.biWidth = BitmapWidth;
BitmapInfo.bmiHeader.biHeight = -BitmapHeight;
BitmapInfo.bmiHeader.biPlanes = 1;
BitmapInfo.bmiHeader.biBitCount = 32;
BitmapInfo.bmiHeader.biCompression = BI_RGB;
// NOTE(casey): Thank you to Chris Hecker of Spy Party fame
// for clarifying the deal with StretchDIBits and BitBlt!
// No more DC for us.
int BitmapMemorySize = (BitmapWidth*BitmapHeight)*BytesPerPixel;
BitmapMemory = VirtualAlloc(0, BitmapMemorySize, MEM_COMMIT,
PAGE_READWRITE);
// TODO(casey): Probably clear this to black
}
internal void
Win32UpdateWindow(HDC DeviceContext, RECT *ClientRect, int X, int Y, int
Width, int Height)
{
int WindowWidth = ClientRect->right - ClientRect->left;
int WindowHeight = ClientRect->bottom - ClientRect->top;
StretchDIBits(DeviceContext,
/*
X, Y, Width, Height,
X, Y, Width, Height,
*/
0, 0, BitmapWidth, BitmapHeight,
0, 0, WindowWidth, WindowHeight,
BitmapMemory,
&BitmapInfo,
DIB_RGB_COLORS, SRCCOPY);
}
LRESULT CALLBACK
Win32MainWindowCallback(HWND Window,
UINT Message,
WPARAM WParam,
LPARAM LParam)
{
LRESULT Result = 0;
switch (Message)
{
case WM_SIZE:
{
RECT ClientRect;
GetClientRect(Window, &ClientRect);
int Width = ClientRect.right - ClientRect.left;
int Height = ClientRect.bottom - ClientRect.top;
Win32ResizeDIBSection(Width, Height);
} break;
case WM_CLOSE:
{
// TODO(casey): Handle this with a message to the user?
Running = false;
} break;
case WM_ACTIVATEAPP:
{
OutputDebugStringA("WM_ACTIVATEAPP\n");
} break;
case WM_DESTROY:
{
// TODO(casey): Handle this as an error - recreate window?
Running = false;
} break;
case WM_PAINT:
{
PAINTSTRUCT Paint;
HDC DeviceContext = BeginPaint(Window, &Paint);
int X = Paint.rcPaint.left;
int Y = Paint.rcPaint.top;
int Width = Paint.rcPaint.right - Paint.rcPaint.left;
int Height = Paint.rcPaint.bottom - Paint.rcPaint.top;
RECT ClientRect;
GetClientRect(Window, &ClientRect);
Win32UpdateWindow(DeviceContext, &ClientRect, X, Y, Width, Height);
EndPaint(Window, &Paint);
} break;
default:
{
// OutputDebugStringA("default\n");
Result = DefWindowProc(Window, Message, WParam, LParam);
} break;
}
return(Result);
}
int CALLBACK
WinMain(HINSTANCE Instance,
HINSTANCE PrevInstance,
LPSTR CommandLine,
int ShowCode)
{
WNDCLASS WindowClass = {};
// TODO(casey): Check if HREDRAW/VREDRAW/OWNDC still matter
WindowClass.lpfnWndProc = Win32MainWindowCallback;
WindowClass.hInstance = Instance;
// WindowClass.hIcon;
WindowClass.lpszClassName = "HandmadeHeroWindowClass";
if (RegisterClassA(&WindowClass))
{
HWND Window =
CreateWindowExA(
0,
WindowClass.lpszClassName,
"Handmade Hero",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
Instance,
0);
if (Window)
{
int XOffset = 0;
int YOffset = 0;
Running = true;
while (Running)
{
MSG Message;
while (PeekMessage(&Message, 0, 0, 0, PM_REMOVE))
{
if (Message.message == WM_QUIT)
{
Running = false;
}
TranslateMessage(&Message);
DispatchMessageA(&Message);
}
RenderWeirdGradient(XOffset, YOffset);
HDC DeviceContext = GetDC(Window);
RECT ClientRect;
GetClientRect(Window, &ClientRect);
int WindowWidth = ClientRect.right - ClientRect.left;
int WindowHeight = ClientRect.bottom - ClientRect.top;
Win32UpdateWindow(DeviceContext, &ClientRect, 0, 0, WindowWidth,
WindowHeight);
ReleaseDC(Window, DeviceContext);
++XOffset;
YOffset += 2;
}
}
else
{
// TODO(casey): Logging
}
}
else
{
// TODO(casey): Logging
}
return(0);
}
For who asked for the callstack here it is in better formating since it won't work well as a comment.
win32_handmade.exe!Win32ResizeDIBSection(int Width, int Height) Line 62 C++
win32_handmade.exe!WIN32MainWindowCallback(HWND__ * Window, unsigned int Message, unsigned int WParam, long LParam) Line 118 C++
[External Code]
win32_handmade.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * PrevInstance, char * CommandLine, int ShowCode) Line 184 C++
[External Code]
I'm trying to write a light Win32 program (OS is Windows XP SP 1) which should draw a 2D plot from a data set stored in a .csv file. I'm using Code::Blocks IDE with a MS Visual C++ Express 2008 compiler. Therefore, I'm also using SDK as a source of Windows headers and libraries.
Here's the code:
main.cpp
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include "daneWykres.h"
static TCHAR szWindowClass[] = _T("win32app");
static TCHAR szTitle[] = _T("Win32 Guided Tour Application");
POINT logicalUnitSize;
RECT ClientRect;
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wcex))
{
MessageBox(NULL,
_T("Call to RegisterClassEx failed!"),
_T("Win32 Guided Tour"),
0);
return 1;
}
hInst = hInstance;
logicalUnitSize.x = 560;
logicalUnitSize.y = 560;
HWND hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
100, 100,
logicalUnitSize.x, logicalUnitSize.y,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
{
MessageBox(NULL,
_T("Call to CreateWindow failed!"),
_T("Win32 Guided Tour"),
0);
return 1;
}
GetClientRect(hWnd,&ClientRect);
ShowWindow(hWnd,
nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
daneWykres* rysik = new daneWykres;
rysik->sczytaj(); // method putting data from CSV into a static array dane[][]
int iloscPunktow = rysik->getRozmiar(); // a getter for the array's size (number of points)
double minX = rysik->najmniejsza(0); // the lowest X coordinate
double minY = rysik->najmniejsza(1); // the lowest Y coordinate
double maxX = rysik->najwieksza(0); // the highest X coordinate
double maxY = rysik->najwieksza(1); // the highest Y coordinate
int lgXokna = 30; // top left corner X coordinate of the border rectangle
int lgYokna = 30; // top left corner Y coordinate of the border rectangle
int pdXokna = ClientRect.right - lgXokna; // bottom right corner X coordinate of the border rectangle
int pdYokna = ClientRect.bottom - lgYokna; // bottom right corner Y coordinate of the border rectangle
POINT punkt;
punkt.x = 0;
punkt.y = 0;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
HPEN hPenOld;
HPEN hSolidPen;
HPEN hBluePen;
COLORREF qLineColor;
qLineColor = RGB(0,0,0);
hSolidPen = CreatePen(PS_SOLID, 1, qLineColor);
qLineColor = RGB(0,0,255);
hBluePen = CreatePen(PS_SOLID, 2, qLineColor);
hPenOld = (HPEN)SelectObject(hdc, hSolidPen);
// the border rectangle
Rectangle(ps.hdc, lgXokna, lgYokna, pdXokna, pdYokna);
hPenOld = (HPEN)SelectObject(hdc, hBluePen);
// draws text every time a daneWykres constructor is called
if (rysik->getFlaga() == true) DrawText(ps.hdc, TEXT("Rysik istnieje"), -1, &ClientRect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
// 'the drawing loop'
for (int i = 0; i < iloscPunktow; i++)
{
punkt.x = lgXokna + ((pdXokna - lgXokna)*(rysik->getSingleData(i,0) - minX)/(maxX - minX));
punkt.y = pdYokna - ((pdYokna - lgYokna)*(rysik->getSingleData(i,1) - minY)/(maxY - minY));
if (i > 0) LineTo(ps.hdc,punkt.x,punkt.y);
MoveToEx(ps.hdc,punkt.x,punkt.y,NULL);
}
SelectObject(hdc,hPenOld);
DeleteObject(hSolidPen);
DeleteObject(hBluePen);
delete rysik;
EndPaint(hWnd, &ps);
break;
case WM_SIZE:
GetClientRect(hWnd,&ClientRect); // update the client area dimensions (and the plot shape with it)
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
daneWykres.h
#ifndef DANEWYKRES_H_INCLUDED
#define DANEWYKRES_H_INCLUDED
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;
class daneWykres {
static const int rozmiar = 3490; // number of data points
private:
bool flaga; // set to 'true' when constructor is called
ifstream file;
string value;
double wartosc;
double data[rozmiar][2];
public:
daneWykres();
~daneWykres();
double strToDouble(string);
void sczytaj(); // reading method adjusted to the contents of 'file.csv'
double najmniejsza(int indeksKolumny); // returns minimum value in a dataset
double najwieksza(int indeksKolumny); // returns maximum value in a dataset
int getRozmiar();
// returns an X or Y coordinate of a data point by row and column index, respectively
double getSingleData(int indeksWiersza, int indeksKolumny);
bool getFlaga();
};
#endif // DANEWYKRES_H_INCLUDED
daneWykres.cpp
#include "daneWykres.h"
daneWykres::daneWykres()
{
file.open("file.csv");
flaga = true;
}
daneWykres::~daneWykres()
{
file.close();
flaga = false;
}
double daneWykres::strToDouble(string zapis)
{
const char* c = zapis.c_str();
return atof(c);
}
void daneWykres::sczytaj()
{
int i = 0;
while (i < rozmiar)
{
getline(file,value,',');
wartosc = strToDouble(value);
data[i][0] = wartosc;
getline(file,value,',');
wartosc = strToDouble(value);
data[i][1] = wartosc;
getline(file,value,'\n');
i++;
}
}
double daneWykres::najmniejsza(int indeksKolumny)
{
double najmniejsza = data[0][indeksKolumny];
for (int i = 0; i < rozmiar; i++) najmniejsza = ((data[i][indeksKolumny] < najmniejsza) ? data[i][indeksKolumny] : najmniejsza);
return najmniejsza;
}
double daneWykres::najwieksza(int indeksKolumny)
{
double najwieksza = data[0][indeksKolumny];
for (int i = 0; i < rozmiar; i++) najwieksza = ((data[i][indeksKolumny] > najwieksza) ? data[i][indeksKolumny] : najwieksza);
return najwieksza;
}
int daneWykres::getRozmiar()
{
return rozmiar;
}
double daneWykres::getSingleData(int indeksWiersza, int indeksKolumny)
{
return data[indeksWiersza][indeksKolumny];
}
bool daneWykres::getFlaga()
{
return flaga;
}
Also, here's the link to a zip containing 'file.csv', just in case:
https://drive.google.com/file/d/1QHore2OJpQqaAzgxJEiVJIs0BFiSShFo/view?usp=sharing
Whenever I'm running the build via Code::Blocks (don't mind the title) on a machine with Win XP SP1, I see this:
https://imgur.com/SjOMu0G
And after resizing or minimizing a few times I see this:
https://imgur.com/mwzOf3w
I initially thought it was a matter of the object from daneWykres class being prematurely destroyed but the 'Rysik istnieje' string displays properly.
I've got hold of "Programming Windows" by C. Petzold recently but I'm still struggling to understand the basic concepts underlaying WinAPI and it shows.
So, the main question is: why are the black line rectangle and 'Rysik istnieje' string being drawn, yet the blue plot lines cease to be drawn at some point?
EDIT:
I've moved the delete rysik back to the end of the WM_PAINT message. I've separated the declarations and assignments and moved it all into the WM_PAINT message (along with allocation to and deallocation from rysik). This has seemingly helped limit the memory/resource leak.
I've moved the code for opening and reading the file into global functions. The resulting application seems to run smoother but I don't see any significant changes in the Task Manager compared to the earlier edit.
Here's what I have thus far:
main.cpp
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include <fstream>
#include <string>
#include <vector>
#include "daneWykres.h"
static TCHAR szWindowClass[] = _T("win32app");
static TCHAR szTitle[] = _T("My Application");
double data[3490][2];
POINT logicalUnitSize;
RECT ClientRect;
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
double strToDouble(string zapis)
{
const char* c = zapis.c_str();
return atof(c);
}
void sczytaj(ifstream& file, double (&data)[3490][2])
{
double wartosc;
string line;
unsigned int i = 0;
unsigned int pos, pos2;
while (getline(file,line))
{
pos = 0;
pos2 = line.find(',',pos);
wartosc = strToDouble(line.substr(pos,(pos2-pos)));
data[i][0] = wartosc;
pos = pos2 + 1;
pos2 = line.find(',',pos);
wartosc = strToDouble(line.substr(pos,(pos2-pos)));
data[i][1] = wartosc;
i++;
}
}
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wcex))
{
MessageBox(NULL,
_T("Call to RegisterClassEx failed!"),
_T("Win32 Guided Tour"),
0);
return 1;
}
hInst = hInstance;
ifstream file;
file.open("file.csv");
sczytaj(file,data);
file.close();
logicalUnitSize.x = 560;
logicalUnitSize.y = 560;
HWND hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
100, 100,
logicalUnitSize.x, logicalUnitSize.y,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
{
MessageBox(NULL,
_T("Call to CreateWindow failed!"),
_T("Win32 Guided Tour"),
0);
return 1;
}
GetClientRect(hWnd,&ClientRect);
ShowWindow(hWnd,
nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
daneWykres* rysik;
rysik = new daneWykres;
int iloscPunktow;
iloscPunktow = rysik->getRozmiar();
double minX, minY, maxX, maxY;
minX = rysik->najmniejsza(data, 0);
minY = rysik->najmniejsza(data, 1);
maxX = rysik->najwieksza(data, 0);
maxY = rysik->najwieksza(data, 1);
int lgXokna, lgYokna, pdXokna, pdYokna;
lgXokna = 30;
lgYokna = 30;
pdXokna = ClientRect.right - lgXokna;
pdYokna = ClientRect.bottom - lgYokna;
POINT punkt;
punkt.x = 0;
punkt.y = 0;
HPEN hPen;
HPEN hSolidPen;
HPEN hBluePen;
hSolidPen = CreatePen(PS_SOLID, 1, RGB(0,0,0));
hBluePen = CreatePen(PS_SOLID, 2, RGB(0,0,255));
hPen = (HPEN)SelectObject(hdc, hSolidPen);
Rectangle(ps.hdc, lgXokna, lgYokna, pdXokna, pdYokna);
SelectObject(hdc,hPen);
DeleteObject(hSolidPen);
hPen = (HPEN)SelectObject(hdc, hBluePen);
if (rysik) DrawText(ps.hdc, TEXT("Rysik istnieje"), -1, &ClientRect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
for (int i = 0; i < iloscPunktow; i++)
{
punkt.x = lgXokna + ((pdXokna - lgXokna)*(data[i][0] - minX)/(maxX - minX));
punkt.y = pdYokna - ((pdYokna - lgYokna)*(data[i][1] - minY)/(maxY - minY));
if (i > 0) LineTo(ps.hdc,punkt.x,punkt.y);
MoveToEx(ps.hdc,punkt.x,punkt.y,NULL);
}
SelectObject(hdc,hPen);
DeleteObject(hBluePen);
delete rysik;
EndPaint(hWnd, &ps);
break;
case WM_SIZE:
GetClientRect(hWnd,&ClientRect);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
daneWykres.h
#ifndef DANEWYKRES_H_INCLUDED
#define DANEWYKRES_H_INCLUDED
#include <fstream>
#include <string>
#include <cstdlib>
#include <vector>
using namespace std;
class daneWykres {
static const int rozmiar = 3490;
private:
public:
daneWykres();
~daneWykres();
double najmniejsza( double (&data)[3490][2], int indeksKolumny);
double najwieksza( double (&data)[3490][2], int indeksKolumny);
int getRozmiar();
};
#endif // DANEWYKRES_H_INCLUDED
daneWykres.cpp
#include "daneWykres.h"
daneWykres::daneWykres()
{
}
daneWykres::~daneWykres()
{
}
double daneWykres::najmniejsza( double (&data)[3490][2], int indeksKolumny)
{
double najmniejsza = data[0][indeksKolumny];
for (int i = 0; i < rozmiar; i++) najmniejsza = ((data[i][indeksKolumny] < najmniejsza) ? data[i][indeksKolumny] : najmniejsza);
return najmniejsza;
}
double daneWykres::najwieksza( double (&data)[3490][2], int indeksKolumny)
{
double najwieksza = data[0][indeksKolumny];
for (int i = 0; i < rozmiar; i++) najwieksza = ((data[i][indeksKolumny] > najwieksza) ? data[i][indeksKolumny] : najwieksza);
return najwieksza;
}
int daneWykres::getRozmiar()
{
return rozmiar;
}