You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

数字涂色项目Undo/Redo功能异常:undoneActions栈问题排查

撤销重做功能BUG排查:仅能恢复最后一次撤销的操作

我在数字涂色项目中实现了撤销(UndoLastPaint)和重做(MoveForward)功能,用来在用户误涂时恢复界面状态。目前撤销功能正常,但重做功能只能恢复最后一次撤销的方块,哪怕连续执行了多次撤销也没用。怀疑是undoneActions栈的问题,求排查帮助。

相关代码

void UndoLastPaint(HWND hWnd)
{
    if (!paintedCoordinates.empty())
    {
        // Retrieve the coordinates of the last painted square
        int x = paintedCoordinates.back().first;
        int y = paintedCoordinates.back().second;

        // Check if the square was previously painted
        bool wasPainted = false;
        COLORREF color = RGB(255, 255, 255); // Default color if the square was not painted
        for (int i = paintedCoordinates.size() - 2; i >= 0; --i) {
            if (paintedCoordinates[i].first == x && paintedCoordinates[i].second == y) {
                wasPainted = true;
                color = paintedColors[i]; // Store color from the loop
                // Store the undone action
                PaintAction undoneAction;
                undoneAction.x = x;
                undoneAction.y = y;
                undoneAction.color = color;
                undoneActions.push(undoneAction);
                break;
            }
        }

        if (wasPainted) {
            // Restore the previous color of the square
            RECT squareRect = { x * squareSize, y * squareSize, (x + 1) * squareSize, (y + 1) * squareSize };
            HDC hdc = GetDC(hWnd);
            HBRUSH brush = CreateSolidBrush(color);
            FillRect(hdc, &squareRect, brush);
            ReleaseDC(hWnd, hdc);
        }
        else {
            // Calculate the rectangle occupied by the last painted square
            RECT squareRect = { x * squareSize, y * squareSize, (x + 1) * squareSize, (y + 1) * squareSize };

            // Remove the last painted square from the screen
            InvalidateRect(hWnd, &squareRect, TRUE);
            UpdateWindow(hWnd);
        }

        // Remove the last painted square from the data collection
        paintedCoordinates.pop_back();
        paintedColors.pop_back();
    }
}


void MoveForward(HWND hWnd)
{
    if (!undoneActions.empty())
    {
        // Retrieve the coordinates and color of the last undone action
        int x = undoneActions.top().x;
        int y = undoneActions.top().y;
        COLORREF color = undoneActions.top().color;

        // Remove the next undone action from the stack
        undoneActions.pop();

        // Paint the square at the next undone action
        PaintSquare(hWnd, x, y);

        // Store the repainted square in the data collection
        paintedCoordinates.push_back({ x, y });
        paintedColors.push_back(color);
    }
}

问题根源

你的UndoLastPaint函数只在方块之前被涂过色(wasPainted = true)的场景下,才会往undoneActions栈添加记录。但如果是第一次涂空白方块(wasPainted = false),这步撤销完全没有生成对应的重做记录,导致连续撤销时,这类操作不会被存入栈,自然无法被重做。

另外,当前记录到undoneActions里的颜色是恢复后的颜色,而不是撤销前的涂色,这会导致重做时涂错颜色。

修复方案

1. 修改UndoLastPaint函数,确保所有撤销动作都被记录

void UndoLastPaint(HWND hWnd)
{
    if (!paintedCoordinates.empty())
    {
        // 获取最后一次涂色的坐标和涂上去的颜色
        int x = paintedCoordinates.back().first;
        int y = paintedCoordinates.back().second;
        COLORREF undoBeforeColor = paintedColors.back();

        bool wasPainted = false;
        COLORREF restoreColor = RGB(255, 255, 255); // 默认恢复为白色
        for (int i = paintedCoordinates.size() - 2; i >= 0; --i) {
            if (paintedCoordinates[i].first == x && paintedCoordinates[i].second == y) {
                wasPainted = true;
                restoreColor = paintedColors[i];
                break;
            }
        }

        // 不管之前有没有涂色,都记录撤销动作——要重做的是撤销前的颜色(undoBeforeColor)
        PaintAction undoneAction;
        undoneAction.x = x;
        undoneAction.y = y;
        undoneAction.color = undoBeforeColor;
        undoneActions.push(undoneAction);

        if (wasPainted) {
            // 恢复之前的颜色
            RECT squareRect = { x * squareSize, y * squareSize, (x + 1) * squareSize, (y + 1) * squareSize };
            HDC hdc = GetDC(hWnd);
            HBRUSH brush = CreateSolidBrush(restoreColor);
            FillRect(hdc, &squareRect, brush);
            DeleteObject(brush); // 必须删除GDI对象,避免内存泄漏
            ReleaseDC(hWnd, hdc);
        }
        else {
            // 恢复空白
            RECT squareRect = { x * squareSize, y * squareSize, (x + 1) * squareSize, (y + 1) * squareSize };
            InvalidateRect(hWnd, &squareRect, TRUE);
            UpdateWindow(hWnd);
        }

        paintedCoordinates.pop_back();
        paintedColors.pop_back();
    }
}

2. 确保MoveForward使用正确的颜色涂色

检查PaintSquare函数,如果它是用当前选中的颜色而不是传入的参数,需要修改函数定义和调用:

// 修改PaintSquare,支持传入颜色参数
void PaintSquare(HWND hWnd, int x, int y, COLORREF color)
{
    RECT squareRect = { x * squareSize, y * squareSize, (x + 1) * squareSize, (y + 1) * squareSize };
    HDC hdc = GetDC(hWnd);
    HBRUSH brush = CreateSolidBrush(color);
    FillRect(hdc, &squareRect, brush);
    DeleteObject(brush);
    ReleaseDC(hWnd, hdc);
}

// 修改MoveForward里的调用
void MoveForward(HWND hWnd)
{
    if (!undoneActions.empty())
    {
        int x = undoneActions.top().x;
        int y = undoneActions.top().y;
        COLORREF color = undoneActions.top().color;

        undoneActions.pop();

        PaintSquare(hWnd, x, y, color); // 传入记录的颜色

        paintedCoordinates.push_back({ x, y });
        paintedColors.push_back(color);
    }
}

3. 额外修复:GDI资源泄漏

原代码中创建HBRUSH后没有调用DeleteObject,会导致GDI资源耗尽,修复后的代码已经补上了这一步。

内容的提问来源于stack exchange,提问作者tetiana.mlk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 18:55:23