惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
V
Visual Studio Blog
C
Check Point Blog
Google DeepMind News
Google DeepMind News
S
SegmentFault 最新的问题
博客园 - 聂微东
量子位
T
Tailwind CSS Blog
罗磊的独立博客
I
InfoQ
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Y
Y Combinator Blog
L
LangChain Blog
小众软件
小众软件
Engineering at Meta
Engineering at Meta
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Security Latest
Security Latest
M
MIT News - Artificial intelligence
Know Your Adversary
Know Your Adversary
MongoDB | Blog
MongoDB | Blog
Google DeepMind News
Google DeepMind News
大猫的无限游戏
大猫的无限游戏
H
Help Net Security
爱范儿
爱范儿
T
The Exploit Database - CXSecurity.com
有赞技术团队
有赞技术团队
V
Vulnerabilities – Threatpost
Martin Fowler
Martin Fowler
A
Arctic Wolf
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 司徒正美
Cyberwarzone
Cyberwarzone
阮一峰的网络日志
阮一峰的网络日志
The Hacker News
The Hacker News
Apple Machine Learning Research
Apple Machine Learning Research
宝玉的分享
宝玉的分享
GbyAI
GbyAI
Latest news
Latest news
云风的 BLOG
云风的 BLOG
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
腾讯CDC
AWS News Blog
AWS News Blog
aimingoo的专栏
aimingoo的专栏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
L
Lohrmann on Cybersecurity
博客园 - Franky
S
Securelist
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Threatpost
美团技术团队

豆沙工作室

从 Gitea 迁移到 Forgejo 评论里的私信消息 记得清理你的日志文件 招魂 Annihilation [11ty] 使用 Pagefind 实现静态站点的搜索 直面死亡 [11ty] 修复 RSS 里的数学公式 看到了一个神奇的 UA PreFound:10B - 四次击键 一点小小的 AI 震撼 PreFound:10A - 文本之外,还有(绘)文字 PreFound:09 - 色彩运算子 计算机不予申辩 Re-Game 2.0 Addendum C - Press START PreFound:08 - 动效与仿射 PreFound:06 - 漫游雪花海 PreFound:05 - 写出到屏幕 And Trek I Will PreFound:04 - 举起渲染器 PreFound:03 - 跨越比特面 PreFound:02 - 启动编码器 PreFound:01 - 新建文件夹 PreFound:00 - 梦开始的地方 向着无穷远跑去 如何打包一个 Python 项目 只能作为光盘手册存在的书籍 航天飞机和马屁股的关系 Get an iPod 从鸿蒙系统中拉取蓝牙 HCI 日志 第二类擦除 迁移 FreshRSS 的数据库
PreFound:07 - 图片拼贴报
https://archive.dsstudio.tech/person/ · 2026-05-26 · via 豆沙工作室

来点图片是正确的、必要的、鼓舞人心的。

上期决定添加图片的支持。SDL 作为一个 2D 图形库,对于图片自然有一流的支持,用起来也非常舒心。

导入

和字体支持一样,SDL 的图片支持也是单独成库的。编辑 vcpkg.json 引入 SDL3_image 库:

{
    /* ... */
    "dependencies": [
        /* ... */
        {
            "name": "sdl3-image",
            "features": [
                "jpeg",
                "png"
            ]
        }
    ]
    /* ... */
}

注意到常见的两个图像格式 .jpg.png 需要作为功能单独开启。SDL 底层实际会去调用 libjpeg-turbolibpng 两个库来处理这两个类型的图像。

然后加入对应的链接库:

# ...
find_package(SDL3_image CONFIG REQUIRED)
# ...
target_link_libraries(Foundation
    $<IF:$<TARGET_EXISTS:SDL3_image::SDL3_image-shared>,SDL3_image::SDL3_image-shared,SDL3_image::SDL3_image-static>
)

就可以使用了。

加载图像

和 SDL 2 版本不同,SDL 3 版本的图形库不再需要手动调用 IMG_Init(.) 来初始化支持了。它会在你首次使用对应的图像类型的时候,自动初始化底层库。

要加载一个图像,我们只需要执行 IMG_LoadTexture(.) 就可以将图像加载成为一个纹理。

计算机图像处理一般到了这个时候,就该抽出一张莱娜图了。不过这回我们还是用点别的吧——

I retired from modeling a long time ago. It's time I retired from tech, too.

auto* image = IMG_LoadTexture(sdl.renderer(), "assets/image/pepper.png");

将绘制抽象成类

后续我们应该会用到更多的需要引用外部资源,或者有复杂绘制流程。如果都摊开的话未免有点太乱。以图像为例子,我们可以构建一个类来封装它的资源申请、释放和绘制操作:

class Image {
public:
    Image(SdlContext *sdl, int x, int y, const char* path) : _sdl(sdl), _x(x), _y(y) {
        _image = IMG_LoadTexture(_sdl->renderer(), path);
    }

    ~Image() {
        if (_image) {
            SDL_DestroyTexture(_image);
        }
    }

    void render(SdlContext *sdl) {
        if (_image) {
            _draw_image();
        } else {
            _draw_missing_texture();
        }
    }

private:
    SdlContext* _sdl;
    int _x, _y;
    SDL_Texture* _image{nullptr};

    void _draw_image() { /* ... */ }
    void _draw_missing_texture() { /* ... */ }
}

然后我们就可以这样进行调用:

Image img(&sdl, 10, 10, "assets/image/pepper.png");
img.render(&sdl);

处理资源文件找不到的情况

有些时候,图片文件可能恰好找不到或者是损坏的。这会使得 IMG_LoadTexture(.) 返回一个 nullptr. 我们在绘制的时候需要处理这种情况。

业界常用的方法是绘制一个「材质丢失」站位图,一般是洋红和黑色组成的棋盘格。由于没有图片加载的情况下,我们无从知道这个图像具体的宽高,所以就先统一使用 200x200 大小。

void _draw_missing_texture() {
    for (int y = 0, cy = 0; y < 200; y += 16, cy++) {
        for (int x = 0, cx = 0; x < 200; x += 16, cx++) {
            const SDL_FRect rect = {
                    .x = _x + static_cast<float>(x),
                    .y = _y + static_cast<float>(y),
                    .w = 16.f,
                    .h = 16.f,
            };

            if ((xc + yc) % 2 == 0) {
                SDL_SetRenderDrawColor(_sdl->renderer(), 255, 0, 255, 255);
            } else {
                SDL_SetRenderDrawColor(_sdl->renderer(), 0, 0, 0, 255);
            }
            SDL_RenderFillRect(_sdl->renderer(), &rect);
        }
    }
}

checkerboard.png

图 1 棋盘格

绘制图像

图像绘制本身非常简单,只是一个纹理复制的事情:

void _draw_image() {
    const SDL_FRect dstRect = {
        .x = _x,
        .y = _y,
        .w = _image->w,
        .h = _image->h
    };
    SDL_SetRenderTarget(_sdl->renderer(), _sdl->texture());
    SDL_RenderTexture(_sdl->renderer(), _image, nullptr, &dstRect);
}

pepper.png

图 2 彩椒图案

下一步

现在,图形绘制原语已经基本齐备。接下来或许可以研究一些动画效果了。