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>
- #include <algorithm>
- using namespace std;
- struct Brushstroke;
- struct Eraserstroke;
- 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, int eraserSize) {
- if (!IsPointInClientRect(hWnd, x, y)) return;
- HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
- SelectObject(hdc, GetStockObject(NULL_PEN));
- SelectObject(hdc, hBrush);
- Ellipse(hdc, x - eraserSize, y - eraserSize, x + eraserSize, y + eraserSize);
- DeleteObject(hBrush);
- // Remove brushstrokes that intersect with the eraser
- storedBrushstrokes.erase(
- std::remove_if(storedBrushstrokes.begin(), storedBrushstrokes.end(),
- [x, y, eraserSize](const Brushstroke& stroke) {
- int dx = stroke.x - x;
- int dy = stroke.y - y;
- return (dx * dx + dy * dy) <= (eraserSize + stroke.size) * (eraserSize + stroke.size);
- }),
- storedBrushstrokes.end()
- );
- }
- // Update the ClearDrawing function
- void ClearDrawing(HWND hwnd) {
- RECT rect;
- GetClientRect(hwnd, &rect);
- HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
- // Clear both hdc AND memDC
- HDC hdc = GetDC(hwnd);
- FillRect(hdc, &rect, hBrush);
- FillRect(memDC, &rect, hBrush);
- ReleaseDC(hwnd, hdc);
- DeleteObject(hBrush);
- storedBrushstrokes.clear();
- InvalidateRect(hwnd, NULL, FALSE);
- 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, brushSize);
- 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));
- }
- // Update the window
- InvalidateRect(hWnd, NULL, FALSE);
- UpdateWindow(hWnd);
- }
- void SaveDrawing(const string& filename) {
- ofstream outFile(filename);
- if (outFile.is_open()) {
- outFile << storedBrushstrokes.size() << endl;
- for (const auto& brushstroke : storedBrushstrokes) {
- brushstroke.serialize(outFile);
- }
- outFile.close();
- }
- }
- void LoadDrawing(const string& filename) {
- ifstream inFile(filename);
- if (inFile.is_open()) {
- int numBrushstrokes;
- inFile >> numBrushstrokes;
- storedBrushstrokes.clear();
- storedBrushstrokes.reserve(numBrushstrokes);
- 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);
- }
- inFile.close();
- RedrawStrokes();
- }
- }
- 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);
- }
- }
- outFile << validBrushstrokes.size() << endl;
- for (const auto& brushstroke : validBrushstrokes) {
- brushstroke.serialize(outFile);
- }
- outFile.close();
- }
- }
- 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) { // 'C' key
- if (!(GetKeyState(VK_CONTROL) & 0x8000)) {
- ClearDrawing(hwnd);
- }
- }
- 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);
- }
- 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, brushSize);
- InvalidateRect(hwnd, NULL, 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.bottom, 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_CLOSE:
- // Sanitize and save before closing
- SanitizeAndSaveDrawing("drawing.txt");
- DestroyWindow(hwnd);
- break;
- case WM_DESTROY:
- DeleteDC(memDC);
- DeleteObject(memBitmap);
- PostQuitMessage(0);
- break;
- default:
- return DefWindowProc(hwnd, uMsg, wParam, lParam);
- }
- }
- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
- const wchar_t CLASS_NAME[] = L"PaintCanvasWindowClass";
- WNDCLASS wc = { };
- wc.lpfnWndProc = WindowProc;
- wc.hInstance = hInstance;
- wc.lpszClassName = CLASS_NAME;
- RegisterClass(&wc);
- HWND hwnd = CreateWindowEx(
- 0, // Optional window styles
- CLASS_NAME, // Window class
- L"Paint Canvas (P=Brush E=Eraser C=Clear +-=BrushSize Ctrl+S=Save Ctrl+O=Load)", // Window text
- WS_OVERLAPPEDWINDOW | WS_MAXIMIZE, // Window style
- // Size and position
- CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
- NULL, // Parent window
- NULL, // Menu
- hInstance, // Instance handle
- NULL // Additional application data
- );
- if (hwnd == NULL) {
- return 0;
- }
- ShowWindow(hwnd, SW_SHOWMAXIMIZED);
- // Run the message loop
- MSG msg = { };
- while (GetMessage(&msg, NULL, 0, 0)) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement