UE4取色器实现里的 GetPixel HDC 释放,把 Codex 的接口地址改到 TaoToken 后对照检查

发布时间:2026/9/20 20:33:57
UE4取色器实现里的 GetPixel HDC 释放,把 Codex 的接口地址改到 TaoToken 后对照检查 1. UE4 取色器的场景与两个暗坑UE4 取色器里用 GetPixel 读屏幕像素看起来就三行 API真正让人回头翻代码的往往是 HDC 释放有没有配对、COLORREF 拆出来的 R/G/B 有没有错位。这篇不打算把取色函数改成联网调用而是把用来复核这段代码的 Codex 接到 TaoToken先把 Base URL 指向 https://taotoken.net/api再让 Codex 对着 GetDC、GetPixel、ReleaseDC 逐行核对顺带把颜色盘那套 RGB 与 HSV 的换算也过一遍。如果你手上正好有 ColorPickerFunctionLibrary.cpp跟着走一遍就能拿到一份能直接改的检查清单而不是对着屏幕 DC 的返回值干瞪眼。1.1 取色函数实际在做什么在 UE4 里做屏幕取色链路其实很短。先用 GetCursorPos 把鼠标的屏幕坐标读进一个 POINT再用 GetDC(NULL) 拿到覆盖整个屏幕的设备上下文句柄接着 GetPixel(hdc, p.x, p.y) 返回一个 COLORREF最后把通道拆出来塞进 FColor 交给蓝图或者 Slate 控件。整个过程放在 UColorPickerFunctionLibrary 这种 BlueprintFunctionLibrary 里用 UFUNCTION(BlueprintCallable) 暴露出去蓝图侧一个节点就能拿到颜色。问题在于这段链路里只有 GetPixel 是「读」其余全是「借」和「还」。借了屏幕 DC 不还或者还错了对象编辑器短时间跑着看不出异常一旦做成带 Tick 的吸管工具几分钟后 GDI 对象计数就上去了。更隐蔽的是通道顺序取一块纯红区域却出来蓝色第一反应往往是怀疑 DPI 或者显示色彩空间实际上大概率只是 COLORREF 的角度和 FColor 的角度不一样。1.2 HDC 配对与 COLORREF 通道顺序先说 HDC。Windows 里拿 DC 有两个常见入口GetDC(NULL) 和 CreateDC。前者是「借用」释放必须用 ReleaseDC(NULL, hdc)第一个参数要和获取时一致后者是「新建」释放必须用 DeleteDC(hdc)。两者不能互换ReleaseDC 去释放 CreateDC 出来的句柄返回值会告诉你失败但很多人不检查返回值于是泄漏就这么留下了。再说 COLORREF。它的内存布局是 0x00BBGGRR低字节是红高字节是蓝和直觉里的 RGB 正好反过来。Windows 提供了 GetRValue、GetGValue、GetBValue 三个宏来拆所以只要老老实实按宏来写就不会错。麻烦出在有人图省事手写位运算或者记错了宏的对应关系写出 FColor(GetBValue(c), GetGValue(c), GetRValue(c)) 这种红蓝对调的代码。取绿色和灰色时看不出来一取红色立刻露馅。2. TaoToken 前置给 Codex 换一条模型通道2.1 先创建一个可用的 KeyCodex 默认走的是官方通道要让它去读你本地的 UE4 工程并给出针对性的修改建议得先有一条能用的模型通道。打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 完成账号流程后进入控制台在 API Keys 页面新建一个 Key。新建时建议只给它起个能认出来的名字比如 ue4-codex-check方便以后按项目回收。Key 只会完整显示一次复制后先放在手边。不要把它直接写进工程的 Config 目录也不要贴进任何会进版本库的文件里。UE4 项目里 Source 目录经常被整个提交一旦 Key 混进去清理起来比重建一个还费劲。2.2 Base URL 应该填到哪一层Codex 这类 CLI 客户端的地址配置分两层一层是 Base URL也就是服务入口另一层是具体的接口路径由客户端自己拼接。这里要填的是 Base URL值是 https://taotoken.net/api后面不要再手工补 /v1 或者 /chat/completions。有些客户端在配置里要求写全路径那就以客户端文档为准但 Codex 的 config.toml 走的是 base_url 字段填到 /api 这一层就够了。如果你用的是别的编辑器插件或者自己写的脚本判断标准很简单请求最终打到 https://taotoken.net/api 开头的地址鉴权头带上 Bearer 加 Key就说明入口没问题。反过来如果日志里出现的是官方域名说明环境变量或者配置文件没生效客户端还在读旧的默认值。3. 可复制配置Codex 的 config.toml 与 Base URL3.1 写一份 config.tomlCodex 的全局配置在用户目录下的 .codex/config.tomlWindows 一般是 C:\Users\你的用户名.codex\config.tomlmacOS 和 Linux 是 ~/.codex/config.toml。下面这份可以直接抄把 model 换成你控制台里实际可用的模型名即可model gpt-5-codex model_provider taotoken model_reasoning_effort medium [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api responseswire_api 这一项按你所用模型通道的实际协议来定如果客户端报「接口不存在」或者解析响应失败把它改成 chat 再试一次。base_url 保持 https://taotoken.net/api 不动注意不要写成带尾部斜杠的形式部分客户端会把斜杠和路径拼成双斜杠。3.2 用环境变量放 Key配置文件里不写明文 Key靠 env_key 指向环境变量。Windows PowerShell 临时生效$env:TAOTOKEN_API_KEY sk-你的Key要长期保留写进用户级环境变量[Environment]::SetEnvironmentVariable(TAOTOKEN_API_KEY, sk-你的Key, User)macOS 和 Linuxexport TAOTOKEN_API_KEYsk-你的Key echo export TAOTOKEN_API_KEYsk-你的Key ~/.zshrc设完之后新开一个终端用echo $env:TAOTOKEN_API_KEY或者echo $TAOTOKEN_API_KEY确认变量真的在。踩过的坑大多出在这里在 A 终端设了变量在 B 终端跑 Codex自然读不到。3.3 先验证通道能不能通在动 UE4 代码之前先用一条最小请求确认通道可用避免把配置问题和代码问题混在一起排查curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {model:gpt-5-codex,messages:[{role:user,content:reply with ok}],max_tokens:16}返回体里能看到 choices 数组和正常的 message 内容就说明 Key 和地址都没问题。这里模型名如果不在你的可用列表里会直接返回错误以控制台里列出的名字为准不要照抄命令行里的字符串。4. 验证请求对着 GetDC/GetPixel/ReleaseDC 逐行核对4.1 检查提示词怎么写进入工程根目录用 Codex 打开对话把范围限定清楚。提示词里说清三件事要检查哪些文件、检查哪几个点、按什么格式输出。下面这段可以直接用请阅读 Source/First/ColorPickerFunctionLibrary.cpp 和对应的 .h 聚焦 GetPixelFromCursorPostion 这个函数 1. GetDC / ReleaseDC 是否成对是否存在提前 return 导致漏掉释放的分支 2. GetCursorPos 和 GetPixel 的返回值是否检查CLR_INVALID 是否处理 3. COLORREF 拆 R/G/B 的顺序是否正确转换到 FColor 是否通道错位 4. 返回类型从 float 到 FColor 的隐式转换有没有隐患。 按「问题位置 / 风险 / 修改建议」三列输出并给出修改后的完整函数。把提示词写具体比笼统地说「帮我看看这段代码」有用得多。限定文件和函数之后Codex 不会到处发散输出的建议也基本落在你能直接改的地方。如果工程里有多个取色入口一次只喂一个检查质量比一把梭高。4.2 期望拿到的结论与修复实现一份正常的检查结果通常会指出原始写法有两个隐患一是异常分支没有释放 DC二是 float 到 FColor 的转换不必要。修复后的函数大致是这个样子判断顺序和释放路径都补上了FColor UColorPickerFunctionLibrary::GetPixelFromCursorPostion() { HDC hdc ::GetDC(nullptr); if (hdc nullptr) { return FColor::Black; } POINT cursorPos { 0, 0 }; if (!::GetCursorPos(cursorPos)) { ::ReleaseDC(nullptr, hdc); return FColor::Black; } const COLORREF pixel ::GetPixel(hdc, cursorPos.x, cursorPos.y); ::ReleaseDC(nullptr, hdc); if (pixel CLR_INVALID) { return FColor::Black; } return FColor( static_castuint8(GetRValue(pixel)), static_castuint8(GetGValue(pixel)), static_castuint8(GetBValue(pixel)), 255); }改动点集中在三处GetDC 之后立刻判空GetCursorPos 失败时先还 DC 再返回GetPixel 的结果和 CLR_INVALID 比较避免把失败值当成黑色。通道顺序保持 GetRValue 到 R、GetGValue 到 G、GetBValue 到 B和 FColor 的构造顺序一致。原来的 float 中间变量可以去掉了FColor 的构造参数本来就是 uint8多绕一圈只会让类型转换的意图变模糊。4.3 顺带把颜色盘的 RGB/HSV 换算也问了取色只是入口颜色盘那部分更容易出边界问题。可以在同一个会话里追加一轮让 Codex 一起看颜色盘用角度决定色相S 条是白色到纯色V 条是黑色到当前色。 请给出 RGB 与 HSV 互转的实现建议重点说明 角度到 H 的映射是 0-360 还是 0-1 H 在 0/360 边界和负角度时的处理 S0 时 H 的取值为什么要归零。 用 UE4 的 FLinearColor 和 FColor 各写一版。比较稳的做法是角度归一化到 [0,1)再做六段插值而不是直接拿角度去算三角函数。S 条和 V 条用材质做渐变是合适的但取值时必须先把 H 固定、S 或 V 拉满否则拖动时颜色会整体偏灰。S0 时色相无意义统一归零可以避免取色器在灰度区域来回跳 H 值。这部分结论让 Codex 列一遍再对照引擎里的 FLinearColor::MakeFromHSV8 复核基本不会跑偏。5. 常见错排查401、404、HDC 泄漏与 R/B 错位5.1 接入侧的现象与处理现象常见原因处理方式401 UnauthorizedKey 未进环境变量或 Bearer 拼写有误新开终端确认变量存在检查请求头格式404 接口不存在base_url 多写了 /v1或 wire_api 协议不匹配base_url 保持 https://taotoken.net/api切换协议再试响应解析失败客户端按旧协议解析返回体核对客户端版本与 wire_api 配置一直走默认通道改了项目配置但全局配置优先级更高检查 ~/.codex/config.toml 的 model_provider排查顺序建议从外往里先 curl 直连确认 Key 有效再跑 Codex 的最简提问确认客户端读到了配置最后才让它读工程文件。顺序反了很容易把网络问题和代码问题搅在一起。5.2 UE4 侧的现象与处理HDC 泄漏最典型的表现是取色工具连续运行几分钟后开始返回黑色或者编辑器整体变卡。判断方法是把 GetDC 的返回值打日志如果某次开始一直为空基本可以确定是句柄没还。检查点就一个函数里每条 return 路径之前是不是都走过 ReleaseDC。R/B 错位则表现为取红色显示成蓝色但取灰阶完全正常。这时不要去看色彩空间设置先把 FColor 的三个参数和 GetRValue、GetGValue、GetBValue 一一对上。另外一个容易忽略的点是编译告警UE4 对浮点转整数的隐式转换在部分配置下会给提示如果日志里有 narrowing 相关告警顺手把中间类型改成 uint8。还有一类是坐标问题GetCursorPos 返回的是物理像素坐标高 DPI 缩放下和 Slate 的本地坐标不是一套体系。取色本身用物理坐标没问题但把结果画到 UI 上时要做一次换算否则吸管落点和颜色显示位置会差一截。6. 接入文档与 API Keys 入口把 Codex 的 Base URL 改成 https://taotoken.net/api 之后取色器这段代码的复核就变成了一个可重复的动作改完 HDC 释放路径再跑一轮提示词确认没有新的分支漏掉释放改完通道顺序取一块纯红区域验证输出。之后无论你是加多显示器支持、加取色历史还是把吸管接进 Slate 控件都能用同一套检查流程兜底。需要新建或回收 Key直接进 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 接口路径、鉴权头、参数格式这些细节在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里有完整说明。如果你后面打算把这类代码检查做成长期跑的任务可以看看 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 把模型通道和额度固定下来比每次临时配一遍省事。