#ifndef __UI_SURFACE_HPP__ #define __UI_SURFACE_HPP__ #include #include #include #include #include "Theme.hpp" namespace jdi { /** * @brief 一块可绘制表面(DC + 矩形 + 可选像素直写视图)。 * * 这是"整个应用共享一张后台位图"这一设想的落地载体:宿主创建一次, * 之后把"我负责哪一块"以 `UiSurface::SubView()` 的形式发给子元素。 * * ## 为什么需要 `version` * * 位图会在 `WM_SIZE` / `WM_DPICHANGED` 时被销毁重建。任何缓存下来的 `bits` * **立刻变成野指针**,因此像素指针必须与"位图代次"绑定:版本不一致就作废重取。 * ⚠️ **绝不缓存裸 `bits` 跨帧使用**(本项目历史上因缓存裸指针踩过悬垂访问)。 * * ## 什么时候才需要 `bits` * * `dc + rect` 已经足够做 GDI/GDI+ 绘制(往子矩形里画本来就是 O(1))。 * `bits` 只在**CPU 侧直接改像素**(memcpy / alpha 混合 / 重采样)时才有意义; * 用它来"画控件"是净亏损(会破坏 ClearType、引入 stride/翻转/写序错误)。 */ struct UiSurface { HDC dc = nullptr; /**< 目标 DC(GDI / GDI+ 都画这个)。 */ gd::RectF rect; /**< 该表面在宿主坐标系中的矩形(设备像素)。 */ int stride = 0; /**< 行字节数(**不可**假设等于 width × 4)。 */ std::uint32_t* bits = nullptr; /**< `rect` 左上角那一像素(不是位图首地址)。 */ std::uint64_t version = 0; /**< 位图代次;失配 ⇒ 该表面作废。 */ float scale = 1.0f; /**< DPI 缩放系数(逻辑像素 → 设备像素)。 */ /** @brief 表面是否可用。 */ bool Valid() const { return dc != nullptr && rect.Width > 0.0f && rect.Height > 0.0f; } /** * @brief 派生子视图(相对本表面左上角偏移)。 * * `bits` 会按 `stride` 正确换算到子矩形左上角;子矩形会被夹在本表面之内。 */ UiSurface SubView(const gd::RectF& localRect) const; }; } // namespace jdi #endif // __UI_SURFACE_HPP__