Advertisement
alien_fx_fiend

Memory DC PaintCanvas Projectv13 FINAL (New Approach to saving Eraser Srokes)

Jul 14th, 2024 (edited)
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.10 KB | None | 0 0
  1. #include <vector>
  2. #include <windows.h>
  3. #include <fstream>
  4. #include <string>
  5. #include <commdlg.h>
  6. #include <codecvt>
  7. #include <algorithm>
  8.  
  9. using namespace std;
  10.  
  11. struct Brushstroke;
  12. struct Eraserstroke;
  13. HWND hWnd;
  14. HINSTANCE hInst;
  15. bool IsPointInClientRect(HWND hwnd, int x, int y) {
  16.     RECT rect;
  17.     GetClientRect(hwnd, &rect);
  18.     return (x >= 0 && x < rect.right&& y >= 0 && y < rect.bottom);
  19. }
  20.  
  21. struct Brushstroke {
  22.     int x;
  23.     int y;
  24.     int size;
  25.     COLORREF color;
  26.     void serialize(ofstream& outFile) const {
  27.         outFile << "B " << x << " " << y << " " << size << " "
  28.             << (int)GetRValue(color) << " "
  29.             << (int)GetGValue(color) << " "
  30.             << (int)GetBValue(color) << endl;
  31.     }
  32. };
  33.  
  34. struct Eraserstroke {
  35.     int x;
  36.     int y;
  37.     int size;
  38.     COLORREF color; // Added color property to Eraserstroke
  39.     void serialize(ofstream& outFile) const {
  40.         outFile << "E " << x << " " << y << " " << size << " "
  41.             << (int)GetRValue(color) << " "
  42.             << (int)GetGValue(color) << " "
  43.             << (int)GetBValue(color) << endl;
  44.     }
  45. };
  46.  
  47. std::vector<Brushstroke> storedBrushstrokes;
  48. std::vector<Eraserstroke> storedEraserstrokes;
  49.  
  50. void RedrawStrokes();
  51. void SaveDrawing(const string& filename);
  52. void LoadDrawing(const string& filename);
  53.  
  54. POINT previousPoint;
  55. HDC hdc, memDC;
  56. HBITMAP memBitmap;
  57.  
  58. bool isPaintbrushSelected = true;
  59. bool isDrawing = false;
  60. bool isErasing = false;
  61. bool isClearing = false;
  62. bool isEraserSelected = false;
  63. int brushSize = 10;
  64.  
  65. void DrawBrush(HDC hdc, int x, int y, COLORREF color) {
  66.     if (!IsPointInClientRect(hWnd, x, y)) return;
  67.     HGDIOBJ originalBrush = SelectObject(hdc, CreateSolidBrush(color));
  68.     HGDIOBJ originalPen = SelectObject(hdc, GetStockObject(NULL_PEN));
  69.     Ellipse(hdc, x - brushSize, y - brushSize, x + brushSize, y + brushSize);
  70.     DeleteObject(SelectObject(hdc, originalBrush));
  71.     DeleteObject(SelectObject(hdc, originalPen));
  72. }
  73.  
  74. void Erase(HDC hdc, int x, int y, int eraserSize, COLORREF color = RGB(255, 255, 255)) {
  75.     if (!IsPointInClientRect(hWnd, x, y)) return;
  76.     HBRUSH hBrush = CreateSolidBrush(color);
  77.     SelectObject(hdc, GetStockObject(NULL_PEN));
  78.     SelectObject(hdc, hBrush);
  79.     Ellipse(hdc, x - eraserSize, y - eraserSize, x + eraserSize, y + eraserSize);
  80.     DeleteObject(hBrush);
  81. }
  82.  
  83. void ClearDrawing(HWND hwnd) {
  84.     RECT rect;
  85.     GetClientRect(hwnd, &rect);
  86.     HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
  87.     HDC hdc = GetDC(hwnd);
  88.     FillRect(hdc, &rect, hBrush);
  89.     FillRect(memDC, &rect, hBrush);
  90.     ReleaseDC(hwnd, hdc);
  91.     DeleteObject(hBrush);
  92.     storedBrushstrokes.clear();
  93.     storedEraserstrokes.clear(); // Also clear eraser strokes
  94.     InvalidateRect(hwnd, NULL, FALSE);
  95.     UpdateWindow(hwnd);
  96. }
  97.  
  98. void DrawSmoothBrush(HDC hdc, int x, int y) {
  99.     if (isDrawing && (isPaintbrushSelected || isEraserSelected)) {
  100.         int numPoints = 3;
  101.         POINT currentPoint = { x, y };
  102.         for (int i = 1; i <= numPoints; i++) {
  103.             float t = (float)i / (float)numPoints;
  104.             int smoothX = (int)(previousPoint.x + t * (currentPoint.x - previousPoint.x));
  105.             int smoothY = (int)(previousPoint.y + t * (currentPoint.y - previousPoint.y));
  106.             if (isPaintbrushSelected) {
  107.                 DrawBrush(hdc, smoothX, smoothY, RGB(255, 0, 0)); // Use DrawBrush for brushstrokes
  108.                 Brushstroke newBrushstroke;
  109.                 newBrushstroke.x = smoothX;
  110.                 newBrushstroke.y = smoothY;
  111.                 newBrushstroke.size = brushSize;
  112.                 newBrushstroke.color = RGB(255, 0, 0);
  113.                 storedBrushstrokes.push_back(newBrushstroke);
  114.             }
  115.             else if (isEraserSelected) {
  116.                 Erase(hdc, smoothX, smoothY, brushSize);
  117.                 Eraserstroke newEraserstroke;
  118.                 newEraserstroke.x = smoothX;
  119.                 newEraserstroke.y = smoothY;
  120.                 newEraserstroke.size = brushSize;
  121.                 newEraserstroke.color = RGB(255, 255, 255);
  122.                 storedEraserstrokes.push_back(newEraserstroke);
  123.             }
  124.         }
  125.         previousPoint = currentPoint;
  126.     }
  127. }
  128.  
  129. void RedrawStrokes() {
  130.     for (const auto& brushstroke : storedBrushstrokes) {
  131.         DrawBrush(memDC, brushstroke.x, brushstroke.y, brushstroke.color);
  132.     }
  133.  
  134.     for (const auto& eraserstroke : storedEraserstrokes) {
  135.         Erase(memDC, eraserstroke.x, eraserstroke.y, eraserstroke.size, eraserstroke.color);
  136.     }
  137.  
  138.     InvalidateRect(hWnd, NULL, FALSE);
  139.     UpdateWindow(hWnd);
  140. }
  141.  
  142. void SaveDrawing(const string& filename) {
  143.     ofstream outFile(filename);
  144.     if (outFile.is_open()) {
  145.         // Save both brushstrokes and eraserstrokes
  146.         outFile << storedBrushstrokes.size() << " " << storedEraserstrokes.size() << endl;
  147.  
  148.         for (const auto& brushstroke : storedBrushstrokes) {
  149.             brushstroke.serialize(outFile);
  150.         }
  151.  
  152.         for (const auto& eraserstroke : storedEraserstrokes) {
  153.             eraserstroke.serialize(outFile);
  154.         }
  155.  
  156.         outFile.close();
  157.     }
  158. }
  159.  
  160. void LoadDrawing(const string& filename) {
  161.     ifstream inFile(filename);
  162.     if (inFile.is_open()) {
  163.         int numBrushstrokes, numEraserstrokes;
  164.         inFile >> numBrushstrokes >> numEraserstrokes;
  165.  
  166.         storedBrushstrokes.clear();
  167.         storedBrushstrokes.reserve(numBrushstrokes);
  168.  
  169.         storedEraserstrokes.clear();
  170.         storedEraserstrokes.reserve(numEraserstrokes);
  171.  
  172.         for (int i = 0; i < numBrushstrokes; ++i) {
  173.             char type;
  174.             Brushstroke brushstroke;
  175.             int r, g, b;
  176.             inFile >> type >> brushstroke.x >> brushstroke.y >> brushstroke.size >> r >> g >> b;
  177.             brushstroke.color = RGB(r, g, b);
  178.             storedBrushstrokes.push_back(brushstroke);
  179.         }
  180.  
  181.         for (int i = 0; i < numEraserstrokes; ++i) {
  182.             char type;
  183.             Eraserstroke eraserstroke;
  184.             int r, g, b;
  185.             inFile >> type >> eraserstroke.x >> eraserstroke.y >> eraserstroke.size >> r >> g >> b;
  186.             eraserstroke.color = RGB(r, g, b);
  187.             storedEraserstrokes.push_back(eraserstroke);
  188.         }
  189.  
  190.         inFile.close();
  191.         RedrawStrokes();
  192.     }
  193. }
  194.  
  195. void SaveDrawingDialog(HWND hwnd) {
  196.     OPENFILENAME ofn;
  197.     wchar_t szFileName[MAX_PATH] = L"";
  198.  
  199.     ZeroMemory(&ofn, sizeof(ofn));
  200.     ofn.lStructSize = sizeof(ofn);
  201.     ofn.hwndOwner = hwnd;
  202.     ofn.lpstrFilter = L"Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
  203.     ofn.lpstrFile = szFileName;
  204.     ofn.nMaxFile = MAX_PATH;
  205.     ofn.Flags = OFN_EXPLORER | OFN_OVERWRITEPROMPT;
  206.     ofn.lpstrDefExt = L"txt";
  207.  
  208.     if (GetSaveFileName(&ofn)) {
  209.         std::wstring_convert < std::codecvt_utf8 < wchar_t>> converter;
  210.         std::string filenameStr = converter.to_bytes(szFileName);
  211.         SaveDrawing(filenameStr);
  212.         MessageBox(hwnd, L"Save Complete", L"Doodle App", MB_OK | MB_ICONINFORMATION);
  213.     }
  214. }
  215.  
  216. void LoadDrawingDialog(HWND hwnd) {
  217.     OPENFILENAME ofn;
  218.     wchar_t szFileName[MAX_PATH] = L"";
  219.  
  220.     ZeroMemory(&ofn, sizeof(ofn));
  221.     ofn.lStructSize = sizeof(ofn);
  222.     ofn.hwndOwner = hwnd;
  223.     ofn.lpstrFilter = L"Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
  224.     ofn.lpstrFile = szFileName;
  225.     ofn.nMaxFile = MAX_PATH;
  226.     ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
  227.  
  228.     if (GetOpenFileName(&ofn)) {
  229.         std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
  230.         std::string filenameStr = converter.to_bytes(szFileName);
  231.         LoadDrawing(filenameStr);
  232.     }
  233. }
  234.  
  235. void SanitizeAndSaveDrawing(const string& filename) {
  236.     ofstream outFile(filename);
  237.     if (outFile.is_open()) {
  238.         RECT rect;
  239.         GetClientRect(hWnd, &rect);
  240.  
  241.         vector<Brushstroke> validBrushstrokes;
  242.         for (const auto& brushstroke : storedBrushstrokes) {
  243.             if (IsPointInClientRect(hWnd, brushstroke.x, brushstroke.y)) {
  244.                 validBrushstrokes.push_back(brushstroke);
  245.             }
  246.         }
  247.  
  248.         vector<Eraserstroke> validEraserstrokes;
  249.         for (const auto& eraserstroke : storedEraserstrokes) {
  250.             if (IsPointInClientRect(hWnd, eraserstroke.x, eraserstroke.y)) {
  251.                 validEraserstrokes.push_back(eraserstroke);
  252.             }
  253.         }
  254.  
  255.         outFile << validBrushstrokes.size() << " " << validEraserstrokes.size() << endl;
  256.  
  257.         for (const auto& brushstroke : validBrushstrokes) {
  258.             brushstroke.serialize(outFile);
  259.         }
  260.  
  261.         for (const auto& eraserstroke : validEraserstrokes) {
  262.             eraserstroke.serialize(outFile);
  263.         }
  264.  
  265.         outFile.close();
  266.     }
  267. }
  268. LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  269.     switch (uMsg) {
  270.     case WM_CREATE:
  271.     {
  272.         hWnd = hwnd;
  273.         RECT rect;
  274.         GetClientRect(hwnd, &rect);
  275.         HDC hdc = GetDC(hwnd);
  276.         memDC = CreateCompatibleDC(hdc);
  277.         memBitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top);
  278.         SelectObject(memDC, memBitmap);
  279.         ReleaseDC(hwnd, hdc);
  280.         HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
  281.         FillRect(memDC, &rect, hBrush);
  282.         DeleteObject(hBrush);
  283.         isPaintbrushSelected = true;
  284.         LoadDrawing("drawing.txt");
  285.     }
  286.     break;
  287.     case WM_KEYDOWN:
  288.         if (wParam == VK_ADD) {
  289.             brushSize += 5;
  290.         }
  291.         else if (wParam == VK_SUBTRACT) {
  292.             if (brushSize > 5) {
  293.                 brushSize -= 5;
  294.             }
  295.         }
  296.         else if (wParam == 0x43) { // 'C' key
  297.             if (!(GetKeyState(VK_CONTROL) & 0x8000)) {
  298.                 ClearDrawing(hwnd);
  299.             }
  300.         }
  301.         else if (wParam == 0x50) {
  302.             isPaintbrushSelected = true;
  303.             isEraserSelected = false;
  304.         }
  305.         else if (wParam == 0x45) {
  306.             isEraserSelected = true;
  307.             isPaintbrushSelected = false;
  308.         }
  309.         else if (wParam == 'S' && (GetKeyState(VK_CONTROL) & 0x8000)) {
  310.             SaveDrawingDialog(hwnd);
  311.         }
  312.         else if (wParam == 'O' && (GetKeyState(VK_CONTROL) & 0x8000)) {
  313.             LoadDrawingDialog(hwnd);
  314.         }
  315.         break;
  316.     case WM_LBUTTONDOWN:
  317.         if (isPaintbrushSelected || isEraserSelected) {
  318.             previousPoint.x = LOWORD(lParam);
  319.             previousPoint.y = HIWORD(lParam);
  320.         }
  321.         isDrawing = true;
  322.         break;
  323.     case WM_LBUTTONUP:
  324.         isDrawing = false;
  325.         break;
  326.     case WM_MOUSEMOVE:
  327.         if (isDrawing && isPaintbrushSelected) {
  328.             int x = LOWORD(lParam);
  329.             int y = HIWORD(lParam);
  330.             DrawSmoothBrush(memDC, x, y);
  331.             InvalidateRect(hwnd, NULL, FALSE);
  332.             if (IsPointInClientRect(hWnd, x, y)) {
  333.                 Brushstroke newBrushstroke;
  334.                 newBrushstroke.x = x;
  335.                 newBrushstroke.y = y;
  336.                 newBrushstroke.size = brushSize;
  337.                 newBrushstroke.color = RGB(255, 0, 0);
  338.                 storedBrushstrokes.push_back(newBrushstroke);
  339.             }
  340.         }
  341.         else if (isDrawing && isEraserSelected) {
  342.             int x = LOWORD(lParam);
  343.             int y = HIWORD(lParam);
  344.             Erase(memDC, x, y, brushSize);
  345.             InvalidateRect(hwnd, NULL, FALSE);
  346.             if (IsPointInClientRect(hWnd, x, y)) {
  347.                 Eraserstroke newEraserstroke;
  348.                 newEraserstroke.x = x;
  349.                 newEraserstroke.y = y;
  350.                 newEraserstroke.size = brushSize;
  351.                 newEraserstroke.color = RGB(255, 255, 255); // White for erasing
  352.                 storedEraserstrokes.push_back(newEraserstroke);
  353.             }
  354.         }
  355.         break;
  356.     case WM_SIZE:
  357.     {
  358.         static bool isMinimized = false;
  359.         if (wParam == SIZE_MINIMIZED)
  360.         {
  361.             isMinimized = true;
  362.         }
  363.         else if (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED)
  364.         {
  365.             if (isMinimized)
  366.             {
  367.                 isMinimized = false;
  368.             }
  369.             InvalidateRect(hwnd, NULL, TRUE);
  370.         }
  371.         RECT rect;
  372.         GetClientRect(hwnd, &rect);
  373.         if (memDC)
  374.         {
  375.             DeleteDC(memDC);
  376.             DeleteObject(memBitmap);
  377.         }
  378.         HDC hdc = GetDC(hwnd);
  379.         memDC = CreateCompatibleDC(hdc);
  380.         memBitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top);
  381.         SelectObject(memDC, memBitmap);
  382.         ReleaseDC(hwnd, hdc);
  383.         HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
  384.         FillRect(memDC, &rect, hBrush);
  385.         DeleteObject(hBrush);
  386.         RedrawStrokes();
  387.     }
  388.     break;
  389.  
  390.     case WM_PAINT:
  391.     {
  392.         PAINTSTRUCT ps;
  393.         HDC hdc = BeginPaint(hwnd, &ps);
  394.         RECT rect;
  395.         GetClientRect(hwnd, &rect);
  396.         BitBlt(hdc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, memDC, 0, 0, SRCCOPY);
  397.         EndPaint(hwnd, &ps);
  398.     }
  399.     break;
  400.  
  401.     case WM_DESTROY:
  402.         SanitizeAndSaveDrawing("drawing.txt");
  403.         DeleteObject(memBitmap);
  404.         DeleteDC(memDC);
  405.         PostQuitMessage(0);
  406.         break;
  407.     default:
  408.         return DefWindowProc(hwnd, uMsg, wParam, lParam);
  409.     }
  410.     return 0;
  411. }
  412. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
  413.     WNDCLASSEX wc = {};
  414.     wc.cbSize = sizeof(wc);
  415.     wc.style = CS_HREDRAW | CS_VREDRAW;
  416.     wc.lpfnWndProc = WindowProc;
  417.     wc.hInstance = hInstance;
  418.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  419.     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  420.     wc.lpszClassName = L"MyClass";
  421.     RegisterClassEx(&wc);
  422.     HWND hwnd = CreateWindowEx(
  423.         0, L"MyClass", L"Doodle App",
  424.         WS_OVERLAPPEDWINDOW | WS_MAXIMIZE, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
  425.         NULL, NULL, hInstance, NULL
  426.     );
  427.     if (hwnd == NULL) {
  428.         return 0;
  429.     }
  430.     ShowWindow(hwnd, SW_SHOWMAXIMIZED);
  431.     UpdateWindow(hwnd);
  432.     MSG msg;
  433.     while (GetMessage(&msg, NULL, 0, 0)) {
  434.         TranslateMessage(&msg);
  435.         DispatchMessage(&msg);
  436.     }
  437.     return 0;
  438. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement