alien_fx_fiend

Infinite Canvas Doodle App (Optimizations Need Fixing)

Nov 30th, 2024
17
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 34.56 KB | Source Code | 0 0
  1. ==++ Here's the full source code (file 1/1) of "main.cpp":: ++==
  2. #define NOMINMAX
  3. #define WIN32_LEAN_AND_MEAN
  4. #include <Unknwn.h>
  5. #include <windows.h>
  6. #include <gdiplus.h>
  7. #include <objidl.h>
  8. #include <windef.h>
  9. #include <wingdi.h>
  10. #include <algorithm>
  11. #include <iostream>
  12. #include <vector>
  13. #include <cmath>
  14. #include <commctrl.h>
  15. #include <commdlg.h>  
  16. #include <stdio.h>
  17. #include <fstream>
  18. #include <thread>
  19. #include <mutex>
  20. #include "resource.h"  
  21.  
  22. #pragma comment(lib, "comctl32.lib")
  23. #pragma comment(lib, "comdlg32.lib")
  24. #pragma comment(lib, "msimg32.lib")
  25. #pragma comment(lib, "gdiplus.lib")
  26.  
  27. using namespace std;
  28. using namespace Gdiplus;
  29.  
  30. GdiplusStartupInput gdiplusStartupInput;
  31. ULONG_PTR gdiplusToken;
  32.  
  33. struct DrawPoint {
  34.    int x, y;
  35.    DWORD timestamp;
  36.    DrawPoint() : x(0), y(0), timestamp(0) {}
  37.    DrawPoint(int px, int py) : x(px), y(py), timestamp(GetTickCount()) {}
  38. };
  39.  
  40. struct SerializedStroke {
  41.    std::vector<DrawPoint> points;
  42.    COLORREF color;
  43.    int brushSize;
  44.    bool isEraser;
  45. };
  46.  
  47. struct CanvasState {
  48.    std::vector<SerializedStroke> strokes;
  49.    float gridZoomFactor;
  50.    bool showGrid;
  51.    bool useAlphaGrid;
  52.    int gridOpacity;
  53. };
  54.  
  55. std::mutex strokeMutex;
  56. std::vector<SerializedStroke> strokeHistory;
  57. bool isLoading = false;
  58. const double MAX_SPEED = 1000.0;
  59.  
  60. std::vector<DrawPoint> strokeBuffer;
  61. const int STROKE_BUFFER_SIZE = 3;
  62. const double MIN_DISTANCE = 2.0;
  63.  
  64. void DrawSmoothStroke(HDC hdc, const std::vector<DrawPoint>& points, bool isEraser, COLORREF strokeColor, int strokeSize);
  65. void DrawBrush(HDC hdc, int x, int y, bool isEraser = false, bool interpolate = true);
  66. void Erase(HDC hdc, int x, int y);
  67. void ClearDrawing(HWND hwnd);
  68. void UpdateStatus(HWND hwnd);
  69. void RedrawAllStrokes();
  70. LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  71.  
  72. HANDLE hFileMapping = NULL;
  73. int virtualWidth = 8192;
  74. int virtualHeight = 8192;
  75. int scrollX = 0;
  76. int scrollY = 0;
  77. bool isSpacePressed = false;
  78. bool isPanning = false;
  79. POINT lastDrawPoint = { -1, -1 };
  80. POINT lastMousePos = { 0, 0 };
  81. POINT dragStart = { 0, 0 };
  82. HDC hMemoryDC = NULL;
  83. HBITMAP hMemoryBitmap = NULL;
  84. HBITMAP hOldBitmap = NULL;
  85. HINSTANCE hInst;
  86. HDC hStatusBufferDC = NULL;
  87. HBITMAP hStatusBufferBitmap = NULL;
  88. POINT previousPoint;
  89. HDC hdc;
  90. HWND hWnd;
  91. DWORD lastStatusUpdateTime = 0;
  92. const DWORD STATUS_UPDATE_INTERVAL = 50;
  93. const int GRID_SIZE = 100;
  94. const COLORREF GRID_COLOR = RGB(255, 140, 0);
  95. HDC hGridDC = NULL;
  96. HBITMAP hGridBitmap = NULL;
  97. BOOL gridInitialized = FALSE;
  98. float gridZoomFactor = 1.0f;
  99. bool showGrid = true;
  100. bool useAlphaGrid = false;
  101. int gridOpacity = 255;
  102. COLORREF currentBrushColor = RGB(24, 123, 205);
  103. bool isInitialized = false;
  104. bool isPaintbrushSelected = true;
  105. bool isDrawing = false;
  106. bool isErasing = false;
  107. bool isClearing = false;
  108. bool isEraserMode = false;
  109. bool isEraserSelected = false;
  110. int minBrushSize = 5;
  111. int maxBrushSize = 50;
  112. int brushSize = 10;
  113. int loadProgress = 0;
  114. const wchar_t* STATE_FILE = L"canvas_state.bin";
  115.  
  116. void InitializeMemoryBitmap(HWND hwnd) {
  117.    HDC hdc = GetDC(hwnd);
  118.    if (!hdc) {
  119.        return;
  120.    }
  121.    hMemoryDC = CreateCompatibleDC(hdc);
  122.    if (!hMemoryDC) {
  123.        ReleaseDC(hwnd, hdc);
  124.        return;
  125.    }
  126.    hMemoryBitmap = CreateCompatibleBitmap(hdc, virtualWidth, virtualHeight);
  127.    if (!hMemoryBitmap) {
  128.        DeleteDC(hMemoryDC);
  129.        ReleaseDC(hwnd, hdc);
  130.        return;
  131.    }
  132.    hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hMemoryBitmap);
  133.    HBRUSH whiteBrush = CreateSolidBrush(RGB(255, 255, 255));
  134.    RECT rect = { 0, 0, virtualWidth, virtualHeight };
  135.    FillRect(hMemoryDC, &rect, whiteBrush);
  136.    DeleteObject(whiteBrush);
  137.    ReleaseDC(hwnd, hdc);
  138.    isInitialized = true;
  139.    HPEN guidePen = CreatePen(PS_SOLID, 1, RGB(200, 200, 200));
  140.    SelectObject(hMemoryDC, guidePen);
  141.    MoveToEx(hMemoryDC, virtualWidth / 2, 0, NULL);
  142.    LineTo(hMemoryDC, virtualWidth / 2, virtualHeight);
  143.    MoveToEx(hMemoryDC, 0, virtualHeight / 2, NULL);
  144.    LineTo(hMemoryDC, virtualWidth, virtualHeight / 2);
  145.    HPEN edgePen = CreatePen(PS_SOLID, 2, RGB(0, 0, 255));
  146.    SelectObject(hMemoryDC, edgePen);
  147.    Rectangle(hMemoryDC, 0, 0, virtualWidth - 1, virtualHeight - 1);
  148.    DeleteObject(guidePen);
  149.    DeleteObject(edgePen);
  150.    RECT clientRect;
  151.    GetClientRect(hwnd, &clientRect);
  152.    scrollX = (virtualWidth - clientRect.right) / 2;
  153.    scrollY = (virtualHeight - clientRect.bottom) / 2;
  154. }
  155.  
  156. void SaveCanvasState() {
  157.    std::ofstream file(STATE_FILE, std::ios::binary | std::ios::out);
  158.    if (!file) return;
  159.  
  160.    // Save canvas settings
  161.    file.write(reinterpret_cast<const char*>(&gridZoomFactor), sizeof(float));
  162.    file.write(reinterpret_cast<const char*>(&showGrid), sizeof(bool));
  163.    file.write(reinterpret_cast<const char*>(&useAlphaGrid), sizeof(bool));
  164.    file.write(reinterpret_cast<const char*>(&gridOpacity), sizeof(int));
  165.    file.write(reinterpret_cast<const char*>(&currentBrushColor), sizeof(COLORREF));
  166.    file.write(reinterpret_cast<const char*>(&brushSize), sizeof(int));  // Save current brush size
  167.  
  168.    // Save strokes
  169.    size_t strokeCount = strokeHistory.size();
  170.    file.write(reinterpret_cast<const char*>(&strokeCount), sizeof(size_t));
  171.  
  172.    for (const auto& stroke : strokeHistory) {
  173.        size_t pointCount = stroke.points.size();
  174.        file.write(reinterpret_cast<const char*>(&pointCount), sizeof(size_t));
  175.        file.write(reinterpret_cast<const char*>(stroke.points.data()),
  176.            pointCount * sizeof(DrawPoint));
  177.        file.write(reinterpret_cast<const char*>(&stroke.color), sizeof(COLORREF));
  178.        file.write(reinterpret_cast<const char*>(&stroke.brushSize), sizeof(int));
  179.        file.write(reinterpret_cast<const char*>(&stroke.isEraser), sizeof(bool));
  180.    }
  181.    file.close();
  182. }
  183.  
  184. void LoadCanvasStateAsync(HWND hwnd) {
  185.    std::thread([hwnd]() {
  186.        std::ifstream file(STATE_FILE, std::ios::binary | std::ios::in);
  187.        if (!file) {
  188.            isLoading = false;
  189.            return;
  190.        }
  191.  
  192.        try {
  193.            // Load canvas settings
  194.            file.read(reinterpret_cast<char*>(&gridZoomFactor), sizeof(float));
  195.            file.read(reinterpret_cast<char*>(&showGrid), sizeof(bool));
  196.            file.read(reinterpret_cast<char*>(&useAlphaGrid), sizeof(bool));
  197.            file.read(reinterpret_cast<char*>(&gridOpacity), sizeof(int));
  198.            file.read(reinterpret_cast<char*>(&currentBrushColor), sizeof(COLORREF));
  199.            file.read(reinterpret_cast<char*>(&brushSize), sizeof(int));  // Load brush size
  200.  
  201.            size_t strokeCount;
  202.            file.read(reinterpret_cast<char*>(&strokeCount), sizeof(size_t));
  203.            std::vector<SerializedStroke> loadedStrokes;
  204.  
  205.            for (size_t i = 0; i < strokeCount && file.good(); i++) {
  206.                SerializedStroke stroke;
  207.                size_t pointCount;
  208.                file.read(reinterpret_cast<char*>(&pointCount), sizeof(size_t));
  209.  
  210.                if (pointCount > 0 && pointCount < 1000000) {
  211.                    stroke.points.resize(pointCount);
  212.                    file.read(reinterpret_cast<char*>(stroke.points.data()),
  213.                        pointCount * sizeof(DrawPoint));
  214.                    file.read(reinterpret_cast<char*>(&stroke.color), sizeof(COLORREF));
  215.                    file.read(reinterpret_cast<char*>(&stroke.brushSize), sizeof(int));
  216.                    file.read(reinterpret_cast<char*>(&stroke.isEraser), sizeof(bool));
  217.                    loadedStrokes.push_back(stroke);
  218.                }
  219.  
  220.                loadProgress = (int)((i + 1) * 100 / strokeCount);
  221.                PostMessage(hwnd, WM_USER + 1, 0, 0);
  222.            }
  223.  
  224.            {
  225.                std::lock_guard<std::mutex> lock(strokeMutex);
  226.                strokeHistory = std::move(loadedStrokes);
  227.            }
  228.  
  229.            // Redraw after loading all strokes
  230.            RedrawAllStrokes();
  231.        }
  232.        catch (...) {
  233.            isLoading = false;
  234.            return;
  235.        }
  236.  
  237.        file.close();
  238.        isLoading = false;
  239.        InvalidateRect(hwnd, NULL, TRUE);
  240.        }).detach();
  241. }
  242.  
  243. void DrawSmoothStroke(HDC hdc, const std::vector<DrawPoint>& points, bool isEraser, COLORREF strokeColor, int strokeSize) {
  244.    if (points.size() < 2) return;
  245.  
  246.    Graphics graphics(hdc);
  247.    graphics.SetSmoothingMode(SmoothingModeAntiAlias);
  248.  
  249.    Color strokeGDIPlusColor(255, GetRValue(strokeColor), GetGValue(strokeColor), GetBValue(strokeColor));
  250.    Pen pen(strokeGDIPlusColor, (float)strokeSize);
  251.    pen.SetLineJoin(LineJoinRound);
  252.  
  253.    // Anti-aliasing and smoother strokes using GDI+
  254.    for (size_t i = 1; i < points.size(); i++) {
  255.        graphics.DrawLine(&pen,
  256.            (REAL)points[i - 1].x, (REAL)points[i - 1].y,
  257.            (REAL)points[i].x, (REAL)points[i].y);
  258.    }
  259.  
  260.    // Optionally, implement batch processing
  261.    const size_t batchSize = 100;  // Process 100 points at a time
  262.    for (size_t i = 0; i < points.size(); i += batchSize) {
  263.        size_t end = std::min(i + batchSize, points.size());
  264.        for (size_t j = i; j < end - 1; ++j) {
  265.            graphics.DrawLine(&pen,
  266.                (REAL)points[j].x, (REAL)points[j].y,
  267.                (REAL)points[j + 1].x, (REAL)points[j + 1].y);
  268.        }
  269.    }
  270. }
  271.  
  272. void SaveIncrementalStroke(const SerializedStroke& stroke) {
  273.    // Open the file for appending
  274.    std::ofstream file("incremental_strokes.bin", std::ios::binary | std::ios::app);
  275.    if (!file) {
  276.        MessageBox(NULL, L"Failed to open incremental save file.", L"Error", MB_OK | MB_ICONERROR);
  277.        return;
  278.    }
  279.  
  280.    // Save the stroke's metadata
  281.     size_t pointCount = stroke.points.size();
  282.     file.write(reinterpret_cast<const char*>(&pointCount), sizeof(size_t));  // Write the number of points
  283.     file.write(reinterpret_cast<const char*>(stroke.points.data()), pointCount * sizeof(DrawPoint));  // Write point data
  284.     file.write(reinterpret_cast<const char*>(&stroke.color), sizeof(COLORREF));  // Write stroke color
  285.     file.write(reinterpret_cast<const char*>(&stroke.brushSize), sizeof(int));  // Write brush size
  286.     file.write(reinterpret_cast<const char*>(&stroke.isEraser), sizeof(bool));  // Write eraser flag
  287.  
  288.     file.close();
  289. }
  290.  
  291. void DrawBrush(HDC hdc, int x, int y, bool isEraser, bool interpolate) {
  292.     if (x < 0 || x > virtualWidth || y < 0 || y > virtualHeight) {
  293.         return;
  294.     }
  295.     COLORREF color = isEraser ? RGB(255, 255, 255) : currentBrushColor;
  296.     HBRUSH brush = CreateSolidBrush(color);
  297.     HPEN pen = CreatePen(PS_SOLID, 1, color);
  298.     HBRUSH oldBrush = (HBRUSH)SelectObject(hdc, brush);
  299.     HPEN oldPen = (HPEN)SelectObject(hdc, pen);
  300.     Ellipse(hdc, x - brushSize, y - brushSize, x + brushSize, y + brushSize);
  301.     SelectObject(hdc, oldBrush);
  302.     SelectObject(hdc, oldPen);
  303.     DeleteObject(brush);
  304.     DeleteObject(pen);
  305. }
  306.  
  307. void StartStroke(int x, int y) {
  308.     strokeBuffer.clear();
  309.     strokeBuffer.push_back(DrawPoint(x, y));
  310. }
  311.  
  312. void AddToStroke(int x, int y) {
  313.     if (strokeBuffer.empty()) {
  314.         StartStroke(x, y);
  315.         return;
  316.     }
  317.     const DrawPoint& lastPt = strokeBuffer.back();
  318.     double dx = x - lastPt.x;
  319.     double dy = y - lastPt.y;
  320.     double distance = sqrt(dx * dx + dy * dy);
  321.     if (distance >= MIN_DISTANCE) {
  322.         strokeBuffer.push_back(DrawPoint(x, y));
  323.         while (strokeBuffer.size() > STROKE_BUFFER_SIZE) {
  324.             strokeBuffer.erase(strokeBuffer.begin());
  325.         }
  326.     }
  327. }
  328.  
  329. void EndStroke() {
  330.     strokeBuffer.clear();
  331. }
  332.  
  333. void ShowColorPicker(HWND hwnd) {
  334.     CHOOSECOLOR cc = { sizeof(CHOOSECOLOR) };
  335.     static COLORREF customColors[16] = { 0 };
  336.     cc.hwndOwner = hwnd;
  337.     cc.rgbResult = currentBrushColor;
  338.     cc.lpCustColors = customColors;
  339.     cc.Flags = CC_FULLOPEN | CC_RGBINIT;
  340.     if (ChooseColor(&cc)) {
  341.         currentBrushColor = cc.rgbResult;
  342.         UpdateStatus(hwnd);
  343.     }
  344. }
  345.  
  346. void Erase(HDC hdc, int x, int y) {
  347.     HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
  348.     SelectObject(hdc, GetStockObject(NULL_PEN));
  349.     SelectObject(hdc, hBrush);
  350.     Ellipse(hdc, x - brushSize, y - brushSize, x + brushSize, y + brushSize);
  351.     DeleteObject(hBrush);
  352. }
  353.  
  354. void ClearCanvas(HWND hwnd) {
  355.     HBRUSH whiteBrush = CreateSolidBrush(RGB(255, 255, 255));
  356.     RECT rect = { 0, 0, virtualWidth, virtualHeight };
  357.     FillRect(hMemoryDC, &rect, whiteBrush);
  358.     DeleteObject(whiteBrush);
  359.     InvalidateRect(hwnd, NULL, FALSE);
  360. }
  361.  
  362. void ClearDrawing(HWND hwnd) {
  363.     HBRUSH whiteBrush = CreateSolidBrush(RGB(255, 255, 255));
  364.     RECT rect = { 0, 0, virtualWidth, virtualHeight };
  365.     FillRect(hMemoryDC, &rect, whiteBrush);
  366.     DeleteObject(whiteBrush);
  367.     RECT clientRect;
  368.     GetClientRect(hwnd, &clientRect);
  369.     scrollX = (virtualWidth - clientRect.right) / 2;
  370.     scrollY = (virtualHeight - clientRect.bottom) / 2;
  371.     HPEN guidePen = CreatePen(PS_SOLID, 1, RGB(200, 200, 200));
  372.     SelectObject(hMemoryDC, guidePen);
  373.     MoveToEx(hMemoryDC, virtualWidth / 2, 0, NULL);
  374.     LineTo(hMemoryDC, virtualWidth / 2, virtualHeight);
  375.     MoveToEx(hMemoryDC, 0, virtualHeight / 2, NULL);
  376.     LineTo(hMemoryDC, virtualWidth, virtualHeight / 2);
  377.     HPEN edgePen = CreatePen(PS_SOLID, 2, RGB(0, 0, 255));
  378.     SelectObject(hMemoryDC, edgePen);
  379.     Rectangle(hMemoryDC, 0, 0, virtualWidth - 1, virtualHeight - 1);
  380.     DeleteObject(guidePen);
  381.     DeleteObject(edgePen);
  382.     InvalidateRect(hwnd, NULL, FALSE);
  383. }
  384.  
  385. void InitializeGridCache(HDC hdc) {
  386.     if (hGridDC) {
  387.         DeleteDC(hGridDC);
  388.         DeleteObject(hGridBitmap);
  389.     }
  390.     int scaledGridSize = (int)(GRID_SIZE * gridZoomFactor);
  391.     hGridDC = CreateCompatibleDC(hdc);
  392.     hGridBitmap = CreateCompatibleBitmap(hdc, scaledGridSize, scaledGridSize);
  393.     HBITMAP oldBitmap = (HBITMAP)SelectObject(hGridDC, hGridBitmap);
  394.     HBRUSH whiteBrush = CreateSolidBrush(RGB(255, 255, 255));
  395.     RECT rect = { 0, 0, scaledGridSize, scaledGridSize };
  396.     FillRect(hGridDC, &rect, whiteBrush);
  397.     DeleteObject(whiteBrush);
  398.     HPEN gridPen = CreatePen(PS_SOLID, 1, GRID_COLOR);
  399.     HPEN oldPen = (HPEN)SelectObject(hGridDC, gridPen);
  400.     MoveToEx(hGridDC, scaledGridSize - 1, 0, NULL);
  401.     LineTo(hGridDC, scaledGridSize - 1, scaledGridSize);
  402.     MoveToEx(hGridDC, 0, scaledGridSize - 1, NULL);
  403.     LineTo(hGridDC, scaledGridSize, scaledGridSize - 1);
  404.     SelectObject(hGridDC, oldPen);
  405.     DeleteObject(gridPen);
  406.     gridInitialized = TRUE;
  407. }
  408.  
  409. void DrawGrid(HDC hdc, const RECT& clientRect, int scrollX, int scrollY) {
  410.     SetBkMode(hdc, TRANSPARENT);
  411.     HPEN gridPen = CreatePen(PS_SOLID, 1, GRID_COLOR);
  412.     HPEN oldPen = (HPEN)SelectObject(hdc, gridPen);
  413.     int scaledGridSize = (int)(GRID_SIZE * gridZoomFactor);
  414.     int offsetX = -(scrollX % scaledGridSize);
  415.     int offsetY = -(scrollY % scaledGridSize);
  416.     for (int x = offsetX; x <= clientRect.right; x += scaledGridSize) {
  417.         MoveToEx(hdc, x, 0, NULL);
  418.         LineTo(hdc, x, clientRect.bottom);
  419.     }
  420.     for (int y = offsetY; y <= clientRect.bottom; y += scaledGridSize) {
  421.         MoveToEx(hdc, 0, y, NULL);
  422.         LineTo(hdc, clientRect.right, y);
  423.     }
  424.     SelectObject(hdc, oldPen);
  425.     DeleteObject(gridPen);
  426. }
  427.  
  428. void DrawGridWithAlpha(HDC hdc, const RECT& clientRect, int scrollX, int scrollY) {
  429.     HDC tempDC = CreateCompatibleDC(hdc);
  430.     HBITMAP tempBitmap = CreateCompatibleBitmap(hdc, clientRect.right, clientRect.bottom);
  431.     HBITMAP oldTempBitmap = (HBITMAP)SelectObject(tempDC, tempBitmap);
  432.     HBRUSH whiteBrush = CreateSolidBrush(RGB(255, 255, 255));
  433.     FillRect(tempDC, &clientRect, whiteBrush);
  434.     DeleteObject(whiteBrush);
  435.     SetBkMode(tempDC, TRANSPARENT);
  436.     HPEN gridPen = CreatePen(PS_SOLID, 1, GRID_COLOR);
  437.     HPEN oldPen = (HPEN)SelectObject(tempDC, gridPen);
  438.     int scaledGridSize = (int)(GRID_SIZE * gridZoomFactor);
  439.     int offsetX = -(scrollX % scaledGridSize);
  440.     int offsetY = -(scrollY % scaledGridSize);
  441.     for (int x = offsetX; x <= clientRect.right; x += scaledGridSize) {
  442.         MoveToEx(tempDC, x, 0, NULL);
  443.         LineTo(tempDC, x, clientRect.bottom);
  444.     }
  445.     for (int y = offsetY; y <= clientRect.bottom; y += scaledGridSize) {
  446.         MoveToEx(tempDC, 0, y, NULL);
  447.         LineTo(tempDC, clientRect.right, y);
  448.     }
  449.     SelectObject(tempDC, oldPen);
  450.     DeleteObject(gridPen);
  451.     BLENDFUNCTION bf;
  452.     bf.BlendOp = AC_SRC_OVER;
  453.     bf.BlendFlags = 0;
  454.     bf.SourceConstantAlpha = gridOpacity;
  455.     bf.AlphaFormat = 0;
  456.     AlphaBlend(hdc, 0, 0, clientRect.right, clientRect.bottom,
  457.         tempDC, 0, 0, clientRect.right, clientRect.bottom,
  458.         bf);
  459.     SelectObject(tempDC, oldTempBitmap);
  460.     DeleteObject(tempBitmap);
  461.     DeleteDC(tempDC);
  462. }
  463.  
  464. void InitializeStatusBuffer(HWND hStatus) {
  465.     if (hStatusBufferDC) {
  466.         DeleteDC(hStatusBufferDC);
  467.         DeleteObject(hStatusBufferBitmap);
  468.     }
  469.     HDC hdc = GetDC(hStatus);
  470.     RECT rect;
  471.     GetClientRect(hStatus, &rect);
  472.     hStatusBufferDC = CreateCompatibleDC(hdc);
  473.     hStatusBufferBitmap = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
  474.     SelectObject(hStatusBufferDC, hStatusBufferBitmap);
  475.     ReleaseDC(hStatus, hdc);
  476. }
  477.  
  478. void UpdateStatus(HWND hwnd) {
  479.     DWORD currentTime = GetTickCount();
  480.     if (currentTime - lastStatusUpdateTime < STATUS_UPDATE_INTERVAL) {
  481.         return;
  482.     }
  483.     lastStatusUpdateTime = currentTime;
  484.     HWND hStatus = GetDlgItem(hwnd, 0);
  485.     if (!hStatus) return;
  486.     if (!hStatusBufferDC) {
  487.         InitializeStatusBuffer(hStatus);
  488.     }
  489.     RECT statusRect;
  490.     GetClientRect(hStatus, &statusRect);
  491.     SendMessage(hStatus, WM_ERASEBKGND, (WPARAM)hStatusBufferDC, 0);
  492.     wchar_t status[512];
  493.     BYTE r = GetRValue(currentBrushColor);
  494.     BYTE g = GetGValue(currentBrushColor);
  495.     BYTE b = GetBValue(currentBrushColor);
  496.     int centerX = scrollX + (virtualWidth / 2);
  497.     int centerY = scrollY + (virtualHeight / 2);
  498.     swprintf_s(status,
  499.         L"Mode: %s | Brush: %d | Color: RGB(%d,%d,%d) | Grid: %s%s | Zoom: %.1fx | Opacity: %d%% | Canvas Pos: (%d,%d)",
  500.         isEraserMode ? L"Eraser" : L"Draw",
  501.         brushSize,
  502.         r, g, b,
  503.         showGrid ? L"On" : L"Off",
  504.         useAlphaGrid ? L"(Alpha)" : L"",
  505.         gridZoomFactor,
  506.         (gridOpacity * 100) / 255,
  507.         centerX, centerY
  508.     );
  509.     HDC hdc = GetDC(hStatus);
  510.     SetBkMode(hStatusBufferDC, TRANSPARENT);
  511.     SendMessage(hStatus, SB_SETTEXT, 0, (LPARAM)status);
  512.     BitBlt(hdc, 0, 0, statusRect.right, statusRect.bottom,
  513.         hStatusBufferDC, 0, 0, SRCCOPY);
  514.     ReleaseDC(hStatus, hdc);
  515. }
  516.  
  517. void RedrawAllStrokes() {
  518.     if (!hMemoryDC) return;
  519.  
  520.     // Clear the memory DC first
  521.     HBRUSH whiteBrush = CreateSolidBrush(RGB(255, 255, 255));
  522.     RECT rect = { 0, 0, virtualWidth, virtualHeight };
  523.     FillRect(hMemoryDC, &rect, whiteBrush);
  524.     DeleteObject(whiteBrush);
  525.  
  526.     // Redraw guide lines
  527.     HPEN guidePen = CreatePen(PS_SOLID, 1, RGB(200, 200, 200));
  528.     SelectObject(hMemoryDC, guidePen);
  529.     MoveToEx(hMemoryDC, virtualWidth / 2, 0, NULL);
  530.     LineTo(hMemoryDC, virtualWidth / 2, virtualHeight);
  531.     MoveToEx(hMemoryDC, 0, virtualHeight / 2, NULL);
  532.     LineTo(hMemoryDC, virtualWidth, virtualHeight / 2);
  533.     DeleteObject(guidePen);
  534.  
  535.     // Redraw border
  536.     HPEN edgePen = CreatePen(PS_SOLID, 2, RGB(0, 0, 255));
  537.     SelectObject(hMemoryDC, edgePen);
  538.     Rectangle(hMemoryDC, 0, 0, virtualWidth - 1, virtualHeight - 1);
  539.     DeleteObject(edgePen);
  540.  
  541.     // Redraw all strokes
  542.     std::lock_guard<std::mutex> lock(strokeMutex);
  543.     for (const auto& stroke : strokeHistory) {
  544.         DrawSmoothStroke(hMemoryDC, stroke.points, stroke.isEraser, stroke.color, stroke.brushSize);
  545.     }
  546. }
  547.  
  548. void InitializeMemoryMappedCanvas(HWND hwnd) {
  549.     // Create a file mapping object
  550.     hFileMapping = CreateFileMapping(
  551.         INVALID_HANDLE_VALUE,   // Use the system paging file
  552.         NULL,                   // Default security
  553.         PAGE_READWRITE,         // Read/write access
  554.         0,                      // Max size (high-order DWORD)
  555.         virtualWidth * virtualHeight * 4, // Max size (low-order DWORD) for 32-bit color
  556.         L"CanvasMemoryMap"      // Name of the mapping object
  557.     );
  558.  
  559.     if (!hFileMapping) {
  560.         MessageBox(hwnd, L"Failed to create memory-mapped file", L"Error", MB_OK);
  561.         return;
  562.     }
  563.  
  564.     // Map a view of the file into the address space
  565.     void* pBaseAddress = MapViewOfFile(
  566.         hFileMapping,           // Handle to the file mapping object
  567.         FILE_MAP_ALL_ACCESS,    // Read/write access
  568.         0,                      // File offset high
  569.         0,                      // File offset low
  570.         virtualWidth * virtualHeight * 4 // Size of the view
  571.     );
  572.  
  573.     if (!pBaseAddress) {
  574.         MessageBox(hwnd, L"Failed to map view of file", L"Error", MB_OK);
  575.         CloseHandle(hFileMapping);
  576.         hFileMapping = NULL;
  577.         return;
  578.     }
  579.  
  580.     // Use the memory for our bitmap
  581.     hMemoryDC = CreateCompatibleDC(NULL);
  582.     hMemoryBitmap = CreateBitmap(virtualWidth, virtualHeight, 1, 32, pBaseAddress);
  583.     hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hMemoryBitmap);
  584. }
  585.  
  586. LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  587.     switch (uMsg) {
  588.     case WM_CREATE:
  589.     {
  590.         InitializeMemoryMappedCanvas(hwnd);
  591.         if (hMemoryDC) {
  592.             HBRUSH redBrush = CreateSolidBrush(RGB(255, 0, 0));
  593.             RECT testRect = { 100, 100, 200, 200 };
  594.             FillRect(hMemoryDC, &testRect, redBrush);
  595.             DeleteObject(redBrush);
  596.             InvalidateRect(hwnd, NULL, FALSE);
  597.         }
  598.         else {
  599.             MessageBox(hwnd, L"Memory DC failed to initialize", L"Error", MB_OK);
  600.         }
  601.         UpdateStatus(hwnd);
  602.         isLoading = true;
  603.         LoadCanvasStateAsync(hwnd);
  604.         return 0;
  605.     }
  606.     case WM_SYSCOMMAND:
  607.     {
  608.         if (wParam == SC_CLOSE) {
  609.             DestroyWindow(hwnd);
  610.             return 0;
  611.         }
  612.         return DefWindowProc(hwnd, uMsg, wParam, lParam);
  613.     }
  614.     case WM_KEYDOWN:
  615.     {
  616.         if (GetKeyState(VK_MENU) & 0x8000) {
  617.             return DefWindowProc(hwnd, uMsg, wParam, lParam);
  618.         }
  619.         if (wParam == VK_SPACE && !isSpacePressed) {
  620.             isSpacePressed = true;
  621.             GetCursorPos(&lastMousePos);
  622.             ScreenToClient(hwnd, &lastMousePos);
  623.             SetCursor(LoadCursor(NULL, IDC_SIZEALL));
  624.             SetCapture(hwnd);
  625.             return 0;
  626.         }
  627.         else if (wParam == 0x50) {
  628.             isPaintbrushSelected = true;
  629.             isEraserMode = false;
  630.             UpdateStatus(hwnd);
  631.         }
  632.         else if (wParam == 0x45) {
  633.             isPaintbrushSelected = false;
  634.             isEraserMode = true;
  635.             UpdateStatus(hwnd);
  636.         }
  637.         else if (wParam == 'Q') {
  638.             ShowColorPicker(hwnd);
  639.         }
  640.         else if (wParam == VK_ADD || wParam == VK_OEM_PLUS) {
  641.             brushSize = std::min(50, brushSize + 5);
  642.             UpdateStatus(hwnd);
  643.         }
  644.         else if (wParam == VK_SUBTRACT || wParam == VK_OEM_MINUS) {
  645.             brushSize = std::max(5, brushSize - 5);
  646.             UpdateStatus(hwnd);
  647.         }
  648.         else if (wParam == 0x43) {
  649.             if (!(GetKeyState(VK_CONTROL) & 0x8000)) {
  650.                 ClearDrawing(hwnd);
  651.             }
  652.         }
  653.         else if (wParam == VK_HOME) {
  654.             RECT clientRect;
  655.             GetClientRect(hwnd, &clientRect);
  656.             scrollX = (virtualWidth - clientRect.right) / 2;
  657.             scrollY = (virtualHeight - clientRect.bottom) / 2;
  658.             InvalidateRect(hwnd, NULL, FALSE);
  659.         }
  660.         else if (wParam == 'G') {
  661.             showGrid = !showGrid;
  662.             InvalidateRect(hwnd, NULL, FALSE);
  663.             UpdateStatus(hwnd);
  664.         }
  665.         else if (wParam == 'A') {
  666.             useAlphaGrid = !useAlphaGrid;
  667.             InvalidateRect(hwnd, NULL, FALSE);
  668.             UpdateStatus(hwnd);
  669.         }
  670.         if (wParam == VK_PRIOR) {
  671.             gridZoomFactor *= 1.1f;
  672.             gridInitialized = FALSE;
  673.             InvalidateRect(hwnd, NULL, FALSE);
  674.             UpdateStatus(hwnd);
  675.         }
  676.         else if (wParam == VK_NEXT) {
  677.             gridZoomFactor *= 0.9f;
  678.             if (gridZoomFactor < 0.1f) gridZoomFactor = 0.1f;
  679.             gridInitialized = FALSE;
  680.             InvalidateRect(hwnd, NULL, FALSE);
  681.             UpdateStatus(hwnd);
  682.         }
  683.         else if (wParam == VK_OEM_6 && useAlphaGrid) {
  684.             gridOpacity = min(255, gridOpacity + 15);
  685.             InvalidateRect(hwnd, NULL, FALSE);
  686.             UpdateStatus(hwnd);
  687.         }
  688.         else if (wParam == VK_OEM_4 && useAlphaGrid) {
  689.             gridOpacity = max(0, gridOpacity - 15);
  690.             InvalidateRect(hwnd, NULL, FALSE);
  691.             UpdateStatus(hwnd);
  692.         }
  693.         else if (wParam == VK_ESCAPE) {
  694.             if (!strokeBuffer.empty()) {  // Only save if there are points
  695.                 SaveCanvasState();  // Save canvas state before exiting
  696.             }
  697.             PostQuitMessage(0);
  698.             return 0;
  699.         }
  700.         else if (wParam == VK_F1) {
  701.             MessageBox(hwnd, TEXT("I made an Infinite Canvas app using GDI and Memory DC, no need for bloated Godot Engine/ Frameworks or M$ Infinite Canvas Control! Eternity of effort paid off! (1383 lines of code) by Entisoft Software (c) Evans Thorpemorton"), TEXT("Information"), MB_OK | MB_ICONINFORMATION);
  702.         }
  703.         return 0;
  704.     }
  705.     case WM_KEYUP:
  706.     {
  707.         if (wParam == VK_SPACE) {
  708.             isSpacePressed = false;
  709.             SetCursor(LoadCursor(NULL, IDC_ARROW));
  710.             ReleaseCapture();
  711.             return 0;
  712.         }
  713.     }
  714.     case WM_LBUTTONDOWN:
  715.     {
  716.         if (!isSpacePressed) {
  717.             isDrawing = true;
  718.             SetCapture(hwnd);
  719.             int x = LOWORD(lParam);
  720.             int y = HIWORD(lParam);
  721.             int canvasX = x + scrollX;
  722.             int canvasY = y + scrollY;
  723.             if (canvasX >= 0 && canvasX <= virtualWidth &&
  724.                 canvasY >= 0 && canvasY <= virtualHeight) {
  725.                 StartStroke(canvasX, canvasY);
  726.                 DrawBrush(hMemoryDC, canvasX, canvasY, isEraserMode, false);
  727.                 HDC screenDC = GetDC(hwnd);
  728.                 DrawBrush(screenDC, x, y, isEraserMode, false);
  729.                 ReleaseDC(hwnd, screenDC);
  730.             }
  731.         }
  732.         return 0;
  733.     }
  734.     // In WM_LBUTTONUP case:
  735.     case WM_LBUTTONUP: {
  736.         if (isPanning) {
  737.             isPanning = false;
  738.             ReleaseCapture();
  739.         }
  740.         if (isDrawing) {
  741.             isDrawing = false;
  742.             if (!strokeBuffer.empty()) {  // Only save if there are points
  743.                 SerializedStroke stroke;
  744.                 stroke.points = strokeBuffer;  // Save all accumulated points
  745.                 stroke.color = currentBrushColor;
  746.                 stroke.brushSize = brushSize;
  747.                 stroke.isEraser = isEraserMode;
  748.  
  749.                 {
  750.                     std::lock_guard<std::mutex> lock(strokeMutex);
  751.                     strokeHistory.push_back(stroke);
  752.                 }
  753.  
  754.                 // Save the stroke incrementally
  755.                 SaveIncrementalStroke(stroke);
  756.             }
  757.             EndStroke();
  758.             ReleaseCapture();
  759.         }
  760.         return 0;
  761.     }
  762.     case WM_MOUSEMOVE:
  763.     {
  764.         int x = LOWORD(lParam);
  765.         int y = HIWORD(lParam);
  766.         if (isSpacePressed) {
  767.             RECT clientRect;
  768.             GetClientRect(hwnd, &clientRect);
  769.             int deltaX = x - lastMousePos.x;
  770.             int deltaY = y - lastMousePos.y;
  771.             int newScrollX = std::max(0, std::min<int>(virtualWidth - clientRect.right, scrollX - deltaX));
  772.             int newScrollY = std::max(0, std::min<int>(virtualHeight - clientRect.bottom, scrollY - deltaY));
  773.             if (newScrollX != scrollX || newScrollY != scrollY) {
  774.                 scrollX = newScrollX;
  775.                 scrollY = newScrollY;
  776.                 ScrollWindowEx(hwnd, deltaX, deltaY,
  777.                     NULL, NULL, NULL, NULL,
  778.                     SW_INVALIDATE | SW_ERASE);
  779.                 if (abs(deltaX) > 5 || abs(deltaY) > 5) {
  780.                     UpdateStatus(hwnd);
  781.                 }
  782.             }
  783.             lastMousePos.x = x;
  784.             lastMousePos.y = y;
  785.         }
  786.         else if (isDrawing && (wParam & MK_LBUTTON)) {
  787.             int canvasX = x + scrollX;
  788.             int canvasY = y + scrollY;
  789.             if (canvasX >= 0 && canvasX <= virtualWidth &&
  790.                 canvasY >= 0 && canvasY <= virtualHeight) {
  791.  
  792.                 DrawPoint newPoint(canvasX, canvasY);
  793.                 strokeBuffer.push_back(newPoint);  // Add point to buffer
  794.  
  795.                 // Draw to memory DC
  796.                 DrawSmoothStroke(hMemoryDC, strokeBuffer, isEraserMode, currentBrushColor, brushSize);
  797.  
  798.                 // Draw to screen
  799.                 HDC screenDC = GetDC(hwnd);
  800.                 std::vector<DrawPoint> screenPoints;
  801.                 for (const auto& pt : strokeBuffer) {
  802.                     screenPoints.push_back(DrawPoint(pt.x - scrollX, pt.y - scrollY));
  803.                 }
  804.                 DrawSmoothStroke(screenDC, screenPoints, isEraserMode, currentBrushColor, brushSize);
  805.                 ReleaseDC(hwnd, screenDC);
  806.  
  807.                 if (GetTickCount() - lastStatusUpdateTime > STATUS_UPDATE_INTERVAL * 2) {
  808.                     UpdateStatus(hwnd);
  809.                 }
  810.             }
  811.         }
  812.         return 0;
  813.     }
  814.     case WM_SIZE:
  815.     {
  816.         HWND hStatus = GetDlgItem(hwnd, 0);
  817.         if (hStatus) {
  818.             SendMessage(hStatus, WM_SIZE, 0, 0);
  819.             InitializeStatusBuffer(hStatus);
  820.             UpdateStatus(hwnd);
  821.         }
  822.         if (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED) {
  823.             InvalidateRect(hwnd, NULL, TRUE);
  824.         }
  825.         return 0;
  826.     }
  827.     case WM_PAINT:
  828.     {
  829.         PAINTSTRUCT ps;
  830.         HDC hdc = BeginPaint(hwnd, &ps);
  831.         RECT clientRect;
  832.         GetClientRect(hwnd, &clientRect);
  833.         int windowWidth = clientRect.right - clientRect.left;
  834.         int windowHeight = clientRect.bottom - clientRect.top;
  835.  
  836.         HDC memDC = CreateCompatibleDC(hdc);
  837.         HBITMAP memBitmap = CreateCompatibleBitmap(hdc, windowWidth, windowHeight);
  838.         HBITMAP oldBitmap = (HBITMAP)SelectObject(memDC, memBitmap);
  839.  
  840.         HBRUSH whiteBrush = CreateSolidBrush(RGB(255, 255, 255));
  841.         FillRect(memDC, &clientRect, whiteBrush);
  842.         DeleteObject(whiteBrush);
  843.  
  844.         BitBlt(memDC, 0, 0, windowWidth, windowHeight, hMemoryDC, scrollX, scrollY, SRCCOPY);
  845.  
  846.         if (showGrid) {
  847.             if (useAlphaGrid) {
  848.                 DrawGridWithAlpha(memDC, clientRect, scrollX, scrollY);
  849.             }
  850.             else {
  851.                 DrawGrid(memDC, clientRect, scrollX, scrollY);
  852.             }
  853.         }
  854.  
  855.         BitBlt(hdc, 0, 0, windowWidth, windowHeight, memDC, 0, 0, SRCCOPY);
  856.  
  857.         SelectObject(memDC, oldBitmap);
  858.         DeleteObject(memBitmap);
  859.         DeleteDC(memDC);
  860.  
  861.         EndPaint(hwnd, &ps);
  862.         return 0;
  863.     }
  864.     case WM_SETCURSOR:
  865.     {
  866.         if (LOWORD(lParam) == HTCLIENT) {
  867.             if (isSpacePressed) {
  868.                 SetCursor(LoadCursor(NULL, IDC_SIZEALL));
  869.                 return TRUE;
  870.             }
  871.             else if (isPaintbrushSelected || isEraserMode) {
  872.                 SetCursor(LoadCursor(NULL, IDC_CROSS));
  873.                 return TRUE;
  874.             }
  875.         }
  876.         return DefWindowProc(hwnd, uMsg, wParam, lParam);
  877.     }
  878.     case WM_MOUSEWHEEL:
  879.     {
  880.         int delta = GET_WHEEL_DELTA_WPARAM(wParam);
  881.         if (GetKeyState(VK_CONTROL) & 0x8000) {
  882.             if (delta > 0)
  883.                 gridZoomFactor *= 1.1f;
  884.             else
  885.                 gridZoomFactor *= 0.9f;
  886.             InvalidateRect(hwnd, NULL, FALSE);
  887.         }
  888.         return 0;
  889.     }
  890.     case WM_DESTROY:
  891.     {
  892.         // Save the canvas state before exiting
  893.         if (!strokeHistory.empty()) {
  894.             SaveCanvasState();
  895.         }
  896.  
  897.         // Clean up resources
  898.         if (hGridDC) {
  899.             DeleteDC(hGridDC);
  900.             hGridDC = NULL;
  901.         }
  902.         if (hGridBitmap) {
  903.             DeleteObject(hGridBitmap);
  904.             hGridBitmap = NULL;
  905.         }
  906.         if (hMemoryDC) {
  907.             if (hOldBitmap) {
  908.                 SelectObject(hMemoryDC, hOldBitmap);
  909.                 hOldBitmap = NULL;
  910.             }
  911.             DeleteDC(hMemoryDC);
  912.             hMemoryDC = NULL;
  913.         }
  914.         if (hMemoryBitmap) {
  915.             DeleteObject(hMemoryBitmap);
  916.             hMemoryBitmap = NULL;
  917.         }
  918.         if (hStatusBufferDC) {
  919.             DeleteDC(hStatusBufferDC);
  920.             hStatusBufferDC = NULL;
  921.         }
  922.         if (hStatusBufferBitmap) {
  923.             DeleteObject(hStatusBufferBitmap);
  924.             hStatusBufferBitmap = NULL;
  925.         }
  926.  
  927.         PostQuitMessage(0);
  928.         return 0;
  929.     }
  930.     default:
  931.         return DefWindowProc(hwnd, uMsg, wParam, lParam);
  932.     }
  933.     return 0;
  934. }
  935.  
  936. INITCOMMONCONTROLSEX icex;
  937.  
  938. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) {
  939.     //GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
  940.     icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
  941.     icex.dwICC = ICC_BAR_CLASSES;
  942.     InitCommonControlsEx(&icex);
  943.  
  944.     const wchar_t CLASS_NAME[] = L"DoodleAppClass";
  945.  
  946.     WNDCLASS wc = { };
  947.     wc.lpfnWndProc = WindowProc;
  948.     wc.hInstance = hInstance;
  949.     wc.lpszClassName = CLASS_NAME;
  950.     wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
  951.  
  952.     RegisterClass(&wc);
  953.  
  954.     hInst = hInstance;
  955.  
  956.     HWND hwnd = CreateWindowEx(
  957.         0,
  958.         CLASS_NAME,
  959.         L"Infinite Canvas Doodle App (P=Brush E=Eraser C=Clear +-=BrushSize Space+Drag=Scroll Home=Center Q=Color G=Grid A=Alpha""[]"" PgUp = ZoomIn PgDown = ZoomOut F1 = About)",
  960.         WS_OVERLAPPEDWINDOW | WS_MAXIMIZE,
  961.         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  962.         NULL,
  963.         NULL,
  964.         hInstance,
  965.         NULL
  966.     );
  967.  
  968.     SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_COMPOSITED);
  969.  
  970.     if (hwnd == NULL) {
  971.         return 0;
  972.     }
  973.  
  974.     HWND hStatus = CreateWindowEx(
  975.         0,
  976.         STATUSCLASSNAME,
  977.         NULL,
  978.         WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
  979.         0, 0, 0, 0,
  980.         hwnd,
  981.         (HMENU)0,
  982.         hInstance,
  983.         NULL
  984.     );
  985.  
  986.     if (hStatus) {
  987.         int statwidths[] = { -1 };
  988.         SendMessage(hStatus, SB_SETPARTS, 1, (LPARAM)statwidths);
  989.         UpdateStatus(hwnd);
  990.     }
  991.  
  992.     ShowWindow(hwnd, SW_SHOWMAXIMIZED);
  993.  
  994.     MSG msg = {};
  995.     while (GetMessage(&msg, NULL, 0, 0)) {
  996.         TranslateMessage(&msg);
  997.         DispatchMessage(&msg);
  998.     }
  999.  
  1000.     GdiplusShutdown(gdiplusToken);
  1001.     return 0;
  1002. }
Tags: #buggy-code
Add Comment
Please, Sign In to add comment