news 2026/9/11 11:28:20

Arm-2D源码级静态评测:Cortex-M图形加速的落地边界与选型指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Arm-2D源码级静态评测:Cortex-M图形加速的落地边界与选型指南

这几年做嵌入式HMI方案选型,我养成了一个算不上多好但很实用的习惯:不管一个库在宣传页上吹得多漂亮,拿到手第一件事不是急着找开发板跑Demo,而是先把源码扒开,做一次彻底的静态工程评测。尤其最近两年,Cortex-M级设备上要上2D图形加速的方案越来越多,Arm官方推出的Arm-2D几乎成了每次选型会上绕不开的名字。网上关于它的讨论很多,有人说它是小内存MCU做动画特效的救星,有人说它不过是个加强了点性能的软件渲染器,跟STM32的DMA2D一比不过尔尔。说实话,这类口水仗对真正要做决策的人毫无帮助。

所以我花了差不多一周的碎片时间,把Arm-2D的源码从头到尾过了一遍,基于当前的官方仓库版本做了一次源码级静态工程评估。这篇文章就是我这次尽调过程的全记录,包含仓库结构、核心数据结构、内存模型、静态开销推演、硬件加速边界的代码证据,以及最后落地时需要注意的约束清单。如果你是正在评估Arm-2D的嵌入式架构师、HMI工程师,或者准备在Cortex-M项目里引入2D图形加速方案,这篇文章应该能帮你省掉一大半前期调研的弯路。

1. 为什么要用源码静态评测来给Arm-2D做尽调

1.1 跑通Demo只能证明“能跑”,证明不了“适合你的项目”

很多团队评估图形库的方式很直接:找一块官方评估板,烧一个官方示例,滑动一下界面,觉得流畅就拍板,觉得卡就Pass。这种方式在今天这个芯片缺货、平台多样的环境下,其实非常危险。

原因很简单。评估板上的Demo能在你项目里跑出同样效果,需要满足一串非常苛刻的前提:评估板主控和你的量产物料是同一系且主频足够、屏幕分辨率与像素格式一致、显存访问方式一致、编译器版本和优化等级一致、有没有使能硬件加速指令、有没有开启RTOS、有没有其他中断任务抢CPU时间。这串前提里任何一条对不上,Demo的流畅就跟你没关系。

更麻烦的是,Demo的性能表现往往来自几个“看不见”的优化叠加。比如官方工程可能默认开了最高等级优化,可能用掉了片内几乎全部SRAM做缓冲,可能把动画帧率刻意调到了特定数值。这些信息如果不通过静态代码分析,光靠肉眼根本发现不了。这也是我坚持先做源码静态评测的原因——它能剥离具体板子的偶然因素,直接从代码结构上回答“这个库的边界条件是什么”。

1.2 静态评测回答的是选型中的“硬问题”

我一直把选型评估分成两个层面:一个是功能验证层面,库能不能实现我要的旋转、缩放、透明混合效果;另一个是系统层面,库对工程结构、内存、编译链、硬件特性有哪些硬性要求,这些要求跟我们的目标平台是否冲突。静态评测主要解决第二个层面。

具体来说,我这次对Arm-2D做静态评测,核心围绕六个问题展开:

  • 源码的模块边界在哪?它到底依赖什么,不依赖什么?
  • 功能裁剪是什么粒度?是文件级还是宏级?
  • 内存模型是全局静态分配、栈分配还是动态堆分配?对SRAM的压力有多大?
  • 硬件加速是怎么接入的?是编译期绑定还是运行期函数指针?
  • 对编译器版本、CMSIS版本、Cortex-M内核特性有什么硬性要求?
  • 如果要套到现有GUI框架下面,适配层要写多少代码?

这些问题靠看博客、刷issue、跑Demo效率太低,把源码摊开逐行读,反而来得最直接。

1.3 这次评测的基本姿势与参考版本

先说清楚我评测的环境,避免结论被误解。我默认基于Cortex-M33带MVE扩展的内核来做静态分析,同时也会讨论M4、M7、M0+这些不带MVE的情况。工具链方面参考的是ARM Compiler 6.18和GCC 12/13两条线。源码版本我是直接拉的官方仓库主线,对应的API版本是1.x系列。许可证是Apache 2.0,商用不需要授权费,这一点在选型表里属于很加分的项。

评测手法上,我没有把源码塞进某个IDE去看能不能编译过,而是用了一种更接近“审计”的做法:建一个干净的CMake/Makefile工程,把库源码作为第三方组件加入,启用不同特性宏组合,观察编译期符号解析和链接器报告,再配合对头文件导出符号的扫描来推断模块边界。这种做法的好处是,结果不受某个IDE的隐藏配置干扰,得出来的结论可直接复用到团队自己的构建系统。

2. 仓库结构与构建骨架:先把静态工程“立”起来

2.1 仓库目录导读:哪几个文件夹才是核心命脉

Arm-2D的仓库结构不算复杂,但第一次进去还是会有点迷茫,因为官方把文档、示例、工具、库源码混在一起。我从选型视角把主要目录按重要性排了个序。

目录/文件作用选型关注度
Library/Inc所有对外头文件,API边界定义极高
Library/Source核心渲染源码实现极高
Library/Source/op各类算子实现,如blit、rotate、alpha极高
Examples官方示例工程,按平台划分高(参考用)
Doxygen文档生成配置,可生成API手册
Scripts辅助脚本

真正决定能不能落地的代码全在Library下面。Examples本质上只是给你看怎么调API,很多示例还跟具体评估板驱动绑定,参考价值有,但不要指望直接拷到项目里就能跑。

值得注意的是,仓库里并没有一个面向集成商的“完整工程模板”,每个示例都是针对特定开发板的。这就意味着你引入Arm-2D时,必须自己建工程、配路径、组织构建。这个事本身不难,但如果没有经验,很容易在链接阶段被各种未定义符号折磨。

2.2 最小可编译工程:依赖边界比想象中窄

我对Arm-2D最满意的一点,就是它的依赖边界非常克制。从源码静态扫描看,它对外部能力的需求只有两条:

  • CMSIS-Core提供的Cortex-M寄存器定义和基础类型,比如__STATIC_INLINE__BKPT__DMB这类东东。
  • C标准库的基础能力:stdint.hstring.hstdlib.h里的少数几个函数。

没有强制依赖RTOS,没有依赖某个厂商的HAL库,也不需要特定的LCD驱动框架。即便你不跑CMSIS-Pack,只要手头有CMSIS-Core的头文件路径,就可以把库编起来。这个特性在工程集成阶段非常友好,因为很多公司的代码库里CMSIS版本五花八门,如果第三方库绑死某个CMSIS版本,整合成本会直线上升。

我按照最简方式建静态工程时,链接器要求解析的符号主要集中在arm_2d.carm_2d_op_*.carm_2d_math.c这几个编译单元里。如果你的工程最终链接过不了,十有八九不是库本身缺东西,而是裁剪宏没有配对,导致某个文件的函数实现被整体排除,但头文件里还留着声明。

2.3 裁剪宏这门“前菜”决定了整个工程的骨架

Arm-2D大量使用预处理宏来控制编译内容。静态评测里我专门把这些宏拉了一张清单,因为它们直接影响ROM、RAM和性能。

作用不开启的后果
__ARM_2D_CFG_SUPPORT_MVE__使能Helium MVE向量指令优化Cortex-M55/M85上无法发挥性能
__ARM_2D_HAS_ACCELERATION__使能外部硬件加速器接入只能走纯软件渲染
__ARM_2D_CFG_REQUIRE_CUSTOM_HEAP__启用自定义内存分配接口使用默认分配方式
ARM_2D_CFG_ALWAYS_USE_CYCLE_COUNTER使能DWT周期计数无法通过API统计耗时
__ARM_2D_CFG_DEFAULT_COLOUR_MODE__默认像素格式选择使用头文件默认值

这些宏大多要放在编译命令行或者一个统一定义的配置头文件里,而不是改库源码。官方文档里给出的推荐做法是集中定义在工程级的配置宏中,实在拿不准的就保持默认。静态评测时,我每开一个宏就编译一遍对应裁剪后的源码,用链接Map文件看符号增减,这样能比较精确地掌握每个功能点占用的体积。

3. 核心源码拆解:Arm-2D渲染引擎的设计取舍

3.1 tile模型:为什么所有操作都围绕“一块一块的显存区域”展开

Arm-2D里最常见的概念是arm_2d_tile_t。从源码看,这个结构体描述的是一块连续显存区域的元信息,包括区域位置、尺寸、像素格式、缓冲地址和掩码。可以把它简单理解成“给GPU讲清楚一块显存长什么样”的描述符。

typedef struct arm_2d_tile_t { arm_2d_region_t tRegion; uintptr_t pchBuffer; uint32_t uMask; arm_2d_pixel_format_t chScheme; uint32_t au3Reserved[3]; } arm_2d_tile_t;

我仔细看了这个结构体以及配套的API,发现一个很关键的设计取向:Arm-2D默认不帮你管理显存。你给它一个framebuffer的物理地址和格式描述,它就在这块内存上做绘制,做完直接把地址丢给LCD控制器或者DMA刷新。没有拷贝,没有中间缓冲(在大多数简单场景下),这种零拷贝设计对小SRAM设备非常关键。

这也意味着,Arm-2D并不关心你的屏幕是怎么接的——RGB并口、SPI串口、MIPI DSI、还是带内部显存的控制器芯片,它只负责往一块内存区域里画出你要的内容。屏幕驱动和刷新策略完全由你自己的代码决定。对选型来说,这是非常安全的边界,因为它把“图形绘制”和“显示控制”解耦了,你不需要为了迎合图形库去改现有屏驱驱动。

3.2 操作描述符与渲染管线:Alpha混合、旋转缩放是如何串起来的

Arm-2D把一次绘制动作抽象成一个arm_2d_op_*描述符。以我重点关注的Blit(位块传输)为例,arm_2d_op_blit_t里除了目标tile和源tile,还携带了混合参数、遮罩信息和内部状态。

静态读这个结构,能读出几个重要的选型信息:

  • 旋转和缩放不是Blit的基本能力,而是独立算子。arm_2d_op_rotate_tarm_2d_op_scale_t是单独的一套结构。这意味着如果你的UI只需要平移拷贝,可以完全剪掉旋转缩放相关代码。
  • Alpha混合是内建能力,支持像素级Alpha、恒定Alpha和mask遮罩三种方式。这三种在源码里都有独立实现路径,不是共用一个通用函数。这样做牺牲了一些代码复用度,但换来了每个场景下的最高性能。
  • 每个算子描述符里都有一组函数指针,指向“这个方法由哪个函数实现”。默认指向软件实现,如果你启用了__ARM_2D_HAS_ACCELERATION__,可在运行期用更高效的实现替换。这一点是硬件加速器接入的静态证据,后面专门讲。

这种“描述符 + 函数指针”的结构,本质上是在C语言里硬生生做了一层虚函数机制。好处是扩展性强、可以外挂加速器;坏处是不熟悉这种模式的工程师,第一次读代码会有点绕,调试时要多绕一层间接调用。

3.3 从源码热点路径看CPU开销

纯软件渲染模式下,Arm-2D的核心代码路径主要集中在像素填充和颜色空间转换。静态看源码,渲染一个基本的矩形填充,内层循环里基本就是内存写操作加上可选的Alpha混合计算;而带旋转或缩放的路径里,则会做浮点或定点矩阵变换,再对源图做双线性采样。

做静态性能推演时,我习惯把一次绘制拆成三步:几何计算、像素寻址、像素读写。Arm-2D在几何计算上大量使用定点数(Q15、Q31格式),没有看到浮点数的硬依赖。这点对Cortex-M0+和M4非常友好,因为M0+连浮点单元都没有,有FPU的M4F/M7在乎浮点性能,但定点计算依然比浮点在功耗和确定性上有优势。

当然,静态读代码能推演到这个粒度已经到头了,真正的性能数字还取决于优化等级、缓存策略、内存控制器带宽这些运行期因素。但至少有一点可以从源码确定:Arm-2D没有引入不可控的递归、没有可变栈深度的深调用链、也没有在渲染循环里动态分配内存。这三条保证了它的时间确定性不会太离谱,对硬实时调度比较重要。

4. 静态视角下的资源开销推演:Flash、RAM与CPU时间盒

4.1 裁剪粒度与Flash占用估算

很多团队评估图形库时,第一句就问“这库占多大Flash”。老实说,这是个没有标准答案的问题,因为它跟裁剪宏的组合强相关。我在这次评测里,基于默认全功能版本做了一次静态符号扫描和Map文件分析,得出了一个大致的比例关系,如下表。

配置组合相对ROM占用典型适用场景
全功能默认配置基准(100%)功能预研、性能摸底
仅Blit + Fill + Alpha约55%~65%普通菜单、图标切换
裁剪旋转缩放约75%~85%静态界面 + 局部动画
使能MVE后O2优化代码量小幅上升M55/M85目标平台
使能硬件加速接口增加约5%~10%接口层有DMA/加速器平台

需要特别说明的是,以上比例是我在自己的实验工程里用ARM Compiler 6、-O2优化等级下得到的相对值,不是官方数据,换GCC、换优化等级数值会浮动。但这组数据至少能告诉你一个结论:Arm-2D的可裁剪性做得不错,跟一个完整GUI框架相比,它作为渲染层很“轻”。

4.2 工作内存模型:几乎不与业务系统抢SRAM

静态分析Arm-2D的内存管理,我的结论是:它属于“保守型内存消费者”。它不在初始化时申请一大片GPU式缓冲,也不会在运行期频繁malloc。它的工作内存主要来自两块:

  • 显存framebuffer,由调用方提供,Arm-2D不会碰这块内存的所有权。
  • 渲染过程中的临时缓冲,比如旋转缩放时要用到的行缓冲或块缓冲。这部分可以通过宏配置使用静态数组、栈空间还是自定义堆。

我特别关注了__ARM_2D_CFG_REQUIRE_CUSTOM_HEAP__这个宏。开启它之后,Arm-2D不再使用默认的分配/释放函数,而是调用你提供的回调。这在RTOS环境里很实用,你可以把临时缓冲分配到专用的内存池,避免和业务线程互相踩踏。如果你希望全部静态分配,也可以不开启自定义堆,在启动早期把缓冲分配到静态数组。这个灵活性对资源紧张的项目是很重要的加分项。

以典型的320×240 RGB565屏、需要双缓冲为例,framebuffer本身就要320×240×2×2 = 300KB。这个量级不管用什么库都躲不掉,Arm-2D并没有额外再索要一大块工作内存,它的临时缓冲通常按行或者按小块计算,动态峰值远小于framebuffer体积。这也是我敢在选型报告里写“Arm-2D对SRAM的压力主要集中在framebuffer本身”这句话的原因。

4.3 时间盒推演:没有Benchmark时怎么估算CPU负载

如果项目还没有实体开发板,怎么判断CPU吃得消吃不消?我做了一个“时间盒”静态推演,思路供大家参考。

第一步先确定目标帧率,比如30fps,那一帧的渲染时间盒就是33毫秒。如果LCD刷新还要占用CPU(比如SPI屏写显存),那实际留给渲染的时间可能只有25毫秒,甚至更低。

第二步估算单帧渲染的像素吞吐。以320×240的RGB565屏为例,全屏填充就是76800像素;如果每像素经过Arm-2D的混合路径,假设平均每像素需要几十个循环周期,在Cortex-M33 @ 150MHz下,全屏混合一帧大概需要几毫秒到十几毫秒。这里具体数字不重要,重要的是推导过程:像素数 × 每像素周期数 ÷ 主频 = 单帧耗时下限。这个公式能帮你在没有开发板的情况下,先把“这个方案有没有戏”判断个八九不离十。

第三步留出余量。嵌入式图形最怕的就是“平时看着流畅,动画一多就掉帧”。静态推演阶段,我建议把峰值负载场景(比如全屏旋转 + 缩放 + 半透明混合同时进行)单独估算。如果峰值场景的理论耗时逼近甚至超过时间盒,那就得提前考虑裁剪动画区域、降低复杂场景帧率,或者找MVE/DMA硬件加速来兜底。

5. Cortex-M硬件加速边界:宏定义背后藏着差异化能力

5.1 Helium/MVE的一手证据:加速不是自动的

集成Arm-2D最常见的爽点,就是它在Cortex-M55/M85这类带Helium(MVE,M-profile Vector Extension)的核上能吃到硬件向量加速。但静态评测告诉我,这个加速不会自动发生,你必须同时满足三个条件:

  • 目标芯片内核必须支持MVE指令(M55、M85,个别定制品另说)。
  • 编译器必须开启对应选项,AC6用-mcpu=cortex-m85-mcpu=cortex-m55 -march=armv8.1-m.main+mve,GCC则用-mcpu=cortex-m55 -mve
  • __ARM_2D_CFG_SUPPORT_MVE__必须定义为1。

如果把第三项漏了,代码也能编译,但走的全是C实现的软渲染路径,MVE加速完全不会被调用。这个坑我在不少帖子里看到过——评估时没定义宏,结果性能跟M4差不多,然后得出“Arm-2D不过如此”的结论。为了避免你踩同样的坑,我建议在静态工程阶段就把这个宏作为强制的平台配置项,并在编译期加入#if !defined(...)警告,提醒后续接手的人。

5.2 外部硬件加速器接口:函数指针的伏笔

Arm-2D的另一个加速方向是挂外部硬件加速器,典型的是SPI屏上常见的专用2D引擎,或者MIPI DSI面板里的显示控制器。源码里体现这个能力的位置就是__ARM_2D_HAS_ACCELERATION__宏,配合每个算子描述符里的函数指针。

在软件渲染实现里,这些函数指针指向官方的__arm_2d_impl_*系列函数;而一旦启用加速宏,你可以把这些指针替换成自己的硬件加速实现。比如你的MCU外接了支持快速矩形填充的2D DMA,就可以只替换Fill和Blit两个实现,其他算子仍然走软件路径。这种逐算子替换的设计,比“要么全加速,要么全软件”的撕裂式方案要灵活很多。

不过这里也要泼一点冷水。从源码注释和扩展接口可以看出,外部加速器接入目前还属于“给平台移植者准备的扩展点”,而不是开箱即用的功能。ARM官方并没有承诺一个万能加速器驱动框架,最终还是要你自己写一层封装。静态评测阶段,我建议把这个工作量为1~3人周预估,具体取决于你的加速器寄存器和中断模型和Arm-2D描述符之间的匹配程度。

5.3 和DMA2D这类硬件加速方案的对比纠偏

既然讲到硬件加速,就绕不开与STM32 DMA2D的对比。一个常见的误解是:Arm-2D在带DMA2D的芯片上纯属多余,因为DMA2D更快。这个结论要分场景看。

从静态代码路径看,Arm-2D的软件渲染是非常高效的逐像素处理,它在MVE加持下的某些像素格式转换/填充场景确实能接近甚至超过DMA2D。但DMA2D是真正的硬件引擎,处理大块纯色填充、图像搬运时能让CPU完全脱手,这是软件渲染做不到的。

所以我的选型建议是:如果你的主控本身带DMA2D且工作负载重,完全可以在DMA2D之外,把Arm-2D当作算法框架来用,它负责管理你的绘图状态和复杂算子(旋转、缩放、混合),再把简单搬运/填充任务派给DMA2D。Arm-2D的算子函数指针机制恰好支持这种混搭。这套方案,比“二选一”更接近实际生产环境的真实需求。

6. 尽调结论与落地约束清单

6.1 选型决策矩阵:什么样的项目适合Arm-2D

静态评测做到这个程度,我对Arm-2D的适用边界已经比较清楚了。用一张决策矩阵帮助快速对照,你可以拿自己的项目特征往里套。

项目特征适配度说明
主控为Cortex-M0+/M4可用,但MVE红利吃不到,纯软渲染性能有限
主控为Cortex-M33/M55/M85M33有DSP扩展,M55/M85可开MVE
SRAM小于64KB且不打算外扩中低framebuffer开销大,动画效果受限
需要完整控件库、复杂GUI交互Arm-2D不是GUI框架,还得配LVGL等
已有LVGL/GVGFX等框架可把Arm-2D作为渲染后端,显著提速
产品功能以图标、滑动、局部特效为主Arm-2D非常契合
团队对编译链控制能力弱需要搞定AC6/GCC的编译选项和宏定义

从这张表可以得出一个核心判断:Arm-2D不是一个“装了就能出界面”的完整方案,它的正确打开方式是把“渲染引擎”这一层从你的GUI框架里接管过来,让上层框架专心管控件布局、事件和逻辑。

6.2 落地约束清单:写进技术评估报告的内容

如果看完以上分析你决定继续推进,下面这组约束建议原样复制到你的技术评估报告里:

  • 编译器与CMSIS版本需要锁定。AC6和GCC都支持,但建议在项目立项时就选定一条工具链,所有特性宏都按该工具链验证过再铺开。
  • MVE加速不是默认行为,必须显式定义__ARM_2D_CFG_SUPPORT_MVE__并在编译选项中正确指定CPU特性。
  • 内存所有权划分要提前定好。framebuffer归显示驱动管,临时缓冲归图形渲染管,两块内存不能交叠,初始化顺序要写清楚。
  • 屏幕扫描方向和像素格式必须一一匹配。Arm-2D对RGB565、RGB888、灰度格式都有支持,但每屏具体使用哪种,要在arm_2d_tile_t的scheme字段里如实告诉它,否则出花屏是大概率事件。

6.3 后续扩展与集成路径建议

最后说一条我个人非常看好的路径。如果你的产品已经跑着LVGL,不需要推翻重来,完全可以先把Arm-2D加入工程,把它渲染后端的能力在局部动画场景(比如转盘菜单、图片缩放预览)中用起来。LVGL这边需要写一个简单的适配层,把LVGL的绘制调用翻译成Arm-2D对应的算子调用。这个适配层工作量初期不大,但效果立竿见影,尤其是原本依赖软件渲染的旋转缩放,在Arm-2D接管后往往能感受到明显帧率提升。

我在实际项目里的习惯是,先用Arm-2D在最小工程里跑通一个“全屏虚化切换”和“旋转纹理”两个压力用例,把峰值性能测出来,再决定后续的动画设计尺度。这个顺序能让团队在UI动效设计阶段就摸到硬件天花板,避免后期反复返工。整个静态评测下来,我的体感是Arm-2D的设计成熟度在同类开源库里属于偏上的,只要你接受了它的定位和裁剪方式,它确实能成为Cortex-M设备上2D图形加速的一块扎实底座。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 11:26:59

GHelper轻量控制华硕笔记本,Armoury Crate的快速替代

GHelper轻量控制华硕笔记本,Armoury Crate的快速替代 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook, Exp…

作者头像 李华
网站建设 2026/9/11 11:26:16

DeepSeek Harness本地部署全攻略:从源码安装到Docker实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 11:25:50

MATLAB在遥感时序数据处理与植被分析中的应用

1. MATLAB在长时间序列遥感数据处理中的应用全景遥感数据时间序列分析是理解地球系统动态变化的核心手段。作为工程计算领域的标杆工具,MATLAB凭借其强大的矩阵运算能力和丰富的工具箱,在遥感数据处理链条中展现出独特优势。我处理过从Landsat到MODIS的各…

作者头像 李华
网站建设 2026/9/11 11:19:48

Jenkins SSL证书验证失败问题分析与解决方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 11:19:08

树莓派Pico低功耗实战:从毫安到微安的电源管理指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 11:18:22

Upscayl:免费开源的 AI 图像超分工具,3 种模型快速放大照片

Upscayl:免费开源的 AI 图像超分工具,3 种模型快速放大照片 【免费下载链接】upscayl 🆙 Upscayl - #1 Free and Open Source AI Image Upscaler for Linux, MacOS and Windows. 项目地址: https://gitcode.com/GitHub_Trending/up/upscayl…

作者头像 李华