- 图形学
- 桌面应用
【免费下载链接】RGF_CJ
RGF是仓颉Windows系统下的通用渲染框架(兼应用开发框架)。RGF仓颉版(后续简称"RGF")基于RGF(C/C++版)封装优化而来。RGF为开发者提供轻量化、安全、高性能以及高度一致性的2D渲染能力以及常用的桌面应用开发工具,并且提供对接Direct3D的相关接口,以满足开发者对3D画面渲染的需求。在Windows开发方面,RGF在子包内也提供开箱即用的Windows API,确保熟悉Win32的开发者快速上手。此外,RGF还提供一些常用功能的封装(如XAudio2、MF),以辅助开发者实现音频、摄像头等扩展功能的管理。
在 Windows 桌面应用中,"文本 + 图片"混合排版是常见诉求,而 RGF 仓颉版(RGF_CJ)通过文本内联对象(TextInlineObj)提供了开箱即用的解决方案:它允许开发者把位图(Bitmap)或图层(Layer)作为行内元素嵌入文本布局(TextLayout),随文本一起参与排版与渲染。本指南将围绕 STextInlineObj 类文档 展开,讲解其与父类TextInlineObj的继承关系、自释放机制、创建与嵌入文本的完整流程,并结合仓库源码揭示其底层实现原理。读完本文,你将能够在自己的 RGF 窗口中轻松实现图片替换文本字符的图文混排效果,并正确管理相关资源的生命周期。
一、什么是文本内联对象
文本内联对象(TextInlineObj)是 RGF 文本体系中的一种"设备相关资源",它把一张图片包装成一个可以被文本布局引擎识别和排版的"字符级"元素。当文本中某个位置被内联对象替换后,该位置的原始字符不再渲染文字,而是渲染为内联对象对应的图片,从而实现"文中带图"的混合排版。
在 RGF 的 API 文档中,其定位清晰:
TextInlineObj:文本内联对象,设备相关资源,见 类 TextInlineObj;STextInlineObj:设备相关资源 -自释放- 文本内联对象,见 类 STextInlineObj。
从源码看,两者的类声明为(rgf_text.cj 与 rgf_auto_class.cj):
// 基础类:文本内联对象(设备相关资源) public open class TextInlineObj <: SfcSubObjBase{ @When[debug]/* 创建者 */ internal var creatorHash:Int64 = 0; public init(){ // 创建空文本内联对象实例 super(CPointer<Unit>(),SfcSubObjType.TextInlineObj) } internal init(oPtr:CPointer<Unit>){ super(oPtr,SfcSubObjType.TextInlineObj) } } // 自释放子类:设备相关资源 - 自释放 - 文本内联对象 public class STextInlineObj <: TextInlineObj { ~init(){ if(objPtr.isNotNull()){ // 释放对象 unsafe { rApiSfcSubObjRelease(objPtr,SfcSubObjType.e2Num(objType)) } objPtr = CPointer<Unit>() // 自动回收 HashCode _hashCodeMgr[1].add(_hashCodeVal) } } }二者的核心差异在于资源释放方式:TextInlineObj需要开发者显式调用release();而STextInlineObj通过仓颉的析构器~init()在对象被回收时自动调用底层释放接口rApiSfcSubObjRelease,并对objPtr置空、回收 HashCode,属于"自释放"的便捷形态。RGF 为每种设备相关资源都提供了对应的 S 前缀自释放版本(如SBitmap、STextFormat、STextLayout等),STextInlineObj即其中一员。
二、类层次与资源体系
2.1 继承链与基础能力
TextInlineObj的父类是SfcSubObjBase,其完整继承链为(rgf_base.cj):
SfcSubObjBase -> SfcSubObjBaseHash -> SfcSubObjBaseLink -> IObjLink & ToString其中IObjLink接口定义了每个资源对象的基础契约(rgf_base.cj):
| 方法 | 说明 |
|---|---|
isValid():Bool | 有效性验证,内部通过objPtr.isNotNull()判断底层对象指针是否有效 |
release():Bool | 对象释放,调用底层rApiSfcSubObjRelease后置空指针 |
get(vType:Int32):Option<Any> | 底层内部对象获取接口(vType=0返回指针,vType=1返回类型枚举) |
set(vAny:Any,vType:Int32):Bool | 底层内部对象设置接口 |
toString():String | 输出{"ptr":...,"type":"TextInlineObj"}形式的格式化描述 |
2.2 类型标识:SfcSubObjType
RGF 通过内部枚举SfcSubObjType区分各类子对象,TextInlineObj的枚举值为6(rgf_base.cj):
internal enum SfcSubObjType { | Brush | StrokeStyle | Bitmap | Path | Matrix | TextCore | TextInlineObj // 6u8 | TextFormat | TextLayout | MemBitmap | Layer ... }这个类型值会被传入rApiSfcSubObjRelease等底层接口,用于告诉底层库当前释放/识别的是哪一类设备资源。
2.3 自动释放宏支持
在 RGF 的元编程体系中,@LifeCycle等宏会通过isDeviceResources()判断成员类型是否为设备相关资源(macros/rgf_utils.cj),TextInlineObj被明确列入其中。这意味着当你在@LifeCycle标注的窗口类中声明TextInlineObj成员时,框架可以自动编排其创建与销毁生命周期,这也是后续实战示例得以简化的基础。
三、实战:在窗口中创建文本内联对象
3.1 前置依赖:文本渲染三件套
RGF 中渲染文本需要依赖TextCore(文本渲染核心)、TextFormat(文本格式)、TextLayout(文本布局)三个资源(见配套课程 013_渲染文本.md)。内联对象需要嵌入到TextLayout中参与布局,因此这三者同样缺一不可:
@LifeCycle class WinSelf <: WinCore{ let txtCore:TextCore // 文本渲染核心 let txtfmt:TextFormat // 文本格式 let txtlyt:TextLayout // 文本布局 let txtInline:TextInlineObj // 文本内联对象(本次主题) }3.2 在设备资源创建事件中初始化
根据配套课程 014_文本内联对象.md,在createDeviceResources()生命周期事件中完成依赖创建:
public override func createDeviceResources():Bool{ // 先创建文本渲染依赖 surface .createTextCore(txtCore) // 创建渲染核心 .createTextFormat(txtfmt,24.0/*字体大小*/) // 创建格式 .createTextLayout(txtlyt,"绘制的文本",txtfmt,Size(Int32.Max,60)/*布局区宽高*/) // 加载一张图片 let bmp:Bitmap = surface.createBitmap("./res/test.png") // 从图片创建一个内联对象(16x16 大小的行内元素,置于 (0,0)) surface.createTextInlineObject(txtInline,0.0,0.0,16.0,16.0,bmp); // 图片位图本身已不再需要,立即释放 bmp.release() return true; }注意:
createDeviceResources()在窗口周期内可能被多次调用(参见 IRenderEventCore 的接口注释),因此这里创建的资源应确保在destroyDeviceResources()中正确释放,或直接使用自释放形态的STextInlineObj交由析构器托管。
3.3 createTextInlineObject 全量重载
createTextInlineObject由渲染表面(Surface/SurfaceCore)提供,源码位于 rgf_renderer.cj,共提供 6 种重载:
| 重载签名 | 说明 |
|---|---|
createTextInlineObject(x:Float32,y:Float32,width:Float32,height:Float32,img:Image):TextInlineObj | 按位置与宽高创建,返回新对象 |
createTextInlineObject(rect:FRect, img:Image):TextInlineObj | 按FRect(浮点矩形)区域创建 |
createTextInlineObject(rect:Rect, img:Image):TextInlineObj | 按Rect(整型矩形)区域创建,内部转为Float32 |
createTextInlineObject(ref:TextInlineObj, x, y, width, height, img:Image):SurfaceCore | 复用传入对象,先把旧对象release()再重建(链式调用返回this) |
createTextInlineObject(ref:TextInlineObj, rect:FRect, img:Image):SurfaceCore | 复用式 + 浮点矩形 |
createTextInlineObject(ref:TextInlineObj, rect:Rect, img:Image):SurfaceCore | 复用式 + 整型矩形 |
其中Rect/FRect重载的内部实现如下(以Rect为例):
public func createTextInlineObject(rect:Rect, img:Image):TextInlineObj{ return createTextInlineObject( Float32(rect.left),Float32(rect.top),Float32(rect.right - rect.left),Float32(rect.bottom - rect.top), img ) }即宽高由矩形坐标直接推导:width = right - left,height = bottom - top。
3.4 底层分发:位图与图层的差异化路径
在 rgf_renderer.cj 中,createTextInlineObject会根据图片类型分流到不同的底层接口:
if(img.objType == SfcSubObjType.Bitmap){ return TextInlineObj(unsafe{ rApiDctClassCreateTextInlineObject(objPtr,x,y,width,height,img.objPtr) }) }else if(img.objType == SfcSubObjType.Layer){ return TextInlineObj(unsafe{ rApiDctClassCreateTextInlineObjectLay(objPtr,x,y,width,height,img.objPtr) }) } throw Exception(rgfErrorCpluapluaObjNotExist)对应的 FFI 外部函数声明在 rgf_api.cj:
foreign func rApiDctClassCreateTextInlineObject(dctClass:RgfDct,x:Float32,y:Float32,width:Float32,height:Float32,bmp:RgfImage):InlineObj foreign func rApiDctClassCreateTextInlineObjectLay(dctClass:RgfDct,x:Float32,y:Float32,width:Float32,height:Float32,bmp:RgfImage):InlineObj而InlineObj在 rgf_type.cj 中定义为type InlineObj = CPointer<Unit>,即底层原生对象指针。可以看出:
- Bitmap 位图走
rApiDctClassCreateTextInlineObject; - Layer 图层走
rApiDctClassCreateTextInlineObjectLay(带Lay后缀); - 传入既非位图也非图层的图片时,会抛出
rgfErrorCpluapluaObjNotExist异常; - 创建前会先校验表面与图片的
isValid(),任一无效同样抛异常。
因此,内联对象的"内容源"实际上可以是任意位图或图层,这为后续在图层上绘制后再嵌入文本提供了想象空间(例如先在一个图层中组合多张图片,再整体作为内联对象置入文本)。
四、将内联对象嵌入文本布局
创建好的内联对象只有通过TextLayout.setInlineObject()置入文本布局,才会真正参与排版与渲染。该方法的源码在 rgf_text.cj:
public func setInlineObject(object:TextInlineObj,startPosition:UInt32,length:UInt32):Bool{ if(!isValid() || !object.isValid()){ throw Exception(rgfErrorCpluapluaObjNotExist) } unsafe{ rApiTxtLayoutSetInlineObject(objPtr,object.objPtr,startPosition,length) } } public func setInlineObject(object:TextInlineObj,textRange:TextRange):Bool{ setInlineObject(object,textRange.startPosition,textRange.length) }4.1 参数语义
| 参数 | 类型 | 说明 |
|---|---|---|
object | TextInlineObj | 要嵌入的行内对象(需isValid(),否则抛异常) |
startPosition | UInt32 | 字符开始位置(从 0 起) |
length | UInt32 | 字符长度,即内联对象替换掉的字符数量 |
textRange | TextRange | 文本范围封装(startPosition+length,见 类 TextRange) |
对应的底层 FFI 声明在 rgf_text_dw.cj:
foreign func rApiTxtLayoutSetInlineObject(textLayoutClass:Layout,object:InlineObj,startPosition:UInt32,length:UInt32):Bool4.2 使用示例
沿用配套课程 014_文本内联对象.md 的写法:
// 将文本内联对象替换入文本 txtlyt.setInlineObject(txtInline, 0/*字符开始位置*/, 1/*字符长度*/);这段代码的含义是:用内联对象(图片)替换布局文本中第 0 个字符起、长度为 1 的原始字符。例如布局文本是"绘制的文本",则最终渲染时首字符位置显示的是图片而非文字。
4.3 读取内联对象:getInlineObject 与 RangeInlineObj
RGF 同样提供了反向查询能力。getInlineObject(currentPosition)(rgf_text.cj)可以查询某个字符位置是否挂有内联对象,返回Option<RangeInlineObj>:
public func getInlineObject(currentPosition:UInt32):Option<RangeInlineObj>{ ... let retv:RangeInlineObj = RangeInlineObj( obj:if(inlineObj.isNull()){ Option<TextInlineObj>.None } else { TextInlineObj(inlineObj) }, range:unsafe{textRange.read()}.toClass() ) ... }RangeInlineObj定义于 rgf_text_dw.cj,包含两个字段:
| 字段 | 类型 | 说明 |
|---|---|---|
obj | Option<TextInlineObj> | 命中的内联对象(无则为None) |
range | TextRange | 该内联对象覆盖的文本范围 |
注意:getInlineObject返回的是新包装的TextInlineObj实例(由底层指针构造),与最初创建时传入的对象并非同一实例,但其底层指针相同;实际使用时应以isValid()和底层指针为准。
五、内联对象的其他应用:文本修剪符号
除了替换正文文字,TextInlineObj还有一个重要用途:作为文本修剪(Trimming)的省略号符号。RGF 的TextFormat.setTrimming()允许指定一个内联对象作为超出布局宽度时的裁剪标记(rgf_text.cj):
public func setTrimming(trimmingOptions:Trimming,trimmingSign:Option<TextInlineObj>):Bool配套的TrimmingPck包装类同样持有一个Option<TextInlineObj>类型的trimmingSign字段(rgf_text_dw.cj):
public class TrimmingPck{ public let trimmingOptions:Trimming public let trimmingSign:Option<TextInlineObj> const public init( trimmingOptions!:Trimming = Trimming(), trimmingSign!:Option<TextInlineObj> = Option<TextInlineObj>.None ) { ... } }这意味着开发者完全可以自定义一个图片型省略号(例如"…"样式图标),替代默认的文本修剪符,实现更灵活、更品牌化的文本溢出表现。
六、资源管理与最佳实践
6.1 自释放 vs 手动释放
- 手动释放:
TextInlineObj创建后,应在合适的时机调用release()(如destroyDeviceResources()事件,或如示例中bmp.release()般用完即放)。release()会调用底层rApiSfcSubObjRelease并置空objPtr(rgf_base.cj),同时回收 HashCode。 - 自释放:
STextInlineObj通过~init()在 GC 回收时自动完成上述释放动作,适合"创建后不便手工管理"的场景。但请注意 GC 释放时机并不确定,若内联对象占用大量显存,仍建议显式release()以保证资源及时回收。
6.2 生命周期注意事项
- 校验优先:所有对
TextInlineObj的操作(创建、嵌入、查询)都会在底层对象未创建时抛出rgfErrorCpluapluaObjNotExist异常,因此应确保内联对象在createDeviceResources()中创建完成后再被setInlineObject()引用; - 多次调用安全:
createDeviceResources()可能被多次触发,使用ref:形式的重载会在重建前自动release()旧对象,避免资源泄漏; - 源图片及时释放:内联对象创建后会持有底层图片数据,示例中创建完内联对象立即
bmp.release()是安全的——因为底层内联对象已经独立持有图片内容; - 替换语义:
setInlineObject是"替换"而非"插入",指定的length个字符将不再渲染文本,规划布局字符串时需留出被替换的位置。
七、小结
文本内联对象是 RGF 文本渲染能力中实现"图文混排"的关键一环:
- 概念上,
TextInlineObj把位图/图层包装为字符级行内元素;STextInlineObj是其自释放变体,通过析构器自动释放底层资源; - 使用上,三步即可完成:在窗口类声明内联对象成员 → 在
createDeviceResources()中通过surface.createTextInlineObject()从图片创建 → 通过txtlyt.setInlineObject()嵌入指定文本范围; - 原理上,创建路径根据
Bitmap/Layer分流到rApiDctClassCreateTextInlineObject/...Lay两个底层 FFI,嵌入路径经由rApiTxtLayoutSetInlineObject交由原生文本布局引擎完成排版; - 扩展上,内联对象还可作为文本修剪的自定义省略号,配合
Trimming/TrimmingPck实现个性化溢出样式。
掌握TextInlineObj的使用,再结合 013_渲染文本.md 中的文本三件套流程与 014_文本内联对象.md 的完整示例,你就能在 RGF 中构建出丰富多彩的图文混排界面。相关 API 全量签名可进一步查阅 类 TextInlineObj、类 STextInlineObj 与 类 TextLayout。
- 图形学
- 桌面应用
【免费下载链接】RGF_CJ
RGF是仓颉Windows系统下的通用渲染框架(兼应用开发框架)。RGF仓颉版(后续简称"RGF")基于RGF(C/C++版)封装优化而来。RGF为开发者提供轻量化、安全、高性能以及高度一致性的2D渲染能力以及常用的桌面应用开发工具,并且提供对接Direct3D的相关接口,以满足开发者对3D画面渲染的需求。在Windows开发方面,RGF在子包内也提供开箱即用的Windows API,确保熟悉Win32的开发者快速上手。此外,RGF还提供一些常用功能的封装(如XAudio2、MF),以辅助开发者实现音频、摄像头等扩展功能的管理。
相关推荐
RGF 仓颉版文本内联对象(TextInlineObj)实战指南:实现文本与图片混合排版
RGF 仓颉版文本内联对象(TextInlineObj)实战指南:实现文本与图片混合排版 导读 在 RGF(仓颉版通用渲染框架)中,文本内联对象( TextIn
图形学桌面应用RGF_CJ 文本内联对象实战:在仓颉渲染框架中实现图文混合排版
RGF_CJ 文本内联对象实战:在仓颉渲染框架中实现图文混合排版 在桌面应用中,"图文混排"是文本排版的高频需求——例如聊天消息中的表情图片、富文本编辑器里的插
图形学桌面应用RGF 仓颉版文本渲染详解:RangeInlineObj 局部内联对象与文本内联查询
RGF 仓颉版文本渲染详解:RangeInlineObj 局部内联对象与文本内联查询 本文以 RGF 仓颉版 rgf.rgf_core 子包中的 RangeIn
图形学桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考