Advertisement
alien_fx_fiend

PaintMemDC (no color bug NEWAPP) *FINAL COMPLETE*

Aug 20th, 2024
65
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.27 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. #include <locale>
  9. #include <windowsx.h>
  10.  
  11. using namespace std;
  12.  
  13. struct Brushstroke;
  14. HWND hWnd;
  15. HINSTANCE hInst;
  16. bool isDirty = false;
  17.  
  18. void SetWindowTitle(HWND hwnd, const std::wstring& filename) {
  19.     std::wstring title = L"Paint Canvas - " + filename + L" (P=Brush E=Eraser C=Clear +-=BrushSize Ctrl+S=Save Ctrl+O=Load)";
  20.     SetWindowText(hwnd, title.c_str());
  21. }
  22.  
  23. std::wstring StringToWString(const std::string& str) {
  24.     std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
  25.     return converter.from_bytes(str);
  26. }
  27.  
  28. bool IsPointInClientRect(HWND hwnd, int x, int y) {
  29.     RECT rect;
  30.     GetClientRect(hwnd, &rect);
  31.     return (x >= 0 && x < rect.right&& y >= 0 && y < rect.bottom);
  32. }
  33.  
  34. struct Brushstroke {
  35.     int x;
  36.     int y;
  37.     int size;
  38.  
  39.     void serialize(ofstream& outFile) const {
  40.         outFile << "B " << x << " " << y << " " << size << endl;
  41.     }
  42. };
  43.  
  44. std::vector<Brushstroke> storedBrushstrokes;
  45. void RedrawStrokes();
  46. void SaveDrawing(const string& filename);
  47. void LoadDrawing(const string& filename);
  48.  
  49. HDC hdc, memDC;
  50. HBITMAP memBitmap;
  51. bool isPaintbrushSelected = true;
  52. bool isDrawing = false;
  53. bool isErasing = false;
  54. bool isClearing = false;
  55. bool isEraserSelected = false;
  56. int brushSize = 10;
  57.  
  58. void DrawBrush(HDC hdc, int x, int y) {
  59.     if (!IsPointInClientRect(hWnd, x, y)) return;
  60.     HGDIOBJ originalBrush = SelectObject(hdc, CreateSolidBrush(RGB(255, 0, 0)));
  61.     HGDIOBJ originalPen = SelectObject(hdc, GetStockObject(NULL_PEN));
  62.     Rectangle(hdc, x - brushSize, y - brushSize, x + brushSize, y + brushSize);
  63.     DeleteObject(SelectObject(hdc, originalBrush));
  64.     DeleteObject(SelectObject(hdc, originalPen));
  65. }
  66.  
  67. void Erase(HDC hdc, int x, int y, int eraserSize) {
  68.     if (!IsPointInClientRect(hWnd, x, y)) return;
  69.     HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
  70.     SelectObject(hdc, GetStockObject(NULL_PEN));
  71.     SelectObject(hdc, hBrush);
  72.     Rectangle(hdc, x - eraserSize, y - eraserSize, x + eraserSize, y + eraserSize);
  73.     DeleteObject(hBrush);
  74.  
  75.     storedBrushstrokes.erase(
  76.         std::remove_if(storedBrushstrokes.begin(), storedBrushstrokes.end(),
  77.             [x, y, eraserSize](const Brushstroke& stroke) {
  78.                 int dx = abs(stroke.x - x);
  79.                 int dy = abs(stroke.y - y);
  80.                 return (dx < eraserSize + stroke.size && dy < eraserSize + stroke.size);
  81.             }),
  82.         storedBrushstrokes.end()
  83.                 );
  84. }
  85.  
  86. void ClearDrawing(HWND hwnd) {
  87.     RECT rect;
  88.     GetClientRect(hwnd, &rect);
  89.     HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
  90.     HDC hdc = GetDC(hwnd);
  91.     FillRect(hdc, &rect, hBrush);
  92.     FillRect(memDC, &rect, hBrush);
  93.     ReleaseDC(hwnd, hdc);
  94.     DeleteObject(hBrush);
  95.     storedBrushstrokes.clear();
  96.     InvalidateRect(hwnd, NULL, FALSE);
  97.     UpdateWindow(hwnd);
  98. }
  99.  
  100. void RedrawStrokes() {
  101.     RECT rect;
  102.     GetClientRect(hWnd, &rect);
  103.     HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
  104.     FillRect(memDC, &rect, hBrush);
  105.     DeleteObject(hBrush);
  106.     for (const auto& brushstroke : storedBrushstrokes) {
  107.         HGDIOBJ originalBrush = SelectObject(memDC, CreateSolidBrush(RGB(255, 0, 0)));
  108.         HGDIOBJ originalPen = SelectObject(memDC, GetStockObject(NULL_PEN));
  109.         Rectangle(memDC, brushstroke.x - brushstroke.size, brushstroke.y - brushstroke.size,
  110.             brushstroke.x + brushstroke.size, brushstroke.y + brushstroke.size);
  111.         DeleteObject(SelectObject(memDC, originalBrush));
  112.         DeleteObject(SelectObject(memDC, originalPen));
  113.     }
  114.     InvalidateRect(hWnd, NULL, FALSE);
  115.     UpdateWindow(hWnd);
  116. }
  117.  
  118. void SaveDrawing(const string& filename) {
  119.     ofstream outFile(filename);
  120.     if (outFile.is_open()) {
  121.         outFile << storedBrushstrokes.size() << endl;
  122.         for (const auto& brushstroke : storedBrushstrokes) {
  123.             brushstroke.serialize(outFile);
  124.         }
  125.         outFile.close();
  126.         if (outFile.fail()) {
  127.             MessageBox(hWnd, L"Error occurred while saving the file", L"Save Error", MB_OK | MB_ICONERROR);
  128.         }
  129.         else {
  130.             isDirty = false;  // Reset the dirty flag after successful save
  131.         }
  132.     }
  133.     else {
  134.         MessageBox(hWnd, L"Could not open file for saving", L"Save Error", MB_OK | MB_ICONERROR);
  135.     }
  136. }
  137.  
  138. void LoadDrawing(const string& filename) {
  139.     ifstream inFile(filename);
  140.     if (inFile.is_open()) {
  141.         int numBrushstrokes;
  142.         inFile >> numBrushstrokes;
  143.         storedBrushstrokes.clear();
  144.         storedBrushstrokes.reserve(numBrushstrokes);
  145.         for (int i = 0; i < numBrushstrokes; ++i) {
  146.             char type;
  147.             Brushstroke brushstroke;
  148.             inFile >> type >> brushstroke.x >> brushstroke.y >> brushstroke.size;
  149.             storedBrushstrokes.push_back(brushstroke);
  150.         }
  151.         inFile.close();
  152.         RedrawStrokes();
  153.         isDirty = false;  // Inserted here
  154.     }
  155. }
  156.  
  157. void SaveDrawingDialog(HWND hwnd) {
  158.     OPENFILENAME ofn;
  159.     wchar_t szFileName[MAX_PATH] = L"";
  160.     ZeroMemory(&ofn, sizeof(ofn));
  161.     ofn.lStructSize = sizeof(ofn);
  162.     ofn.hwndOwner = hwnd;
  163.     ofn.lpstrFilter = L"Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
  164.     ofn.lpstrFile = szFileName;
  165.     ofn.nMaxFile = MAX_PATH;
  166.     ofn.Flags = OFN_EXPLORER | OFN_OVERWRITEPROMPT;
  167.     ofn.lpstrDefExt = L"txt";
  168.     if (GetSaveFileName(&ofn)) {
  169.         std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
  170.         std::string filenameStr = converter.to_bytes(szFileName);
  171.         SaveDrawing(filenameStr);
  172.         MessageBox(hwnd, L"Save Complete", L"Doodle App", MB_OK | MB_ICONINFORMATION);
  173.         SetWindowTitle(hwnd, szFileName);
  174.     }
  175. }
  176.  
  177. void LoadDrawingDialog(HWND hwnd) {
  178.     OPENFILENAME ofn;
  179.     wchar_t szFileName[MAX_PATH] = L"";
  180.     ZeroMemory(&ofn, sizeof(ofn));
  181.     ofn.lStructSize = sizeof(ofn);
  182.     ofn.hwndOwner = hwnd;
  183.     ofn.lpstrFilter = L"Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
  184.     ofn.lpstrFile = szFileName;
  185.     ofn.nMaxFile = MAX_PATH;
  186.     ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
  187.     if (GetOpenFileName(&ofn)) {
  188.         std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
  189.         std::string filenameStr = converter.to_bytes(szFileName);
  190.         LoadDrawing(filenameStr);
  191.         SetWindowTitle(hwnd, szFileName);
  192.     }
  193. }
  194.  
  195. void SanitizeAndSaveDrawing(const string& filename) {
  196.     ofstream outFile(filename);
  197.     if (outFile.is_open()) {
  198.         RECT rect;
  199.         GetClientRect(hWnd, &rect);
  200.         vector<Brushstroke> validBrushstrokes;
  201.         for (const auto& brushstroke : storedBrushstrokes) {
  202.             if (IsPointInClientRect(hWnd, brushstroke.x, brushstroke.y)) {
  203.                 validBrushstrokes.push_back(brushstroke);
  204.             }
  205.         }
  206.         outFile << validBrushstrokes.size() << endl;
  207.         for (const auto& brushstroke : validBrushstrokes) {
  208.             brushstroke.serialize(outFile);
  209.         }
  210.         outFile.close();
  211.     }
  212. }
  213.  
  214. LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  215.     switch (uMsg) {
  216.     case WM_CREATE:
  217.     {
  218.         hWnd = hwnd;
  219.         ShowWindow(hwnd, SW_MAXIMIZE);
  220.         RECT rect;
  221.         GetClientRect(hwnd, &rect);
  222.         HDC hdc = GetDC(hwnd);
  223.         memDC = CreateCompatibleDC(hdc);
  224.         memBitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top);
  225.         SelectObject(memDC, memBitmap);
  226.         SetBkMode(memDC, TRANSPARENT);
  227.         FillRect(memDC, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
  228.         ReleaseDC(hwnd, hdc);
  229.         //SaveDrawing("drawing.txt");
  230.         CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
  231.         std::wstring initialFileName = L"drawing.txt";
  232.         if (pCreate && pCreate->lpCreateParams) {
  233.             initialFileName = *reinterpret_cast<std::wstring*>(pCreate->lpCreateParams);
  234.         }
  235.         std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
  236.         std::string filenameStr = converter.to_bytes(initialFileName);
  237.         LoadDrawing(filenameStr);
  238.         SetWindowTitle(hwnd, initialFileName);
  239.         break;
  240.     }
  241.     case WM_PAINT:
  242.     {
  243.         PAINTSTRUCT ps;
  244.         HDC hdc = BeginPaint(hwnd, &ps);
  245.         BitBlt(hdc, 0, 0, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top,
  246.             memDC, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
  247.         EndPaint(hwnd, &ps);
  248.         break;
  249.     }
  250.     case WM_LBUTTONDOWN:
  251.     {
  252.         isDrawing = true;
  253.         int x = GET_X_LPARAM(lParam);
  254.         int y = GET_Y_LPARAM(lParam);
  255.         if (isPaintbrushSelected) {
  256.             DrawBrush(memDC, x, y);
  257.             storedBrushstrokes.push_back({ x, y, brushSize });
  258.             isDirty = true;
  259.         }
  260.         else if (isEraserSelected) {
  261.             Erase(memDC, x, y, brushSize);
  262.             isDirty = true;
  263.         }
  264.         InvalidateRect(hwnd, NULL, FALSE);
  265.         break;
  266.     }
  267.     case WM_LBUTTONUP:
  268.         isDrawing = false;
  269.         break;
  270.     case WM_MOUSEMOVE:
  271.         if (isDrawing) {
  272.             int x = GET_X_LPARAM(lParam);
  273.             int y = GET_Y_LPARAM(lParam);
  274.             if (isPaintbrushSelected) {
  275.                 DrawBrush(memDC, x, y);
  276.                 storedBrushstrokes.push_back({ x, y, brushSize });
  277.                 isDirty = true;
  278.             }
  279.             else if (isEraserSelected) {
  280.                 Erase(memDC, x, y, brushSize);
  281.                 isDirty = true;
  282.             }
  283.             InvalidateRect(hwnd, NULL, FALSE);
  284.         }
  285.         break;
  286.     case WM_KEYDOWN:
  287.         switch (wParam) {
  288.         case 'P':
  289.             isPaintbrushSelected = true;
  290.             isEraserSelected = false;
  291.             break;
  292.         case 'E':
  293.             isEraserSelected = true;
  294.             isPaintbrushSelected = false;
  295.             break;
  296.         case 'C':
  297.             isClearing = true;
  298.             ClearDrawing(hwnd);
  299.             isClearing = false;
  300.             isDirty = true;
  301.             break;
  302.         case VK_OEM_PLUS:
  303.             brushSize += 2;
  304.             break;
  305.         case VK_OEM_MINUS:
  306.             brushSize = max(1, brushSize - 2);
  307.             break;
  308.         case 'S':
  309.             if (GetKeyState(VK_CONTROL) & 0x8000) {
  310.                 SaveDrawingDialog(hwnd);
  311.             }
  312.             break;
  313.         case 'O':
  314.             if (GetKeyState(VK_CONTROL) & 0x8000) {
  315.                 LoadDrawingDialog(hwnd);
  316.             }
  317.             break;
  318.         case VK_ESCAPE:
  319.             SendMessage(hwnd, WM_CLOSE, 0, 0);
  320.             break;
  321.         }
  322.         break;
  323.     case WM_SIZE:
  324.         if (wParam == SIZE_MAXIMIZED) {
  325.             RECT rect;
  326.             GetClientRect(hwnd, &rect);
  327.             HBITMAP newBitmap = CreateCompatibleBitmap(GetDC(hwnd), rect.right - rect.left, rect.bottom - rect.top);
  328.             SelectObject(memDC, newBitmap);
  329.             DeleteObject(memBitmap);
  330.             memBitmap = newBitmap;
  331.             RedrawStrokes();
  332.         }
  333.         break;
  334.     case WM_SETCURSOR:
  335.         if (LOWORD(lParam) == HTCLIENT) {
  336.             SetCursor(LoadCursor(NULL, IDC_ARROW));
  337.             return TRUE;
  338.         }
  339.         break;
  340.     //Remove below function (added new)
  341.     case WM_CLOSE:
  342.         if (isDirty) {
  343.             int result = MessageBox(hwnd, L"Do you want to save your changes?", L"Doodle App", MB_YESNOCANCEL | MB_ICONQUESTION);
  344.             if (result == IDYES) {
  345.                 SaveDrawing("drawing.txt");
  346.             }
  347.             else if (result == IDCANCEL) {
  348.                 return 0;  // Don't close the window
  349.             }
  350.         }
  351.         DestroyWindow(hwnd);
  352.         break;
  353.         //original WM_DESTROY function below
  354.         /*if (isDirty) {
  355.             SaveDrawing("drawing.txt");
  356.         }
  357.         //SetWindowTitle(hwnd, L"drawing.txt");
  358.         PostQuitMessage(0);
  359.         break;*/
  360.  
  361.         //Ask to Save Changes new function below
  362.         /*if (isDirty) {
  363.             int result = MessageBox(hwnd, L"Do you want to save your changes?", L"Doodle App", MB_YESNOCANCEL | MB_ICONQUESTION);
  364.             if (result == IDYES) {
  365.                 SaveDrawing("drawing.txt");
  366.             }
  367.             else if (result == IDCANCEL) {
  368.                 return 0;  // Don't close the window
  369.             }
  370.         }
  371.         PostQuitMessage(0);
  372.         break;*/
  373.     case WM_DESTROY:        
  374.         PostQuitMessage(0);
  375.         break;
  376.     }
  377.     return DefWindowProc(hwnd, uMsg, wParam, lParam);
  378. }
  379.  
  380. int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
  381.     hInst = hInstance;
  382.     const wchar_t CLASS_NAME[] = L"PaintCanvas";
  383.     WNDCLASS wc = {};
  384.     wc.lpfnWndProc = WindowProc;
  385.     wc.hInstance = hInstance;
  386.     wc.lpszClassName = CLASS_NAME;
  387.     RegisterClass(&wc);
  388.     HWND hwnd = CreateWindowEx(0, CLASS_NAME, L"Paint Canvas (P=Brush E=Eraser C=Clear +-=BrushSize Ctrl+S=Save Ctrl+O=Load)", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
  389.     if (hwnd == NULL) {
  390.         return 0;
  391.     }
  392.     ShowWindow(hwnd, SW_MAXIMIZE);
  393.     MSG msg = {};
  394.     while (GetMessage(&msg, NULL, 0, 0)) {
  395.         TranslateMessage(&msg);
  396.         DispatchMessage(&msg);
  397.     }
  398.     return 0;
  399. }
  400.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement