ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

RGF_CJ 核心包 `rgf.rgf_core` 全面指南:初始化、主循环、窗口事件与底层工具函数

RGF_CJ 核心包 `rgf.rgf_core` 全面指南:初始化、主循环、窗口事件与底层工具函数
  • 图形学
  • 桌面应用

【免费下载链接】RGF_CJ

RGF是仓颉Windows系统下的通用渲染框架(兼应用开发框架)。RGF仓颉版(后续简称"RGF")基于RGF(C/C++版)封装优化而来。RGF为开发者提供轻量化、安全、高性能以及高度一致性的2D渲染能力以及常用的桌面应用开发工具,并且提供对接Direct3D的相关接口,以满足开发者对3D画面渲染的需求。在Windows开发方面,RGF在子包内也提供开箱即用的Windows API,确保熟悉Win32的开发者快速上手。此外,RGF还提供一些常用功能的封装(如XAudio2、MF),以辅助开发者实现音频、摄像头等扩展功能的管理。

项目地址:https://gitcode.com/Cangjie-SIG/RGF_CJ
点击查看免费下载

rgf.rgf_core是 RGF 仓颉版(RGF_CJ)的核心包,承载了绘制库全局初始化、Win32 主消息循环、窗口类体系、渲染事件模型以及一批面向 Win32 互操作的底层工具函数。本文以 doc/api/rgf.rgf_core/readme.md 为骨架,结合仓库源码,系统讲解该包的 API 全貌、调用约定与源码级实现原理,帮助读者掌握用仓颉语言构建 Windows 窗口应用与 2D 渲染程序的完整入口链路。

一、包定位与 API 全景

rgf.rgf_core是 RGF_CJ 项目中规模最大、职责最核心的包:它既是窗口框架(提供WinPure/WinCore窗口基类与全套窗口事件参数类),也是渲染框架(提供Surface、Bitmap、Brush、Path、TextLayout等绘制对象),同时还承载着全局生命周期(rsInitialize/rsDestroy/rsMain)和 Win32 互操作(ru*/rw*系列函数)。

从文档的 API 列表看,该包公开成员可归为八大类:

类别数量代表成员
函数22 个(含重载)rsMain、rsInitialize、rsCreateSysTimer、ruStrToWStr、rwBatchMove
类90+WinPure、WinCore、Surface、Color、Ev*事件参数类
接口12IDraw、IEvent、IRenderEvent、IObjLink、IWinMethod等
结构体2Context、ColorPct
枚举40+Substrate、RunMode、Antialias、WinStyle等
类型别名3RgfHwnd、RgfHdc、RgfTimer
变量与常量9rgfErrorWinUncreated、rgfMessageNote等错误/告警文案
异常1WinException

从源码结构看(src/rgf_core),该包对应的实现文件按职责拆分得十分清晰:rgf_api.cj(C 侧函数声明与包装)、rgf_base.cj(窗口基类与主入口封装)、rgf_utils.cj(工具函数)、rgf_enum.cj(枚举)、rgf_struct.cj(结构体与几何类)、rgf_type.cj(类型别名)等,读者可据此按图索骥深入阅读。

二、全局生命周期:初始化、释放与主入口

RGF 绘制库的使用遵循“初始化 → 创建资源 → 消息循环 → 销毁”的生命周期,所有入口都集中在rgf.rgf_core。

2.1rsInitialize()与rsDestroy(token)

public func rsInitialize(): ContextPtr public func rsDestroy(token: ContextPtr): Bool
  • rsInitialize()完成 RGF 绘制库的全局初始化,返回一个全局上下文(ContextPtr令牌,源码中为CPointer<Unit>类型别名,见 src/rgf_core/rgf_type.cj)。
  • rsDestroy(token)执行全局释放,传入rsInitialize返回的令牌,返回是否成功。

源码中两者的实现是直接的 C 侧调用包装:rsInitialize调用rApiInitialize(),rsDestroy调用rApiDestroy(token)(见 src/rgf_core/rgf_api.cj)。需要特别注意的是,文档明确提示“本函数创建的上下文环境需要使用rsDestroy()方法释放”——即rsInitialize属于手动管理模式,开发者自己rsInitialize后必须自行rsDestroy,不能依赖 GC。

2.2rsMain:封装 Win32 主流程的入口函数

public func rsMain(create:()->Unit, destroy:()->Unit, mode!:RunMode = ...): Unit public func rsMain(create:()->Unit, mode!:RunMode): Unit public func rsMain(create:()->Unit): Unit public func rsMain(create:()->Unit, destroy:()->Unit): Unit

rsMain是应用的主入口,内部封装了 Win32 的标准流程:获取模块句柄 → 初始化绘制库 → 创建Context→ 调用create创建窗口资源 → 进入GetMessageW/TranslateMessage/DispatchMessageW消息循环 → 主窗口关闭后调用destroy释放资源 → 释放上下文。

源码 src/rgf_core/rgf_base.cj 中rsMainInner的实现揭示了几个关键细节:

  • 主窗口判定:消息循环以while(haveMainWin.load())为条件,只要存在主窗口就持续分发消息;WM_QUIT(bRet == 0)或主窗口销毁后循环退出。
  • 无效消息过滤:循环内对hwnd == 0或!wIsWindow(hwnd)的消息直接continue,避免分发到已销毁窗口。
  • 中文说明:文档注明“本函数内部封装了 Win 的主要流程,若需要更高的自由度可参考此代码结构自行实现”,即开发者完全可以参照rsMainInner的代码自行编写更精细的消息循环。

2.3RunMode与调试/发布差异

RunMode枚举定义了主函数执行模式(源码 src/rgf_core/rgf_base.cj):

枚举值含义
SysThreadAndAsyn新建系统线程,异步不阻塞当前线程
SysThreadAndWait新建系统线程,并等待执行结束(默认)
CJMainThread直接基于仓颉主线程运行

rsMain有多个重载,且带@When[debug]/@When[!debug]编译期条件注解,调试与发布构建下的默认行为不同:

  • 发布构建(@When[!debug]):rsMain(create)/rsMain(create, destroy)默认走RunMode.SysThreadAndWait,即新建 UI 线程并阻塞主线程等待结束。
  • 调试构建(@When[debug]):同一签名的重载默认走RunMode.CJMainThread,直接在仓颉主线程上执行窗口逻辑,便于调试。

显式传入mode的重载(rsMain(create, destroy, mode)与rsMain(create, mode))则无编译期差异,完全由开发者指定运行模式。若使用SysThreadAndAsyn,配合ruFinished()判断 RGF 是否执行完成(返回false表示正在执行,true表示完成),用于在其他线程阻塞等待。

三、窗口类体系与窗口事件模型

3.1 窗口类注册:rwRegister/rwUnregister

public func rwRegister(ctxt: WinContext, className: String, style: UInt32, hbrBackground!: Int32 = 5): Bool public func rwUnregister(ctxt: WinContext): Bool

rwRegister是 RGF 封装的窗口类注册方法,hbrBackground默认值为5(即NULL_BRUSH,背景画刷为空)。源码 src/rgf_core/rgf_api.cj 中,注册成功后会将className记录进静态映射staticWinClassMap,并回填ctxt.winClassCTxtPtr与ctxt._className;rwUnregister注销成功后同步从映射移除。

文档特别强调一个关键注意事项:非本类注册的窗口类需要手动注入窗口过程函数,否则 RGF 窗口类无法捕获系统事件。也就是说,若开发者自行用原生 API 注册窗口类,必须自行把消息路由到 RGF 的窗口过程,事件系统才能工作。此外“凡注册的窗口类都应该由本方法注销”,源码中还提供了rwClearAllWinClass()在rsMain销毁阶段统一清理所有记录在案、未被释放的窗口类。

3.2 窗口句柄与类型别名

public type RgfHwnd = UIntNative // 窗口句柄 public type RgfHdc = UIntNative // 设备上下文 public type RgfTimer = HANDLE // Timer 句柄

三个公开类型别名中,RgfHwnd/RgfHdc是数值句柄,RgfTimer直接映射到 Win32 的HANDLE(见 src/rgf_core/rgf_type.cj)。配套函数ruToHwnd(rgfHwnd)与ruToHdc(rgfHdc)负责把 RGF 句柄还原为 Win32 的HWND/HDC。

3.3 窗口基类与渲染事件接口

类体系的核心是WinPure(RGF 窗口核心,纯窗口,见 doc/api/rgf.rgf_core/类/WinPure.md)与WinCore(继承WinPure,具备渲染能力,见 doc/api/rgf.rgf_core/类/WinCore.md)。二者都实现渲染事件接口IRenderEvent/IRenderEventCore与绑定式事件接口IBindRenderEvent/IBindRenderEventCore。

从源码 src/rgf_core/rgf_base.cj 可以看出窗口生命周期的完整顺序:

  1. beforeCreate():调用createWin后最先触发,此时渲染表面尚未创建;
  2. created():窗口创建完成,携带hwnd, x, y, nWidth, nHeight;
  3. rendererCreated(surface):渲染表面就绪(WinCore在此默认设置Antialias.HighQuality抗锯齿);
  4. createDeviceResources():创建设备相关/渲染资源,此事件可能在窗口周期内被多次调用;
  5. onPaint(wRect):窗口需要重绘时触发,参数为需要重绘的矩形;
  6. destroyDeviceResources():销毁设备资源;
  7. rendererDestroy():渲染表面释放前触发;
  8. beforeDestroy():窗口销毁前;
  9. destroyed():窗口生命周期终结。

除了覆写式事件方法,窗口类还提供一套绑定式(lambda)事件:bindBeforeCreate、bindCreated、bindRendererCreated、bindCreateDeviceResources、bindOnPaint、bindDestroyDeviceResources、bindRendererDestroy、bindBeforeDestroy、bindDestroyed、bindWndProc。从GFBase.callWndProc的实现看,绑定式事件优先于类自身方法,满足“扩展修订类”的需求——即开发者不必继承窗口类,直接给实例挂 lambda 即可自定义行为。

此外,rgf.rgf_core下还有 90+ 个Ev*事件参数类(如EvMouse、EvKey、EvSize、EvTimer、EvDPIChanged等,见 doc/api/rgf.rgf_core/类 目录),它们把 Win32 消息转换为类型安全的仓颉对象,是绑定式事件与wndProc处理时的参数载体。

3.4 渲染相关辅助函数

  • rwBatchMove(batch):批量移动窗口,有三种重载,分别接收(RgfHwnd,...)、(WinPure,...)、(IWinMethod,...)元组数组,元组格式为[句柄/窗口, X, Y, 宽, 高]。源码实现使用BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos三件套,保证一批窗口位置更新期间的重绘同步与性能。
  • rwIsMainWin(rw/hWnd/IWinMethod):判断窗口是否为主窗口(haveMainWin原子标志与mainWinHwnd全局句柄配合实现)。
  • rwGetRgfClass(rgfHwnd):通过句柄反向获取窗口类实例(从staticMap永久映射中查找)。
  • rwDefWindowProc(hWnd, uMsg, wParam, lParam):DefWindowProc的包装,用于未处理消息的默认处理。

四、绘制对象与资源管理

rgf.rgf_core同时暴露了完整的 2D 渲染对象体系,按“设备相关/设备无关”和“手动释放/自释放”两个维度组织:

几何与颜色值类型:Point/FPoint(位置)、Size/FSize(尺寸)、Rect/FRect(矩形,实现IRect<T>接口,见 doc/api/rgf.rgf_core/类/Rect.md)、Color(色彩)、Matrix/Matrix3X3。

绘制对象:Surface(窗口渲染表面)、BindSurface(绑定式渲染表面)、Bitmap/MemBitmap(位图/内存位图)、Brush及其子类SolidColorBrush、LinearGradientBrush、BitmapBrush、Path(路径)、StrokeStyle(笔划风格)、Layer(图层)、Image(图像父类)。

文本体系:TextCore、TextFormat、TextLayout、TextInlineObj、TextMetrics、LineMetrics、ClusterMetrics、OverhangMetrics、HitTestMetrics及Range*局部格式类。

资源生命周期:所有资源类都实现IObjLink接口(isValid()有效性验证、release()释放、get/set底层对象访问),基类SfcSubObjBase通过SfcSubObjType枚举区分对象类型并统一走rApiSfcSubObjRelease释放(见 src/rgf_core/rgf_base.cj)。带S前缀的类(SBitmap、SPath、STextLayout等)为自释放版本——它们被渲染表面接管,释放表面时自动释放,无需手动release()。

五、系统工具函数:定时器、字符串与内存互操作

5.1 系统定时器

public func rsCreateSysTimer(callback: CFunc<(CPointer<Unit>, UInt8) -> Unit>, parameter: CPointer<Unit>, dueTime: UInt32, period: UInt32): RgfTimer public func rsDestroySysTimer(timer: RgfTimer): Unit

rsCreateSysTimer基于 Win32 定时器队列创建定时器:callback为@C风格回调(可传入@CLambda),parameter为回调参数指针,dueTime为首次执行等待时间(毫秒),period为每次间隔时间(毫秒),成功返回RgfTimer句柄。源码 src/rgf_core/rgf_utils.cj 中,其底层调用wCreateTimerQueueTimer,并附加WT_EXECUTEDEFAULT | WT_EXECUTELONGFUNCTION标志以支持长任务执行;创建失败抛WinException。

务必注意文档提示:“凡是本函数创建的定时器,都需要销毁”——用完调用rsDestroySysTimer(timer)注销(底层wDeleteTimerQueueTimer,失败抛WinException),否则会造成句柄与回调资源泄漏。

5.2 字符串与宽字符互操作

RGF 在仓颉String与 Win32LPWSTR(C 宽字符串)之间提供了一组对称转换工具:

函数作用
ruStrToWStr(str: String): LPWSTR仓颉字符串 → C 宽字符串(含编码转换);创建的字符串需用ruFree释放
ruWStrToStr(str: LPWSTR): StringC 宽字符串 → 仓颉字符串(含编码转换,内部自动释放中间结果)
ruWStrlen(wcharPtr: LPWSTR): UInt32获取 C 宽字符串长度(宽字符数)
ruFree(cPtr: CPointer<Unit>): Unit释放ruStrToWStr申请的宽字符串内存
ruStrToCStr<T>(str, callback)字符串转换 + 回调内使用 +自动释放,一种更安全的便捷封装

ruStrToCStr的实现(源码 src/rgf_core/rgf_utils.cj)为:先ruStrToWStr转换,执行用户回调,最后rApiReleaseWcharStr释放——使用者在回调内使用cStr即可,无需关心释放。推荐优先使用ruStrToCStr而非裸用ruStrToWStr+ruFree,以规避忘记释放的风险。

5.3 指针与内存工具

  • ruUllToPtr(ptrVal: UIntNative): CPointer<Unit>:将无符号数值(如句柄地址值)恢复为指针。
  • ruLlToPtr(ptrVal: IntNative): CPointer<Unit>:将有符号数值恢复为指针(文档标注“极少数情况下使用”)。
  • ruRedefBit<T1,T2>(v): T2:从内存比特层面重解释数据值,类似 C++ 的联合体(union)。实现上取两个类型大小的最大值开临时缓冲区,writeLittleEndian写入后readLittleEndian读回(源码 src/rgf_core/rgf_utils.cj),约束为T1/T2 <: LittleEndianOrder<T> & CType。
  • ruSafeCStruct<T,RT>(count, callback):自动创建count个T的 CType 内存、执行回调、自动释放,回调收到CPointer<T>,适合向 C 侧传入临时结构体数组。
  • rsModRefToCurrDir():将当前程序的存储路径设为相对路径起点(底层SetCurrentDirectoryToExe),解决资源文件相对路径定位问题。
  • rsGetMonitorFromPoint(x, y / Point / FPoint): Rect:获取桌面坐标系下某点所在显示器的工作区矩形(rcWork)。源码通过EnumDisplayMonitors回调 +PtInRect命中测试 +GetMonitorInfoW获取工作区,未命中时抛WinException。三个重载分别接受(Int32, Int32)、Point、FPoint。
  • rsGetContext(): Option<Context>:获取全局上下文,未初始化时返回None。
  • rsEnd(exitCode: UInt32):结束进程(底层ExitProcess),用于主动终止应用。
  • ruFinished(): Bool:判断 RGF 是否执行完成,配合SysThreadAndAsyn模式下的跨线程等待。

六、结构体、常量与异常约定

6.1 结构体

  • Context(技术上下文):封装了ctxtPtr(ContextPtr,RGF 全局上下文指针)与hInst(HINSTANCE,程序模块实例句柄),由rsMain内部创建并通过rsGetContext()获取。hInst在rwRegister注册窗口类时作为参数传给 C 侧rApiRegisterWinClass(见 src/rgf_core/rgf_struct.cj 与 src/rgf_core/rgf_api.cj)。
  • ColorPct(颜色及百分比值):用于渐变色标/百分比配对等场景,配合LinearGradientBrush使用。

6.2 错误与告警常量

包内定义了一组rgf*常量(见 doc/api/rgf.rgf_core/变量与常量.md),用于错误抛出与调试提示,例如:

常量含义
rgfErrorWinUncreated“访问未创建的窗口类!”
rgfErrorMainUiThreadinited“RGF 的主 UI 线程已被初始化!”(重复调用rsMain会抛出)
rgfErrorCpluapluaObjNotExist“底层对象未初始化或已经释放!”
rgfErrorWindowsApi“Windows Api 出现错误!错误码:”
rgfErrorArrLenOverflow“数组长度超出最大限额!”
rgfWarnValidRdObj“异步调用渲染时,请对类实例进行有效性验证!”
rgfMessageNote/rgfWarningNote调试消息/告警消息提示(不影响正式程序执行)
rgfFunctionWillIgnored/rgfWrongAttribution“当前控件并未实现此绑定式事件!传入的事件函数将被忽略!”
rgfNegativeReinter“当前正在进行负值相关的数值重解释!”

6.3 异常类

WinException(见 doc/api/rgf.rgf_core/异常/WinException.md)是系统相关异常,携带 Win32GetLastError错误码,所有涉及 Windows API 调用的失败路径(定时器创建失败、监视器查询失败、线程创建失败、消息循环错误等)均抛出该异常。

七、典型用法:一个完整的 RGF 窗口应用骨架

综合以上 API,一个典型 RGF 窗口应用的最小骨架如下(结构参考 test/CT/base/src/main.cj 等仓库内测试工程):

package main import rgf.rgf_core.* // 自定义渲染窗口(继承 WinCore 获得渲染能力) class MyWin <: WinCore { public override func createDeviceResources(): Bool { // 创建设备相关资源(画刷、位图等) return true } public override func onPaint(wRect: Rect): Unit { // 在此执行绘制,例如: // surface.clear(1.0, 1.0, 1.0, 1.0) } public override func destroyDeviceResources(): Bool { // 销毁设备相关资源 return true } } // 窗口类上下文(全局) var winCtxt: WinContext = WinContext() main() { // 注册窗口类(hbrBackground 默认 NULL_BRUSH) rwRegister(winCtxt, "MyWindowClass", WinStyle.e2n(WinStyle.Default)) // RGF 主入口:create 中创建窗口资源,destroy 中释放 rsMain({ let win = MyWin() win.createWin( winCtxt, "RGF 示例窗口", 100, 100, 800, 600, // 位置与尺寸 0, // 父窗口句柄(0 为桌面) WinStyle.e2n(WinStyle.Default), substrate: Substrate.Direct2D, // 推荐 Direct2D 渲染后端 bgMod: BackGroundMod.CoverNotDraw, ) win.show(true) }, { // destroy:释放窗口资源 rwUnregister(winCtxt) }) }

关键点回顾:

  • 渲染后端通过Substrate枚举选择:Direct2D(推荐)、GdiPlus、Pure(纯窗口无渲染能力)。
  • rsMain在发布构建默认SysThreadAndWait、调试构建默认CJMainThread,两种模式都能直接运行上述代码。
  • 如需更高自由度(如自定义消息循环、消息过滤、加速键翻译),文档建议参考rsMainInner的源码结构自行实现。

八、深入阅读指引

  • 包 API 全表:doc/api/rgf.rgf_core/readme.md;函数细节:doc/api/rgf.rgf_core/函数.md
  • 窗口基类与事件:WinPure/WinCore文档见 doc/api/rgf.rgf_core/类,实现见 src/rgf_core/rgf_base.cj
  • 主入口与消息循环实现:src/rgf_core/rgf_base.cj(rsMainInner、RunMode)
  • 工具函数(定时器/字符串/指针/监视器):src/rgf_core/rgf_utils.cj
  • C 侧函数声明与窗口类注册:src/rgf_core/rgf_api.cj
  • 类型别名定义:src/rgf_core/rgf_type.cj
  • 可运行的测试工程:test/CT/base/src/main.cj、test/HLT/base/src/main.cj、test/HLT/pure/src/main.cj
  • 图形学
  • 桌面应用

【免费下载链接】RGF_CJ

RGF是仓颉Windows系统下的通用渲染框架(兼应用开发框架)。RGF仓颉版(后续简称"RGF")基于RGF(C/C++版)封装优化而来。RGF为开发者提供轻量化、安全、高性能以及高度一致性的2D渲染能力以及常用的桌面应用开发工具,并且提供对接Direct3D的相关接口,以满足开发者对3D画面渲染的需求。在Windows开发方面,RGF在子包内也提供开箱即用的Windows API,确保熟悉Win32的开发者快速上手。此外,RGF还提供一些常用功能的封装(如XAudio2、MF),以辅助开发者实现音频、摄像头等扩展功能的管理。

项目地址:https://gitcode.com/Cangjie-SIG/RGF_CJ
点击查看免费下载

相关推荐

上一篇:OSSN插件开发入门:如何为社交平台添加自定义功能?
下一篇:BAAH开发者贡献指南:如何为开源项目提交代码的完整教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进