Advertisement
alien_fx_fiend

Broken MemDC Paint (Trying Alternatives) bkp

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