alien_fx_fiend

Infinite Canvas Doodle App (Optmizations Applied) *FINAL RELEASE*

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