在 SOUI 中使用 SVG¶
概述¶
在高分屏时代,传统位图资源(PNG、BMP 等)已难以满足现代 UI 开发对多分辨率适配、资源体积和视觉效果的要求。SVG(Scalable Vector Graphics,可缩放矢量图形)作为一种基于 XML 的矢量图形格式,凭借无损缩放、体积小、易编辑等优势,被广泛应用于各类 UI 开发中。
SOUI 框架通过集成轻量级 SVG 解析引擎,并与自身的 RenderTarget 渲染系统深度结合,为开发者提供了一套高效、灵活的 SVG 资源解决方案。
SVG 支持方案:技术实现¶
1. 轻量级 SVG 解码引擎¶
SOUI 采用 NanoSVG 库作为 SVG 解析引擎,这是一个专为嵌入式系统和游戏开发设计的轻量级库:
技术特点:
- 轻量级:NanoSVG 代码量小,集成成本低
- 高性能:解析速度快,内存占用小
- 跨平台:支持 Windows、Linux 等平台
SOUI 的扩展改进:
- Text 标签支持:在原版 NanoSVG 基础上增加了
<text>标签支持,使 SVG 可以包含文本元素 - 渐变参数修复:修复了渐变参数传递的问题,确保线性渐变和径向渐变能够正确渲染
- 属性兼容性:解决了 SVG 属性大小写区分的问题,提高了兼容性
2. 与 RenderTarget 的深度结合¶
SOUI 的核心特色在于将 SVG 与自身的 RenderTarget 渲染系统进行了有机结合,无需引入额外的渲染库,完全基于 SOUI 自身的渲染系统:
// IRenderTarget 接口中扩展的 DrawSVG 方法
STDMETHOD_(HRESULT, DrawSVG)(THIS_
ISvgObj * pSvg, // SVG对象
LPCRECT pRect, // 目标绘制区域
LPCRECT prcSrc = NULL, // 源SVG区域(可选)
BYTE byAlpha = 0xFF // 透明度(0-255)
) PURE;
技术原理: 利用 SOUI 现有的矢量图形渲染能力,将 SVG 的路径、填充、描边等元素直接转换为 RenderTarget 可处理的绘制命令,避免了"先光栅化后渲染"的传统方式,完整保留了 SVG 的矢量优势。
3. 九宫格绘制支持¶
SOUI 为 SVG 实现了原生的九宫格绘制功能,这是 UI 开发中极为常用的特性:
实现原理:
将 SVG 图像分割为 9 个部分(4 个角、4 条边、1 个中心),分别调用 DrawSVG 绘制到目标区域的对应位置,保持边角不变形,边框和背景独立拉伸。
适用场景: - 按钮背景(圆角保持不变) - 窗口边框(边角弧度保持) - 输入框、列表项等需要拉伸的 UI 元素
4. 智能 SVG 加载机制¶
SOUI 的 SVG 加载机制设计简洁,支持两种识别方式:
- 资源类型前缀识别:通过
SVG:前缀识别,例如svg:btn_play - 文件扩展名识别:自动识别
.svg文件扩展名
加载逻辑(简化版):
void SSkinImgList::LoadSrcImage() const
{
if (m_strSrc.IsEmpty()) return;
SStringWList list;
SplitString(m_strSrc, L':', list);
if (list.GetCount() == 1) {
list.InsertAt(0, L"file");
}
list[0].MakeLower();
list[1].MakeLower();
// 识别SVG资源
if (list[0] == L"svg" ||
(list[0] == L"file" && list[1].EndsWith(L"svg") == 0))
{
m_pSvg.Attach(CreateSvgFromResId(S_CW2T(m_strSrc)));
}
else
{
m_pImg.Attach(LOADIMAGE2(m_strSrc));
}
}
5. 编译控制开关¶
SOUI 的 CMake 构建系统提供了 SOUI_ENABLE_SVG 编译开关,让开发者可以根据项目需求灵活控制:
option(SOUI_ENABLE_SVG "Enable SVG support" ON)
ON:启用 SVG 支持(默认)OFF:禁用 SVG 支持,减小最终可执行文件体积
SVG vs PNG:技术对比¶
| 特性 | PNG 方案 | SVG 方案 |
|---|---|---|
| 分辨率依赖 | 高 DPI 设备需准备多套资源,增加体积 | 一次设计,处处适用,无需多套资源 |
| 内存占用 | 每个分辨率独立位图数据,占用大 | 只需加载一次矢量数据,占用小 |
| 文件体积 | 多状态多尺寸时体积迅速膨胀 | 简单图形比 PNG 小数倍至数十倍 |
| 缩放效果 | 非整数倍缩放模糊或有锯齿 | 无损缩放,任意分辨率保持清晰 |
| 动态生成 | 难以通过程序动态生成或修改 | 文本格式,易于程序或 AI 生成修改 |
| 缩放性能 | 非 1:1 绘制需插值,性能损失大 | 无论如何缩放性能稳定 |
| 渲染效率 | 九宫格需像素处理,开销大 | 矢量特性九宫格场景表现出色 |
| 可编辑性 | 二进制格式,编辑困难 | 文本格式,易于编辑和修改 |
SVG 资源的组织方式¶
参考 xmusic 项目的实际结构,建议按以下方式组织 SVG 资源:
uires/
├── uires.idx # 资源索引文件
├── svg/ # SVG资源目录
│ ├── btn_play.svg
│ ├── btn_pause.svg
│ ├── btn_close.svg
│ ├── search_box_bg.svg
│ ├── progress_track.svg
│ ├── volume_filled.svg
│ └── ...
├── values/
│ └── skin.xml # 皮肤定义文件
└── ...
在 XML 中使用 SVG¶
1. imglist 与 SVG:多状态图标皮肤¶
imglist 是使用 SVG 最常见的方式,适用于按钮、图标等需要多种状态的场景。
来自 xmusic 项目的实际例子(skin.xml):
<skin>
<!-- 播放按钮 - 4种状态: normal, hover, pressed, disabled -->
<imglist name="skin_play_btn" src="svg:btn_play" states="4"/>
<!-- 暂停按钮 - 4种状态 -->
<imglist name="skin_pause_btn" src="svg:btn_pause" states="4"/>
<!-- 停止按钮 - 4种状态 -->
<imglist name="skin_stop_btn" src="svg:btn_stop" states="4"/>
<!-- 上一曲/下一曲按钮 -->
<imglist name="skin_prev_btn" src="svg:btn_prev" states="4"/>
<imglist name="skin_next_btn" src="svg:btn_next" states="4"/>
<!-- 窗口控制按钮 -->
<imglist name="skin_btn_minimize" src="svg:btn_minimize" states="4"/>
<imglist name="skin_btn_maximize" src="svg:btn_maximize" states="4"/>
<imglist name="skin_btn_restore" src="svg:btn_restore" states="4"/>
<imglist name="skin_btn_close" src="svg:btn_close" states="4"/>
<!-- 功能按钮 -->
<imglist name="skin_btn_playlist" src="svg:btn_playlist" states="4"/>
<imglist name="skin_btn_eq" src="svg:btn_eq" states="4"/>
<!-- 进度条相关 -->
<imglist name="skin_progress_track" src="svg:progress_track"/>
<imglist name="skin_progress_filled" src="svg:progress_filled"/>
<imglist name="skin_progress_thumb" src="svg:progress_thumb" states="4"/>
<!-- 收藏图标 - 2种状态: 未收藏, 已收藏 -->
<imglist name="skin_favorite" src="svg:icon_favorite" states="2"/>
<!-- 音量图标 - 3种状态: 静音, 小音量, 大音量 -->
<imglist name="skin_volume_icon" src="svg:icon_volume" states="3"/>
<!-- 批量转换动画 - 10帧动画 -->
<imglist name="skin_icon_converting_anim" src="svg:icon_converting" states="10"/>
</skin>
对应的 SVG 文件结构(btn_play.svg):
<?xml version="1.0" encoding="UTF-8"?>
<!-- 播放按钮 - 4种状态水平排列:每 50px 为一个状态 -->
<svg width="200" height="50" viewBox="0 0 200 50"
fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- State 1: Normal (0-50px) - 红色圆形背景 + 白色播放图标 -->
<g transform="translate(0, 0)">
<circle cx="25" cy="25" r="25" fill="#e60026"/>
<path d="M20 17v16l12-8z" fill="#ffffff"/>
</g>
<!-- State 2: Hover (50-100px) - 亮红色圆形背景 -->
<g transform="translate(50, 0)">
<circle cx="25" cy="25" r="25" fill="#ff1744"/>
<path d="M20 17v16l12-8z" fill="#ffffff"/>
</g>
<!-- State 3: Pushed (100-150px) - 深红色圆形背景 -->
<g transform="translate(100, 0)">
<circle cx="25" cy="25" r="25" fill="#cc0022"/>
<path d="M20 17v16l12-8z" fill="#ffffff"/>
</g>
<!-- State 4: Disabled (150-200px) - 灰色圆形背景 -->
<g transform="translate(150, 0)">
<circle cx="25" cy="25" r="25" fill="#666666"/>
<path d="M20 17v16l12-8z" fill="#999999"/>
</g>
</svg>
2. imgframe 与 SVG:九宫格拉伸皮肤¶
imgframe 用于需要九宫格拉伸的场景,如按钮背景、输入框边框、菜单边框等。
来自 xmusic 项目的实际例子:
<skin>
<!-- 搜索框背景 - 左右各10px保持不变,中间拉伸 -->
<imgframe name="skin_search_box_bg"
src="svg:search_box_bg"
margin="10,0,10,0"/>
<!-- 音量滑块轨道 - 九宫格拉伸 -->
<imgframe name="skin_volume_track"
src="svg:volume_track"
margin="5,0,5,0"/>
<imgframe name="skin_volume_filled"
src="svg:volume_filled"
margin="5,0,5,0"/>
<!-- 普通按钮 - 4种状态,九宫格拉伸 -->
<imgframe name="skin_btn_normal"
src="svg:btn_normal"
margin="8,5,8,5"
states="4"/>
<!-- 主按钮(强调色) - 4种状态 -->
<imgframe name="skin_btn_primary"
src="svg:btn_primary"
margin="8,5,8,5"
states="4"/>
<!-- 播放列表项背景 - 4种状态 -->
<imgframe name="skin_playlist_item_bg"
src="svg:playlist_item_bg"
margin="10,0,10,0"
states="4"/>
<!-- 输入框边框 - 2种状态: normal, focus -->
<imgframe name="skin_edit_border"
src="svg:border_edit"
margin="3,3,3,3"
states="2"/>
<!-- 托盘菜单背景 - 圆角边框 -->
<imgframe name="skin_bg_tray_menu"
src="svg:bg_tray_menu"
margin="12,12,12,12"/>
<!-- 批量转换进度条 - 九宫格拉伸 -->
<imgframe name="skin_progress_batch_track"
src="svg:progress_batch_track"
margin="10,0,10,0"/>
<imgframe name="skin_progress_batch_filled"
src="svg:progress_batch_filled"
margin="10,0,10,0"/>
<!-- 歌词API列表项背景 - 4种状态 -->
<imgframe name="skin_lyrics_api_item_bg"
src="svg:bg_lyrics_api_item"
margin="8,8,8,8"
states="4"/>
</skin>
对应的 SVG 文件结构(search_box_bg.svg):
<?xml version="1.0" encoding="UTF-8"?>
<!-- 搜索框背景 - 圆角矩形,左右圆角通过margin保持不变 -->
<svg xmlns="http://www.w3.org/2000/svg"
width="100" height="36" viewBox="0 0 100 36">
<rect x="0" y="0" width="100" height="36"
fill="#2a2a2a"
stroke="#3a3a3a"
stroke-width="1"
rx="18" ry="18"/>
</svg>
3. vscrollbar 与 SVG:滚动条皮肤¶
滚动条也可以使用 SVG 实现:
<skin>
<!-- 自定义列表滚动条 - 4种状态,margin控制九宫格 -->
<vscrollbar name="skin_vscrollbar_lv"
src="svg:scrollbar_lv"
states="4"
margin="6"/>
</skin>
4. 在布局中使用 SVG 皮肤¶
定义好皮肤后,在布局 XML 中通过 skin 属性引用:
<!-- 使用 SVG 按钮皮肤 -->
<button skin="skin_play_btn" width="50" height="50" name="btn_play"/>
<button skin="skin_pause_btn" width="50" height="50" name="btn_pause"/>
<!-- 使用 SVG 输入框边框 -->
<edit skin="skin_edit_border"
background="#00000000"
width="200" height="32"/>
<!-- 使用 SVG 搜索框背景 -->
<window skin="skin_search_box_bg" width="300" height="36">
<edit background="#00000000" padding="10,0,36,0"/>
<button skin="skin_btn_search"
pos="-36,0,-0,-0" width="36" height="36"/>
</window>
<!-- 使用 SVG 进度条 -->
<progress pos="10,0,-10,30"
skinTrack="skin_progress_track"
skinSeek="skin_progress_filled"
skinThumb="skin_progress_thumb"/>
<!-- 使用 SVG 音量滑块 -->
<slider pos="10,0,-10,30"
skinChannel="skin_volume_track"
skinThumb="skin_volume_thumb"/>
在 C++ 代码中使用 SVG¶
1. 从皮肤对象获取 SVG 并绘制¶
void CMyControl::OnPaint(IRenderTarget *pRT)
{
// 获取皮肤对象
SSkinObjBase *pSkin = GetSkin();
if (!pSkin) return;
// 获取SVG对象
ISvgObj* pSvg = pSkin->GetSvg();
if (pSvg)
{
// 绘制整个SVG到目标区域
CRect rcDest = GetClientRect();
pRT->DrawSVG(pSvg, &rcDest, NULL, 255);
// 绘制SVG的部分区域(源裁剪)
CRect rcSrc(10, 10, 50, 50); // 取SVG的(10,10)到(50,50)区域
CRect rcDest2(0, 0, 40, 40); // 绘制到40x40的区域
pRT->DrawSVG(pSvg, &rcDest2, &rcSrc, 200); // 带透明度
}
}
2. 直接从资源加载 SVG¶
#include <svg/SvgObject.h>
void DrawSvgFromResource(IRenderTarget* pRT, const CRect& rc)
{
// 方式1:通过资源前缀加载
ISvgObj* pSvg1 = CreateSvgFromResId(_T("SVG:btn_play"));
// 方式2:通过文件路径加载
ISvgObj* pSvg2 = CreateSvgFromFile(_T("svg/btn_play.svg"));
if (pSvg1)
{
pRT->DrawSVG(pSvg1, &rc);
pSvg1->Release();
}
}
3. 自定义控件中使用 SVG 作为状态绘制¶
class SMyButton : public SButton
{
protected:
void OnPaint(IRenderTarget *pRT)
{
SSkinObjBase *pSkin = GetSkin();
if (pSkin && pSkin->GetSvg())
{
// 根据当前状态选择SVG的区域
int nState = GetState(); // 0=normal, 1=hover, 2=pressed, 3=disabled
SIZE szSvg = pSkin->GetSkinSize();
int nStates = ((SSkinImgList*)pSkin)->GetStates();
int cxItem = szSvg.cx / nStates;
CRect rcSrc(nState * cxItem, 0, (nState + 1) * cxItem, szSvg.cy);
CRect rcDest = GetClientRect();
pRT->DrawSVG(pSkin->GetSvg(), &rcDest, &rcSrc);
}
else
{
__super::OnPaint(pRT);
}
}
};
SVG 设计规范与最佳实践¶
1. 多状态 SVG 设计规范¶
设计包含多状态的 SVG 时,应遵循以下规范:
| 状态数量 | 排列方式 | 典型应用 |
|---|---|---|
| 2 状态 | 水平 2 列 | 收藏/取消收藏、开关等 |
| 3 状态 | 水平 3 列 | 音量图标(静音/低/高)等 |
| 4 状态 | 水平 4 列 | 最常用:按钮的 normal/hover/pressed/disabled |
| 6-10 状态 | 水平 N 列 | 复杂动画、帧动画等 |
示例:4 状态按钮 SVG 布局
Width = 4 × 单状态宽度
┌─────────┬─────────┬─────────┬─────────┐
│ Normal │ Hover │ Pressed │Disabled │
│ 0~W/4 │ W/4~W/2 │ W/2~3W/4│ 3W/4~W │
└─────────┴─────────┴─────────┴─────────┘
2. 九宫格 SVG 设计规范¶
对于需要拉伸的 SVG(按钮背景、边框等),需要合理设计 margin:
margin="左,上,右,下" 或 margin="统一值"
推荐设计:
| 元素类型 | 推荐 margin | 说明 |
|---|---|---|
| 圆角按钮 | 8,5,8,5 | 左右 8px 保持圆角,上下 5px |
| 输入框 | 3,3,3,3 | 四边各 3px 保持边框 |
| 搜索框(胶囊形) | 18,0,18,0 | 左右保持半圆形状 |
| 菜单边框 | 8,8,8,8 | 四角 8px 圆角 |
| 进度条轨道 | 10,0,10,0 | 左右保持圆角,中间拉伸 |
3. 颜色设计建议¶
- 使用语义化颜色命名,便于主题切换
- 状态颜色保持一致的亮度差(如 hover 比 normal 亮 10-15%)
- disabled 状态统一使用灰色系
- 主色调、强调色保持一致性
xmusic 项目的颜色方案示例:
<!-- values/color.xml -->
<color>
<item name="color_primary">#e60026</item> <!-- 主色:红色 -->
<item name="color_primary_light">#ff1744</item> <!-- 主色亮色(hover) -->
<item name="color_primary_dark">#cc0022</item> <!-- 主色暗色(pressed) -->
<item name="color_disabled">#666666</item> <!-- 禁用色 -->
<item name="color_bg_dark">#2a2a2a</item> <!-- 暗色背景 -->
<item name="color_border">#3a3a3a</item> <!-- 边框色 -->
</color>
4. SVG 文件优化建议¶
- 简化路径:使用简化工具优化 SVG 路径数据,减小文件体积
- 移除冗余属性:移除编辑器生成的无用属性(如
inkscape:、sodipodi:前缀) - 统一 viewBox:保持合理的 viewBox 尺寸,避免过大数据
- 避免嵌套过深:减少不必要的
<g>嵌套层级 - 使用十六进制颜色:优先使用
#RRGGBB格式,兼容性更好
5. 常见 SVG 模板¶
圆角按钮背景(4 状态):
<?xml version="1.0" encoding="UTF-8"?>
<svg width="400" height="40" viewBox="0 0 400 40" xmlns="http://www.w3.org/2000/svg">
<!-- Normal: 100x40 -->
<g transform="translate(0,0)">
<rect x="1" y="1" width="98" height="38" rx="5" ry="5"
fill="#e60026" stroke="#cc0022" stroke-width="1"/>
</g>
<!-- Hover: 100x40 -->
<g transform="translate(100,0)">
<rect x="1" y="1" width="98" height="38" rx="5" ry="5"
fill="#ff1744" stroke="#e60026" stroke-width="1"/>
</g>
<!-- Pressed: 100x40 -->
<g transform="translate(200,0)">
<rect x="1" y="1" width="98" height="38" rx="5" ry="5"
fill="#cc0022" stroke="#aa0011" stroke-width="1"/>
</g>
<!-- Disabled: 100x40 -->
<g transform="translate(300,0)">
<rect x="1" y="1" width="98" height="38" rx="5" ry="5"
fill="#666666" stroke="#555555" stroke-width="1"/>
</g>
</svg>
图标按钮(4 状态,带透明背景):
<?xml version="1.0" encoding="UTF-8"?>
<svg width="160" height="40" viewBox="0 0 160 40" xmlns="http://www.w3.org/2000/svg">
<!-- Normal: 深色图标 -->
<g transform="translate(0,0)">
<path d="M20 10 L30 20 L20 30 M10 20 L28 20"
fill="none" stroke="#333333" stroke-width="2" stroke-linecap="round"/>
</g>
<!-- Hover: 主色图标 -->
<g transform="translate(40,0)">
<path d="M60 10 L70 20 L60 30 M50 20 L68 20"
fill="none" stroke="#e60026" stroke-width="2" stroke-linecap="round"/>
</g>
<!-- Pressed: 深色主色 -->
<g transform="translate(80,0)">
<path d="M100 10 L110 20 L100 30 M90 20 L108 20"
fill="none" stroke="#cc0022" stroke-width="2" stroke-linecap="round"/>
</g>
<!-- Disabled: 灰色 -->
<g transform="translate(120,0)">
<path d="M140 10 L150 20 L140 30 M130 20 L148 20"
fill="none" stroke="#999999" stroke-width="2" stroke-linecap="round"/>
</g>
</svg>
AI 辅助 SVG 生成¶
SVG 的文本格式特性使其成为 AI 辅助 UI 开发的理想选择。
典型工作流¶
步骤 1:准备设计规范
请生成一套音乐播放器的 SVG 图标资源,要求:
- 颜色方案:主色 #e60026,暗色背景 #2a2a2a,文字 #ffffff
- 每个按钮 4 种状态水平排列(normal/hover/pressed/disabled)
- 单状态尺寸 40x40px,4 状态总宽度 160px
- 包含图标:播放、暂停、停止、上一曲、下一曲、播放列表、设置、最小化、最大化、关闭
步骤 2:AI 生成 SVG 代码
AI 会根据要求直接输出可使用的 SVG 文件内容。
步骤 3:集成到 SOUI
将生成的 SVG 文件放入 uires/svg/ 目录,在 skin.xml 中引用:
<imglist name="skin_play_btn" src="svg:btn_play" states="4"/>
AI 生成 SVG 的优势¶
- 格式友好:基于 XML 的文本格式,AI 可以直接生成和修改
- 批量处理:根据设计规范批量生成风格一致的 UI 元素
- 快速迭代:几秒钟内生成多种设计方案,加速开发流程
- 个性化定制:根据用户需求生成独特的 UI 元素
常见问题¶
Q1:SVG 支持哪些元素和属性?¶
SOUI 支持的 SVG 元素包括:
- 基本形状:<rect>、<circle>、<ellipse>、<line>、<polyline>、<polygon>
- 路径:<path>(支持 M/L/C/Q/Z/A 等命令)
- 填充描边:fill、stroke、stroke-width、fill-opacity、stroke-opacity
- 渐变:<linearGradient>、<radialGradient>
- 变换:transform(translate、scale、rotate、matrix)
- 文本:<text>(SOUI 扩展支持)
- 分组:<g>、<defs>
Q2:使用 SVG 后性能会下降吗?¶
不会明显下降。SOUI 的 SVG 采用纯矢量渲染路径,相比光栅化方案: - 避免了中间位图的内存开销 - 九宫格场景下性能优于位图(无需像素插值) - 缩放时性能稳定,不会因为放大导致性能下降
Q3:SVG 可以和 PNG 混用吗?¶
可以。皮肤系统会根据资源类型自动选择加载方式,可以在同一个项目中混用 SVG 和 PNG:
<skin>
<!-- 简单图标使用 SVG -->
<imglist name="skin_icon_play" src="svg:icon_play" states="4"/>
<!-- 复杂照片类资源使用 PNG -->
<imglist name="skin_cover_placeholder" src="png:cover_placeholder"/>
</skin>
Q4:如何调试 SVG 显示问题?¶
- 浏览器预览:用 Chrome/Edge 直接打开 SVG 文件,检查图形是否正确
- 检查 viewBox:确保 viewBox 与实际图形范围匹配
- 检查单位:避免使用
px、em等单位,使用纯数字 - 简化测试:先用简单图形测试,逐步增加复杂度
- 查看日志:开启 SOUI 调试日志,检查资源加载错误
Q5:SOUI 支持动画 SVG 吗?¶
目前 SOUI 不直接支持 SMIL 动画,但可以通过以下方式实现动画效果:
- 多帧动画:使用
imglist的多状态机制,每帧为一个状态,通过定时器切换状态 - 代码动画:通过代码控制绘制位置、缩放、透明度等属性
- 属性动画:使用 SOUI 的动画系统控制控件属性
示例:xmusic 项目中的转换动画
<!-- 10 帧转换动画,states=10 -->
<imglist name="skin_icon_converting_anim" src="svg:icon_converting" states="10"/>
// 代码中通过定时器切换帧
void CBatchConvertDlg::OnTimer(UINT_PTR nIDEvent)
{
static int s_nFrame = 0;
m_pIconConverting->SetState(s_nFrame % 10);
s_nFrame++;
}
总结¶
SOUI 的 SVG 支持方案在保持轻量、高性能的前提下,实现了 SVG 与框架渲染系统的深度整合:
- 轻量实现:基于 NanoSVG 解析,代码紧凑,集成成本低
- 深度融合:直接利用 IRenderTarget 矢量渲染能力,避免中间位图开销
- 完整特性:支持文本、渐变、九宫格等常用 SVG 特性
- 开发友好:通过
svg:资源前缀自动识别,与现有皮肤系统无缝协作 - AI 就绪:文本格式为智能化 UI 设计与生成提供基础
相比将 SVG 光栅化的传统方式,SOUI 的矢量渲染路径在内存、性能、视觉效果和开发效率上均具有显著优势,是现代桌面应用 UI 开发的高效选择。