Advertisement
alien_fx_fiend

Memory DC PaintCanvas Projectv9 (orig GeminiPro bkp before Claude)

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