Advertisement
alien_fx_fiend

Memory DC PaintCanvas Projectv7 final (Removed Undo/Redo + Fixed Erase Smear on Launch)

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