Advertisement
alien_fx_fiend

PaintCanvas FINAL (EmptyFile color bug fix) *FINAL COMPLETE*

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