Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <vector>
- #include <windows.h>
- #include <fstream>
- #include <string>
- #include <commdlg.h>
- #include <codecvt>
- using namespace std;
- struct Brushstroke;
- struct Eraserstroke;
- std::vector<Brushstroke> undoBrushstrokes;
- std::vector<Eraserstroke> undoEraserstrokes;
- std::vector<Brushstroke> redoBrushstrokes;
- std::vector<Eraserstroke> redoEraserstrokes;
- HWND hWnd;
- HINSTANCE hInst;
- bool IsPointInClientRect(HWND hwnd, int x, int y) {
- RECT rect;
- GetClientRect(hwnd, &rect);
- return (x >= 0 && x < rect.right&& y >= 0 && y < rect.bottom);
- }
- struct Brushstroke {
- int x;
- int y;
- int size;
- COLORREF color;
- void serialize(ofstream& outFile) const {
- outFile << "B " << x << " " << y << " " << size << " " << (int)GetRValue(color) << " " << (int)GetGValue(color) << " " << (int)GetBValue(color) << endl;
- }
- };
- struct Eraserstroke {
- int x;
- int y;
- int size;
- COLORREF color;
- void serialize(ofstream& outFile) const {
- outFile << "E " << x << " " << y << " " << size << endl;
- }
- };
- std::vector<Brushstroke> storedBrushstrokes;
- std::vector<Eraserstroke> storedEraserstrokes;
- void RedrawStrokes();
- void SaveDrawing(const string& filename);
- void LoadDrawing(const string& filename);
- POINT previousPoint;
- HDC hdc, memDC;
- HBITMAP memBitmap;
- bool isPaintbrushSelected = true;
- bool isDrawing = false;
- bool isErasing = false;
- bool isClearing = false;
- bool isEraserSelected = false;
- int brushSize = 10;
- void DrawBrush(HDC hdc, int x, int y) {
- if (!IsPointInClientRect(hWnd, x, y)) return;
- HGDIOBJ originalBrush = SelectObject(hdc, CreateSolidBrush(RGB(255, 0, 0)));
- HGDIOBJ originalPen = SelectObject(hdc, GetStockObject(NULL_PEN));
- Ellipse(hdc, x - brushSize, y - brushSize, x + brushSize, y + brushSize);
- DeleteObject(SelectObject(hdc, originalBrush));
- DeleteObject(SelectObject(hdc, originalPen));
- }
- void Erase(HDC hdc, int x, int y) {
- if (!IsPointInClientRect(hWnd, x, y)) return;
- HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
- SelectObject(hdc, GetStockObject(NULL_PEN));
- SelectObject(hdc, hBrush);
- Ellipse(hdc, x - brushSize, y - brushSize, x + brushSize, y + brushSize);
- DeleteObject(hBrush);
- }
- void ClearDrawing(HDC hdc, int width, int height, HWND hwnd) {
- RECT rect = { 0, 0, width, height };
- HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
- // Clear both hdc AND memDC
- FillRect(hdc, &rect, hBrush);
- FillRect(memDC, &rect, hBrush); // Clear memory DC
- DeleteObject(hBrush);
- storedBrushstrokes.clear();
- storedEraserstrokes.clear();
- UpdateWindow(hwnd);
- }
- void DrawSmoothBrush(HDC hdc, int x, int y) {
- if (isDrawing && (isPaintbrushSelected || isEraserSelected)) {
- int numPoints = 3;
- POINT currentPoint = { x, y };
- for (int i = 1; i <= numPoints; i++) {
- float t = (float)i / (float)numPoints;
- int smoothX = (int)(previousPoint.x + t * (currentPoint.x - previousPoint.x));
- int smoothY = (int)(previousPoint.y + t * (currentPoint.y - previousPoint.y));
- if (isPaintbrushSelected) {
- DrawBrush(hdc, smoothX, smoothY);
- Brushstroke newBrushstroke;
- newBrushstroke.x = smoothX;
- newBrushstroke.y = smoothY;
- newBrushstroke.size = brushSize;
- newBrushstroke.color = RGB(255, 0, 0);
- storedBrushstrokes.push_back(newBrushstroke);
- }
- else if (isEraserSelected) {
- Erase(hdc, smoothX, smoothY);
- Eraserstroke newEraserstroke;
- newEraserstroke.x = smoothX;
- newEraserstroke.y = smoothY;
- newEraserstroke.size = brushSize;
- newEraserstroke.color = RGB(255, 255, 255);
- storedEraserstrokes.push_back(newEraserstroke);
- }
- }
- previousPoint = currentPoint;
- }
- }
- void RedrawStrokes() {
- // Clear the entire drawing area
- RECT rect;
- GetClientRect(hWnd, &rect);
- HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
- FillRect(memDC, &rect, hBrush);
- DeleteObject(hBrush);
- // Redraw all brushstrokes
- for (const auto& brushstroke : storedBrushstrokes) {
- HGDIOBJ originalBrush = SelectObject(memDC, CreateSolidBrush(brushstroke.color));
- HGDIOBJ originalPen = SelectObject(memDC, GetStockObject(NULL_PEN));
- Ellipse(memDC, brushstroke.x - brushstroke.size, brushstroke.y - brushstroke.size,
- brushstroke.x + brushstroke.size, brushstroke.y + brushstroke.size);
- DeleteObject(SelectObject(memDC, originalBrush));
- DeleteObject(SelectObject(memDC, originalPen));
- }
- // Redraw all eraser strokes
- for (const auto& erasepoint : storedEraserstrokes) {
- HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
- SelectObject(memDC, GetStockObject(NULL_PEN));
- SelectObject(memDC, hBrush);
- Ellipse(memDC, erasepoint.x - erasepoint.size, erasepoint.y - erasepoint.size,
- erasepoint.x + erasepoint.size, erasepoint.y + erasepoint.size);
- DeleteObject(hBrush);
- }
- }
- void SaveDrawing(const string& filename) {
- ofstream outFile(filename);
- if (outFile.is_open()) {
- outFile << storedBrushstrokes.size() << " " << storedEraserstrokes.size() << endl;
- for (const auto& brushstroke : storedBrushstrokes) {
- brushstroke.serialize(outFile);
- }
- for (const auto& eraserstroke : storedEraserstrokes) {
- eraserstroke.serialize(outFile);
- }
- outFile.close();
- }
- }
- void LoadDrawing(const string& filename) {
- ifstream inFile(filename);
- if (inFile.is_open()) {
- int numBrushstrokes, numEraserstrokes;
- inFile >> numBrushstrokes >> numEraserstrokes;
- storedBrushstrokes.clear();
- storedEraserstrokes.clear();
- storedBrushstrokes.reserve(numBrushstrokes);
- storedEraserstrokes.reserve(numEraserstrokes);
- for (int i = 0; i < numBrushstrokes; ++i) {
- char type;
- Brushstroke brushstroke;
- int r, g, b;
- inFile >> type >> brushstroke.x >> brushstroke.y >> brushstroke.size >> r >> g >> b;
- brushstroke.color = RGB(r, g, b);
- storedBrushstrokes.push_back(brushstroke);
- }
- for (int i = 0; i < numEraserstrokes; ++i) {
- char type;
- Eraserstroke eraserstroke;
- inFile >> type >> eraserstroke.x >> eraserstroke.y >> eraserstroke.size;
- storedEraserstrokes.push_back(eraserstroke);
- }
- inFile.close();
- // Clear memDC before redrawing
- RECT rect;
- GetClientRect(hWnd, &rect);
- HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
- FillRect(memDC, &rect, hBrush);
- DeleteObject(hBrush);
- RedrawStrokes();
- InvalidateRect(hWnd, NULL, TRUE);
- }
- }
- void SaveDrawingDialog(HWND hwnd) {
- OPENFILENAME ofn;
- wchar_t szFileName[MAX_PATH] = L"";
- ZeroMemory(&ofn, sizeof(ofn));
- ofn.lStructSize = sizeof(ofn);
- ofn.hwndOwner = hwnd;
- ofn.lpstrFilter = L"Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
- ofn.lpstrFile = szFileName;
- ofn.nMaxFile = MAX_PATH;
- ofn.Flags = OFN_EXPLORER | OFN_OVERWRITEPROMPT;
- ofn.lpstrDefExt = L"txt";
- if (GetSaveFileName(&ofn)) {
- std::wstring_convert < std::codecvt_utf8 < wchar_t>> converter;
- std::string filenameStr = converter.to_bytes(szFileName);
- SaveDrawing(filenameStr);
- MessageBox(hwnd, L"Save Complete", L"Doodle App", MB_OK | MB_ICONINFORMATION);
- }
- }
- void LoadDrawingDialog(HWND hwnd) {
- OPENFILENAME ofn;
- wchar_t szFileName[MAX_PATH] = L"";
- ZeroMemory(&ofn, sizeof(ofn));
- ofn.lStructSize = sizeof(ofn);
- ofn.hwndOwner = hwnd;
- ofn.lpstrFilter = L"Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
- ofn.lpstrFile = szFileName;
- ofn.nMaxFile = MAX_PATH;
- ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
- if (GetOpenFileName(&ofn)) {
- std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
- std::string filenameStr = converter.to_bytes(szFileName);
- LoadDrawing(filenameStr);
- }
- }
- void SanitizeAndSaveDrawing(const string& filename) {
- ofstream outFile(filename);
- if (outFile.is_open()) {
- RECT rect;
- GetClientRect(hWnd, &rect);
- vector<Brushstroke> validBrushstrokes;
- for (const auto& brushstroke : storedBrushstrokes) {
- if (IsPointInClientRect(hWnd, brushstroke.x, brushstroke.y)) {
- validBrushstrokes.push_back(brushstroke);
- }
- }
- vector<Eraserstroke> validEraserstrokes;
- for (const auto& eraserstroke : storedEraserstrokes) {
- if (IsPointInClientRect(hWnd, eraserstroke.x, eraserstroke.y)) {
- validEraserstrokes.push_back(eraserstroke);
- }
- }
- outFile << validBrushstrokes.size() << " " << validEraserstrokes.size() << endl;
- for (const auto& brushstroke : validBrushstrokes) {
- brushstroke.serialize(outFile);
- }
- for (const auto& eraserstroke : validEraserstrokes) {
- eraserstroke.serialize(outFile);
- }
- outFile.close();
- }
- }
- void UndoLastAction() {
- if (!storedBrushstrokes.empty() || !storedEraserstrokes.empty()) {
- // Move last actions to undo stacks
- if (!storedBrushstrokes.empty()) {
- undoBrushstrokes.push_back(storedBrushstrokes.back());
- storedBrushstrokes.pop_back();
- }
- if (!storedEraserstrokes.empty()) {
- undoEraserstrokes.push_back(storedEraserstrokes.back());
- storedEraserstrokes.pop_back();
- }
- // Redraw from stored strokes
- RedrawStrokes();
- InvalidateRect(hWnd, NULL, TRUE);
- }
- }
- void RedoLastAction() {
- if (!undoBrushstrokes.empty() || !undoEraserstrokes.empty()) {
- // Move actions from undo stacks back to stored strokes
- if (!undoBrushstrokes.empty()) {
- storedBrushstrokes.push_back(undoBrushstrokes.back());
- undoBrushstrokes.pop_back();
- }
- if (!undoEraserstrokes.empty()) {
- storedEraserstrokes.push_back(undoEraserstrokes.back());
- undoEraserstrokes.pop_back();
- }
- // Redraw from stored strokes
- RedrawStrokes();
- InvalidateRect(hWnd, NULL, TRUE);
- }
- }
- LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
- switch (uMsg) {
- case WM_CREATE:
- {
- hWnd = hwnd;
- RECT rect;
- GetClientRect(hwnd, &rect);
- HDC hdc = GetDC(hwnd);
- memDC = CreateCompatibleDC(hdc);
- memBitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top);
- SelectObject(memDC, memBitmap);
- ReleaseDC(hwnd, hdc);
- HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
- FillRect(memDC, &rect, hBrush);
- DeleteObject(hBrush);
- isPaintbrushSelected = true;
- LoadDrawing("drawing.txt");
- }
- break;
- case WM_KEYDOWN:
- if (wParam == VK_ADD) {
- brushSize += 5;
- }
- else if (wParam == VK_SUBTRACT) {
- if (brushSize > 5) {
- brushSize -= 5;
- }
- }
- else if (wParam == 0x43) {
- if (!(GetKeyState(VK_CONTROL) & 0x8000)) {
- isClearing = true;
- }
- }
- else if (wParam == 0x50) {
- isPaintbrushSelected = true;
- isEraserSelected = false;
- }
- else if (wParam == 0x45) {
- isEraserSelected = true;
- isPaintbrushSelected = false;
- }
- else if (wParam == 'S' && (GetKeyState(VK_CONTROL) & 0x8000)) {
- SaveDrawingDialog(hwnd);
- }
- else if (wParam == 'O' && (GetKeyState(VK_CONTROL) & 0x8000)) {
- LoadDrawingDialog(hwnd);
- }
- // Ctrl+Z for Undo
- else if (wParam == 'Z' && (GetKeyState(VK_CONTROL) & 0x8000)) {
- UndoLastAction();
- }
- // Ctrl+Y for Redo
- else if (wParam == 'Y' && (GetKeyState(VK_CONTROL) & 0x8000)) {
- RedoLastAction();
- }
- break;
- case WM_LBUTTONDOWN:
- if (isPaintbrushSelected || isEraserSelected) {
- previousPoint.x = LOWORD(lParam);
- previousPoint.y = HIWORD(lParam);
- }
- isDrawing = true;
- break;
- case WM_LBUTTONUP:
- isDrawing = false;
- break;
- case WM_MOUSEMOVE:
- if (isDrawing && isPaintbrushSelected) {
- int x = LOWORD(lParam);
- int y = HIWORD(lParam);
- DrawSmoothBrush(memDC, x, y);
- InvalidateRect(hwnd, NULL, FALSE);
- if (IsPointInClientRect(hWnd, x, y)) {
- Brushstroke newBrushstroke;
- newBrushstroke.x = x;
- newBrushstroke.y = y;
- newBrushstroke.size = brushSize;
- newBrushstroke.color = RGB(255, 0, 0);
- storedBrushstrokes.push_back(newBrushstroke);
- }
- }
- else if (isDrawing && isEraserSelected) {
- int x = LOWORD(lParam);
- int y = HIWORD(lParam);
- Erase(memDC, x, y);
- InvalidateRect(hwnd, NULL, FALSE);
- if (IsPointInClientRect(hWnd, x, y)) {
- Eraserstroke newEraserStroke;
- newEraserStroke.x = x;
- newEraserStroke.y = y;
- newEraserStroke.size = brushSize;
- newEraserStroke.color = RGB(255, 255, 255);
- storedEraserstrokes.push_back(newEraserStroke);
- }
- }
- else if (isClearing) {
- HDC hdc = GetDC(hwnd);
- RECT rect;
- GetClientRect(hwnd, &rect);
- ClearDrawing(hdc, rect.right, rect.bottom, hwnd);
- ReleaseDC(hwnd, hdc);
- isClearing = false;
- }
- break;
- case WM_SIZE:
- {
- static bool isMinimized = false;
- if (wParam == SIZE_MINIMIZED)
- {
- isMinimized = true;
- }
- else if (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED)
- {
- if (isMinimized)
- {
- isMinimized = false;
- }
- InvalidateRect(hwnd, NULL, TRUE);
- }
- RECT rect;
- GetClientRect(hwnd, &rect);
- if (memDC)
- {
- DeleteDC(memDC);
- DeleteObject(memBitmap);
- }
- HDC hdc = GetDC(hwnd);
- memDC = CreateCompatibleDC(hdc);
- memBitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top);
- SelectObject(memDC, memBitmap);
- ReleaseDC(hwnd, hdc);
- HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
- FillRect(memDC, &rect, hBrush);
- DeleteObject(hBrush);
- RedrawStrokes();
- }
- break;
- case WM_PAINT:
- {
- PAINTSTRUCT ps;
- HDC hdc = BeginPaint(hwnd, &ps);
- BitBlt(hdc, 0, 0, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top, memDC, 0, 0, SRCCOPY);
- EndPaint(hwnd, &ps);
- }
- break;
- case WM_SETCURSOR:
- if (LOWORD(lParam) == HTCLIENT) {
- SetCursor(LoadCursor(NULL, IDC_ARROW));
- return TRUE;
- }
- break;
- case WM_DESTROY:
- SanitizeAndSaveDrawing("drawing.txt");
- DeleteDC(memDC);
- DeleteObject(memBitmap);
- PostQuitMessage(0);
- return 0;
- default:
- return DefWindowProc(hwnd, uMsg, wParam, lParam);
- }
- return 0;
- }
- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
- const wchar_t CLASS_NAME[] = L"DoodleAppClass";
- WNDCLASS wc = { };
- wc.lpfnWndProc = WindowProc;
- wc.hInstance = hInstance;
- wc.lpszClassName = CLASS_NAME;
- //hInst = hInstance;
- RegisterClass(&wc);
- HWND hwnd = CreateWindowEx(
- 0,
- CLASS_NAME,
- L"Doodle App",
- WS_OVERLAPPEDWINDOW | WS_MAXIMIZE,
- CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
- NULL,
- NULL,
- hInstance,
- NULL
- );
- hdc = GetDC(hwnd);
- if (hwnd == NULL) {
- return 0;
- }
- ShowWindow(hwnd, SW_SHOWMAXIMIZED);
- MSG msg = {};
- while (GetMessage(&msg, NULL, 0, 0)) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement