实战指南:在仓颉中实现图文混排与自释放资源管理)
图形学桌面应用【免费下载链接】RGF_CJRGF是仓颉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点击查看免费下载在 Windows 桌面应用中文本 图片混合排版是常见诉求而 RGF 仓颉版RGF_CJ通过文本内联对象TextInlineObj提供了开箱即用的解决方案它允许开发者把位图Bitmap或图层Layer作为行内元素嵌入文本布局TextLayout随文本一起参与排版与渲染。本指南将围绕 STextInlineObj 类文档 展开讲解其与父类TextInlineObj的继承关系、自释放机制、创建与嵌入文本的完整流程并结合仓库源码揭示其底层实现原理。读完本文你将能够在自己的 RGF 窗口中轻松实现图片替换文本字符的图文混排效果并正确管理相关资源的生命周期。一、什么是文本内联对象文本内联对象TextInlineObj是 RGF 文本体系中的一种设备相关资源它把一张图片包装成一个可以被文本布局引擎识别和排版的字符级元素。当文本中某个位置被内联对象替换后该位置的原始字符不再渲染文字而是渲染为内联对象对应的图片从而实现文中带图的混合排版。在 RGF 的 API 文档中其定位清晰TextInlineObj文本内联对象设备相关资源见 类 TextInlineObjSTextInlineObj设备相关资源 -自释放- 文本内联对象见 类 STextInlineObj。从源码看两者的类声明为rgf_text.cj 与 rgf_auto_class.cj// 基础类文本内联对象设备相关资源 public open class TextInlineObj : SfcSubObjBase{ When[debug]/* 创建者 */ internal var creatorHash:Int64 0; public init(){ // 创建空文本内联对象实例 super(CPointerUnit(),SfcSubObjType.TextInlineObj) } internal init(oPtr:CPointerUnit){ super(oPtr,SfcSubObjType.TextInlineObj) } } // 自释放子类设备相关资源 - 自释放 - 文本内联对象 public class STextInlineObj : TextInlineObj { ~init(){ if(objPtr.isNotNull()){ // 释放对象 unsafe { rApiSfcSubObjRelease(objPtr,SfcSubObjType.e2Num(objType)) } objPtr CPointerUnit() // 自动回收 HashCode _hashCodeMgr[1].add(_hashCodeVal) } } }二者的核心差异在于资源释放方式TextInlineObj需要开发者显式调用release()而STextInlineObj通过仓颉的析构器~init()在对象被回收时自动调用底层释放接口rApiSfcSubObjRelease并对objPtr置空、回收 HashCode属于自释放的便捷形态。RGF 为每种设备相关资源都提供了对应的 S 前缀自释放版本如SBitmap、STextFormat、STextLayout等STextInlineObj即其中一员。二、类层次与资源体系2.1 继承链与基础能力TextInlineObj的父类是SfcSubObjBase其完整继承链为rgf_base.cjSfcSubObjBase - SfcSubObjBaseHash - SfcSubObjBaseLink - IObjLink ToString其中IObjLink接口定义了每个资源对象的基础契约rgf_base.cj方法说明isValid():Bool有效性验证内部通过objPtr.isNotNull()判断底层对象指针是否有效release():Bool对象释放调用底层rApiSfcSubObjRelease后置空指针get(vType:Int32):OptionAny底层内部对象获取接口vType0返回指针vType1返回类型枚举set(vAny:Any,vType:Int32):Bool底层内部对象设置接口toString():String输出{ptr:...,type:TextInlineObj}形式的格式化描述2.2 类型标识SfcSubObjTypeRGF 通过内部枚举SfcSubObjType区分各类子对象TextInlineObj的枚举值为6rgf_base.cjinternal enum SfcSubObjType { | Brush | StrokeStyle | Bitmap | Path | Matrix | TextCore | TextInlineObj // 6u8 | TextFormat | TextLayout | MemBitmap | Layer ... }这个类型值会被传入rApiSfcSubObjRelease等底层接口用于告诉底层库当前释放/识别的是哪一类设备资源。2.3 自动释放宏支持在 RGF 的元编程体系中LifeCycle等宏会通过isDeviceResources()判断成员类型是否为设备相关资源macros/rgf_utils.cjTextInlineObj被明确列入其中。这意味着当你在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整型矩形区域创建内部转为Float32createTextInlineObject(ref:TextInlineObj, x, y, width, height, img:Image):SurfaceCore复用传入对象先把旧对象release()再重建链式调用返回thiscreateTextInlineObject(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 - leftheight 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.cjforeign 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 CPointerUnit即底层原生对象指针。可以看出Bitmap 位图走rApiDctClassCreateTextInlineObjectLayer 图层走rApiDctClassCreateTextInlineObjectLay带Lay后缀传入既非位图也非图层的图片时会抛出rgfErrorCpluapluaObjNotExist异常创建前会先校验表面与图片的isValid()任一无效同样抛异常。因此内联对象的内容源实际上可以是任意位图或图层这为后续在图层上绘制后再嵌入文本提供了想象空间例如先在一个图层中组合多张图片再整体作为内联对象置入文本。四、将内联对象嵌入文本布局创建好的内联对象只有通过TextLayout.setInlineObject()置入文本布局才会真正参与排版与渲染。该方法的源码在 rgf_text.cjpublic 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 参数语义参数类型说明objectTextInlineObj要嵌入的行内对象需isValid()否则抛异常startPositionUInt32字符开始位置从 0 起lengthUInt32字符长度即内联对象替换掉的字符数量textRangeTextRange文本范围封装startPositionlength见 类 TextRange对应的底层 FFI 声明在 rgf_text_dw.cjforeign func rApiTxtLayoutSetInlineObject(textLayoutClass:Layout,object:InlineObj,startPosition:UInt32,length:UInt32):Bool4.2 使用示例沿用配套课程 014_文本内联对象.md 的写法// 将文本内联对象替换入文本 txtlyt.setInlineObject(txtInline, 0/*字符开始位置*/, 1/*字符长度*/);这段代码的含义是用内联对象图片替换布局文本中第 0 个字符起、长度为 1 的原始字符。例如布局文本是绘制的文本则最终渲染时首字符位置显示的是图片而非文字。4.3 读取内联对象getInlineObject 与 RangeInlineObjRGF 同样提供了反向查询能力。getInlineObject(currentPosition)rgf_text.cj可以查询某个字符位置是否挂有内联对象返回OptionRangeInlineObjpublic func getInlineObject(currentPosition:UInt32):OptionRangeInlineObj{ ... let retv:RangeInlineObj RangeInlineObj( obj:if(inlineObj.isNull()){ OptionTextInlineObj.None } else { TextInlineObj(inlineObj) }, range:unsafe{textRange.read()}.toClass() ) ... }RangeInlineObj定义于 rgf_text_dw.cj包含两个字段字段类型说明objOptionTextInlineObj命中的内联对象无则为NonerangeTextRange该内联对象覆盖的文本范围注意getInlineObject返回的是新包装的TextInlineObj实例由底层指针构造与最初创建时传入的对象并非同一实例但其底层指针相同实际使用时应以isValid()和底层指针为准。五、内联对象的其他应用文本修剪符号除了替换正文文字TextInlineObj还有一个重要用途作为文本修剪Trimming的省略号符号。RGF 的TextFormat.setTrimming()允许指定一个内联对象作为超出布局宽度时的裁剪标记rgf_text.cjpublic func setTrimming(trimmingOptions:Trimming,trimmingSign:OptionTextInlineObj):Bool配套的TrimmingPck包装类同样持有一个OptionTextInlineObj类型的trimmingSign字段rgf_text_dw.cjpublic class TrimmingPck{ public let trimmingOptions:Trimming public let trimmingSign:OptionTextInlineObj const public init( trimmingOptions!:Trimming Trimming(), trimmingSign!:OptionTextInlineObj OptionTextInlineObj.None ) { ... } }这意味着开发者完全可以自定义一个图片型省略号例如…样式图标替代默认的文本修剪符实现更灵活、更品牌化的文本溢出表现。六、资源管理与最佳实践6.1 自释放 vs 手动释放手动释放TextInlineObj创建后应在合适的时机调用release()如destroyDeviceResources()事件或如示例中bmp.release()般用完即放。release()会调用底层rApiSfcSubObjRelease并置空objPtrrgf_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_CJRGF是仓颉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 仓颉版文本内联对象TextInlineObj实战指南实现文本与图片混合排版RGF 仓颉版文本内联对象TextInlineObj实战指南实现文本与图片混合排版 导读 在 RGF仓颉版通用渲染框架中文本内联对象 TextIn图形学桌面应用RGF_CJ 文本内联对象实战在仓颉渲染框架中实现图文混合排版RGF_CJ 文本内联对象实战在仓颉渲染框架中实现图文混合排版 在桌面应用中图文混排是文本排版的高频需求——例如聊天消息中的表情图片、富文本编辑器里的插图形学桌面应用RGF 仓颉版文本渲染详解RangeInlineObj 局部内联对象与文本内联查询RGF 仓颉版文本渲染详解RangeInlineObj 局部内联对象与文本内联查询 本文以 RGF 仓颉版 rgf.rgf_core 子包中的 RangeIn图形学桌面应用上一篇LoadingStateView 终极指南快速实现优雅的 Android 加载状态管理下一篇RecordBuilderJava记录构建工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考