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

如何在SDL2中无窗口绘制矩形并保存图片?

问题

能否在不弹出窗口的情况下,在图片上绘制矩形?我已通过以下代码实现了在图片上绘制矩形,但不想弹出窗口,是否可行?我想要实现的流程是:加载JPG图片→绘制矩形→保存为JPG。

int main(int argc, char** argv)
{
        bool quit = false;
        SDL_Event event;

        SDL_Init(SDL_INIT_VIDEO);
        IMG_Init(IMG_INIT_JPG);

        SDL_Window* window = SDL_CreateWindow("Test",
                SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);
        SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
        SDL_Surface* image = IMG_Load("test.jpg");

        if (!image) {
                printf("Image NULL\n");
                return -1;
        }

        SDL_Texture* texture = SDL_CreateTexture(renderer,
                SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC,
                image->w, image->h);
        image = SDL_ConvertSurfaceFormat(image, SDL_PIXELFORMAT_ARGB8888, 0);

        while (!quit)
        {
                SDL_UpdateTexture(texture, NULL, image->pixels,
                        image->w * sizeof(Uint32));

                SDL_WaitEvent(&event);

                switch (event.type)
                {
                case SDL_QUIT:
                        quit = true;
                        break;
                }

                SDL_SetRenderDrawColor(renderer, 136, 8, 8, 255);

                SDL_Rect dstrect = { 20, 10, 320, 145};
                SDL_RenderCopy(renderer, texture, NULL, NULL);
                SDL_RenderDrawRect(renderer, &dstrect);
                SDL_RenderPresent(renderer);
        }

        SDL_DestroyTexture(texture);
        SDL_FreeSurface(image);
        SDL_DestroyRenderer(renderer);
        SDL_DestroyWindow(window);
        IMG_Quit();
        SDL_Quit();

        return 0;
}
解决方案

当然可以实现——完全不需要弹出可见窗口就能完成图片加载、绘制矩形和保存的流程。以下是两种高效的实现方案:

方案一:直接在SDL_Surface上绘制矩形

跳过渲染器和窗口,直接在图像表面上操作像素绘制矩形,这种方式轻量且高效:

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <stdio.h>

int main(int argc, char** argv)
{
    SDL_Init(SDL_INIT_VIDEO);
    IMG_Init(IMG_INIT_JPG);

    // 加载原始JPG图片
    SDL_Surface* image = IMG_Load("test.jpg");
    if (!image) {
        printf("加载图片失败: %s\n", IMG_GetError());
        return -1;
    }

    // 转换为支持Alpha的ARGB8888格式(确保颜色绘制正常)
    SDL_Surface* drawSurface = SDL_ConvertSurfaceFormat(image, SDL_PIXELFORMAT_ARGB8888, 0);
    SDL_FreeSurface(image); // 释放原始表面

    if (!drawSurface) {
        printf("格式转换失败: %s\n", SDL_GetError());
        IMG_Quit();
        SDL_Quit();
        return -1;
    }

    // 将目标颜色映射为表面支持的像素格式
    SDL_Color rectColor = {136, 8, 8, 255};
    Uint32 color = SDL_MapRGBA(drawSurface->format, rectColor.r, rectColor.g, rectColor.b, rectColor.a);

    // 定义要绘制的矩形区域
    SDL_Rect dstrect = {20, 10, 320, 145};

    // 绘制矩形边框(逐像素操作)
    // 绘制上下边
    for (int x = dstrect.x; x < dstrect.x + dstrect.w; x++) {
        Uint32* topPixel = (Uint32*)((Uint8*)drawSurface->pixels + dstrect.y * drawSurface->pitch + x * sizeof(Uint32));
        *topPixel = color;
        Uint32* bottomPixel = (Uint32*)((Uint8*)drawSurface->pixels + (dstrect.y + dstrect.h - 1) * drawSurface->pitch + x * sizeof(Uint32));
        *bottomPixel = color;
    }
    // 绘制左右边
    for (int y = dstrect.y; y < dstrect.y + dstrect.h; y++) {
        Uint32* leftPixel = (Uint32*)((Uint8*)drawSurface->pixels + y * drawSurface->pitch + dstrect.x * sizeof(Uint32));
        *leftPixel = color;
        Uint32* rightPixel = (Uint32*)((Uint8*)drawSurface->pixels + y * drawSurface->pitch + (dstrect.x + dstrect.w - 1) * sizeof(Uint32));
        *rightPixel = color;
    }

    // 保存修改后的图片到output.jpg,质量设为90(0-100,数值越高质量越好)
    if (!IMG_SaveJPG(drawSurface, "output.jpg", 90)) {
        printf("保存图片失败: %s\n", IMG_GetError());
    } else {
        printf("图片已成功保存为output.jpg\n");
    }

    // 释放所有资源
    SDL_FreeSurface(drawSurface);
    IMG_Quit();
    SDL_Quit();

    return 0;
}

方案二:使用离屏渲染器(隐藏窗口)

如果习惯用SDL渲染器的API,可以创建一个隐藏窗口,在后台完成渲染,再导出结果:

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <stdio.h>

int main(int argc, char** argv)
{
    SDL_Init(SDL_INIT_VIDEO);
    IMG_Init(IMG_INIT_JPG);

    // 加载原始图片
    SDL_Surface* image = IMG_Load("test.jpg");
    if (!image) {
        printf("加载图片失败: %s\n", IMG_GetError());
        return -1;
    }

    // 创建与图片尺寸一致的隐藏窗口
    SDL_Window* window = SDL_CreateWindow("Hidden Render",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        image->w, image->h, SDL_WINDOW_HIDDEN);
    // 创建软件渲染器(避免依赖硬件加速)
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);

    // 将图片转换为纹理
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, image);
    SDL_FreeSurface(image);

    // 设置矩形绘制颜色(红棕色)
    SDL_SetRenderDrawColor(renderer, 136, 8, 8, 255);

    // 执行渲染流程
    SDL_RenderClear(renderer);
    SDL_RenderCopy(renderer, texture, NULL, NULL); // 绘制原始图片
    SDL_Rect dstrect = {20, 10, 320, 145};
    SDL_RenderDrawRect(renderer, &dstrect); // 绘制矩形边框
    SDL_RenderPresent(renderer);

    // 将渲染结果读取到Surface中
    SDL_Surface* resultSurface = SDL_CreateRGBSurface(0, image->w, image->h, 32,
        0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
    SDL_RenderReadPixels(renderer, NULL, resultSurface->format->format,
        resultSurface->pixels, resultSurface->pitch);

    // 保存图片
    if (!IMG_SaveJPG(resultSurface, "output.jpg", 90)) {
        printf("保存图片失败: %s\n", IMG_GetError());
    } else {
        printf("图片已成功保存为output.jpg\n");
    }

    // 释放资源
    SDL_FreeSurface(resultSurface);
    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    IMG_Quit();
    SDL_Quit();

    return 0;
}

核心优化点

  • 移除了原代码中的事件循环和窗口显示逻辑,避免弹出窗口
  • 新增IMG_SaveJPG调用,完成最终图片的保存
  • 方案一直接操作像素,性能开销更低;方案二兼容SDL渲染器的API,适合复杂绘制场景
  • 确保所有SDL资源都被正确释放,避免内存泄漏

内容的提问来源于stack exchange,提问作者lifang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 19:02:12