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 IsPointInCircle(int px, int py, int cx, int cy, int radius) {
- int distSq = (px - cx) * (px - cx) + (py - cy) * (py - cy);
- return distSq <= radius * radius;
- }
- 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;
- unsigned long timestamp;
- void serialize(ofstream& outFile) const {
- outFile << "B " << x << " " << y << " " << size << " "
- << (int)GetRValue(color) << " "
- << (int)GetGValue(color) << " "
- << (int)GetBValue(color) << " "
- << timestamp << endl;
- }
- };
- struct Eraserstroke {
- int x;
- int y;
- int size;
- COLORREF color;
- unsigned long timestamp;
- void serialize(ofstream& outFile) const {
- outFile << "E " << x << " " << y << " " << size << " "
- << (int)GetRValue(color) << " "
- << (int)GetGValue(color) << " "
- << (int)GetBValue(color) << " "
- << timestamp << 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;
- unsigned long currentTimestamp = 0;
- void DrawBrush(HDC hdc, int x, int y, COLORREF color, unsigned long timestamp) {
- if (!IsPointInClientRect(hWnd, x, y)) return;
- for (const auto& eraserstroke : storedEraserstrokes) {
- if (eraserstroke.timestamp < timestamp &&
- IsPointInCircle(x, y, eraserstroke.x, eraserstroke.y, eraserstroke.size + brushSize)) {
- return; // Don't draw if erased
- }
- }
- HGDIOBJ originalBrush = SelectObject(hdc, CreateSolidBrush(color));
- 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, COLORREF color = RGB(255, 255, 255)) {
- if (!IsPointInClientRect(hWnd, x, y)) return;
- Eraserstroke newEraserstroke;
- newEraserstroke.x = x;
- newEraserstroke.y = y;
- newEraserstroke.size = eraserSize;
- newEraserstroke.color = color;
- newEraserstroke.timestamp = currentTimestamp++;
- storedEraserstrokes.push_back(newEraserstroke);
- RedrawStrokes();
- }
- void ClearDrawing(HWND hwnd) {
- RECT rect;
- GetClientRect(hwnd, &rect);
- HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
- HDC hdc = GetDC(hwnd);
- FillRect(hdc, &rect, hBrush);
- FillRect(memDC, &rect, hBrush);
- ReleaseDC(hwnd, hdc);
- DeleteObject(hBrush);
- storedBrushstrokes.clear();
- storedEraserstrokes.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) {
- unsigned long timestamp = currentTimestamp++;
- DrawBrush(hdc, smoothX, smoothY, RGB(255, 0, 0), timestamp);
- Brushstroke newBrushstroke;
- newBrushstroke.x = smoothX;
- newBrushstroke.y = smoothY;
- newBrushstroke.size = brushSize;
- newBrushstroke.color = RGB(255, 0, 0);
- newBrushstroke.timestamp = timestamp;
- storedBrushstrokes.push_back(newBrushstroke);
- }
- else if (isEraserSelected) {
- Erase(hdc, smoothX, smoothY, brushSize);
- }
- }
- previousPoint = currentPoint;
- }
- }
- void RedrawStrokes() {
- RECT rect;
- GetClientRect(hWnd, &rect);
- HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
- FillRect(memDC, &rect, hBrush);
- DeleteObject(hBrush);
- for (const auto& brushstroke : storedBrushstrokes) {
- DrawBrush(memDC, brushstroke.x, brushstroke.y, brushstroke.color, brushstroke.timestamp);
- }
- for (const auto& eraserstroke : storedEraserstrokes) {
- Erase(memDC, eraserstroke.x, eraserstroke.y, eraserstroke.size, eraserstroke.color);
- }
- InvalidateRect(hWnd, NULL, FALSE);
- UpdateWindow(hWnd);
- }
- 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()) {
- ClearDrawing(hWnd);
- int numBrushstrokes, numEraserstrokes;
- inFile >> numBrushstrokes >> numEraserstrokes;
- storedBrushstrokes.clear();
- storedBrushstrokes.reserve(numBrushstrokes);
- storedEraserstrokes.clear();
- 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.timestamp;
- brushstroke.color = RGB(r, g, b);
- storedBrushstrokes.push_back(brushstroke);
- }
- for (int i = 0; i < numEraserstrokes; ++i) {
- char type;
- Eraserstroke eraserstroke;
- int r, g, b;
- inFile >> type >> eraserstroke.x >> eraserstroke.y >> eraserstroke.size >> r >> g >> b >> eraserstroke.timestamp;
- eraserstroke.color = RGB(r, g, b);
- storedEraserstrokes.push_back(eraserstroke);
- }
- inFile.close();
- RedrawStrokes();
- }
- currentTimestamp = 0;
- for (const auto& brushstroke : storedBrushstrokes)
- {
- if (brushstroke.timestamp > currentTimestamp) {
- currentTimestamp = brushstroke.timestamp;
- }
- }
- for (const auto& eraserstroke : storedEraserstrokes) {
- if (eraserstroke.timestamp > currentTimestamp) {
- currentTimestamp = eraserstroke.timestamp;
- }
- }
- currentTimestamp++;
- }
- 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();
- }
- }
- 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);
- newBrushstroke.timestamp = currentTimestamp++;
- storedBrushstrokes.push_back(newBrushstroke);
- }
- }
- else if (isDrawing && isEraserSelected) {
- int x = LOWORD(lParam);
- int y = HIWORD(lParam);
- Erase(memDC, x, y, brushSize);
- Eraserstroke newEraserstroke;
- newEraserstroke.x = x;
- newEraserstroke.y = y;
- newEraserstroke.size = brushSize;
- newEraserstroke.color = RGB(255, 255, 255);
- newEraserstroke.timestamp = currentTimestamp++;
- storedEraserstrokes.push_back(newEraserstroke);
- }
- 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);
- RECT rect;
- GetClientRect(hwnd, &rect);
- BitBlt(hdc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, memDC, 0, 0, SRCCOPY);
- EndPaint(hwnd, &ps);
- }
- break;
- case WM_DESTROY:
- SanitizeAndSaveDrawing("drawing.txt");
- DeleteObject(memBitmap);
- DeleteDC(memDC);
- PostQuitMessage(0);
- break;
- default:
- return DefWindowProc(hwnd, uMsg, wParam, lParam);
- }
- return 0;
- }
- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
- WNDCLASSEX wc = {};
- wc.cbSize = sizeof(wc);
- wc.style = CS_HREDRAW | CS_VREDRAW;
- wc.lpfnWndProc = WindowProc;
- wc.hInstance = hInstance;
- wc.hCursor = LoadCursor(NULL, IDC_ARROW);
- wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
- wc.lpszClassName = L"MyClass";
- RegisterClassEx(&wc);
- HWND hwnd = CreateWindowEx(
- 0, L"MyClass", L"Doodle App",
- WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
- NULL, NULL, hInstance, NULL
- );
- if (hwnd == NULL) {
- return 0;
- }
- ShowWindow(hwnd, nCmdShow);
- UpdateWindow(hwnd);
- MSG msg;
- while (GetMessage(&msg, NULL, 0, 0)) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement