利用DFM文件实现自定义窗体样式:为DDColor黑白老照片修复工具打造专属界面
发这篇文章前其实已经搁置很久了,一开始是因为家里那台老电脑跑不动ComfyUI,每次启动都卡得像幻灯片。直到某天我在GitHub上刷到了那个叫DDColor的项目——能把泛黄的老照片一秒变彩色,连爷爷奶奶的脸都能还原得栩栩如生!当时我就想,这么好的东西,为什么不能做成一个简洁漂亮的独立小工具呢?而不是每次都打开浏览器、找节点、拖来拖去?
于是我就动手了,想把它包装成一个真正属于自己的软件。最开始的想法很简单:搞个好看的界面,把几个常用的JSON流程一键加载就行。但很快我发现一个问题:默认的窗体太丑了,全是Win98风的那种灰白条,还有一堆我不想要的按钮(关掉它?不行,我要自己画!)。所以第一步就是去掉那些烦人的SysMenu、Maximize、Minimize按钮。
起初我也走了弯路,以为只要重绘Caption部分就行了,结果发现一旦改变窗口大小或者激活失焦,系统还是会强行画上去一层原生的东西,特别难看。后来我才明白,必须从源头下手:设置BorderStyle := bsNone,并完全接管所有鼠标行为。这一步看似简单,实则踩坑无数——比如刚移除边框时,整个窗体瞬间变成“无头苍蝇”,既不能拖动也不能关闭,差点让我放弃。
从“不可操作”到“自由掌控”的关键突破
为了让这个没有边框的窗体还能被用户正常拖拽移动,我翻遍了VCL的消息机制文档。最终在FormMouseDown事件里找到了突破口:
procedure TFormMain.FormMouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if Button = mbLeft then begin // 假设标题栏高度为60像素 if Y < 60 then Perform(WM_NCLBUTTONDOWN, HTCAPTION, 0); end; end;这一行Perform(WM_NCLBUTTONDOWN, HTCAPTION, 0)堪称点睛之笔。它模拟了Windows系统对非客户区(Non-Client Area)点击的处理方式,让操作系统误以为你在标准标题栏上按下了左键,从而触发窗口拖动逻辑。这样一来,哪怕你的标题栏是用五张图片拼出来的“假货”,也能拥有原生般的拖拽体验。
不过这里有个细节值得注意:如果你的窗体设置了透明度(AlphaBlend),某些显卡驱动下可能会导致HTCAPTION失效。我的解决办法是在设计阶段就把标题区域留出足够的高度,并确保该区域不包含任何半透明渐变层干扰判断。
自绘控件:轻量级美学的实践之路
我不想用第三方皮肤库,因为体积太大,而且很多都是破解版,作为一个技术人员,我觉得我们应该尊重开发者劳动成果。所以我决定自己做。
我把整个窗体的背景图拆成了五张TImage组件(img1, img2, img3, img4, img5),分别代表左上角、顶部拉伸区、右上角、左下按钮区、右侧按钮区。这些全部直接内嵌在DFM里,不需要外部资源文件,方便部署。
特别是Close按钮,我特意用了红色渐变加X符号,在鼠标悬停时会放大一点并加深颜色,给人一种“你真要退出吗?”的心理暗示。虽然只是细节,但我相信好产品藏在其中。
至于三个按钮:最小化、恢复/最大化、关闭,我没有用Button控件,而是用了TImage,每张图都做了Normal、Hover、Pressed三种状态,通过OnMouseMove和OnClick动态切换Picture.Data里的位图数据。这看起来有点笨,但胜在轻量且可控。
procedure TFormMain.imgCloseMouseEnter(Sender: TObject); begin imgClose.Stretch := True; imgClose.Width := Round(OriginalWidth * 1.1); imgClose.Height := Round(OriginalHeight * 1.1); end; procedure TFormMain.imgCloseMouseLeave(Sender: TObject); begin imgClose.Stretch := False; imgClose.Width := OriginalWidth; imgClose.Height := OriginalHeight; end;这种基于事件的状态管理虽然代码略多,但避免了引入复杂的动画框架或资源包管理系统,非常适合小型工具类应用。
DFM即资源:一体化部署的设计哲学
为了实现这一切,我在DFM里预先定义好了所有控件的位置与初始状态,例如:
object FormMain: TForm Left = 300 Top = 150 Width = 780 Height = 560 AlphaBlend = True AlphaBlendValue = 240 BorderStyle = bsNone Color = clWhite Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -12 Font.Name = '微软雅黑' Font.Style = [] OldCreateOrder = False Position = poScreenCenter OnMouseDown = FormMouseDown OnPaint = FormPaint同时,所有按钮图片均以内联方式存入TImage.Picture.Data中,确保打包后仍能正常显示:
object imgTitleLeft: TImage Left = 0 Top = 0 Width = 108 Height = 62 AutoSize = True Picture.Data = { 07544269746D6170...[省略大量Base64编码的BMP数据]... } end这样做最大的好处是:无需任何外部依赖,双击即用。非常适合送给长辈们修复家里的老相册。我记得第一次拿给父亲试用时,他居然没问“怎么安装”,而是直接双击打开了,“咦,这不就跟手机App一样嘛?”——那一刻我知道,这条路走对了。
集成AI能力:从界面美化到功能闭环
说到加载流程,就涉及到今天的主角:DDColor黑白老照片智能修复。这个镜像是基于ComfyUI环境的一个完整工作流,非常强大,支持人物和建筑物两种场景的自动着色修复。
使用方法如下:
打开软件后,点击【工作流】菜单 → 选择你要修复的类型:
- “修复黑白建筑老照片(DDColor建筑黑白修复.json)”
- 或者“人物黑白照片(DDColor人物黑白修复.json)”回到主界面,点击【加载图像】→ 浏览并上传你的黑白旧照
点击【运行】按钮,后台就会自动将图像传给ComfyUI引擎处理,几分钟内就能生成一张鲜活的彩色版本!
如果你觉得色彩不够理想,可以进入高级设置页,调整DDColor-ddcolorize节点中的
model_size参数:
- 对于建筑物类图像,建议设为960~1280
- 对于人脸或人像类图像,推荐使用460~680,既能保留细节又不会过度模糊五官
我甚至把两个常用尺寸预设成了快捷按钮,一键切换,不用再记数字。这些参数不是随便定的,是我反复测试几十张不同年代、不同清晰度的老照片后总结出来的经验值。比如人脸过大的模型会导致眼鼻嘴变形,而太小又无法捕捉纹理;建筑物则相反,需要更大的感受野来理解整体结构。
性能优化:不只是“看起来流畅”
最后提一句性能优化的小技巧:当窗口刷新时(Resize或Repaint),不要频繁重绘整张背景图,而是只更新需要变化的部分(比如按钮状态)。否则即使设置了DoubleBuffered := True,也可能出现闪烁。
另外,关于内存占用的问题,我测试过多次,这种纯DFM内嵌资源的方式比加载外部PNG/JPG反而更省内存,可能是因为Delphi内部做了压缩和缓存管理吧。尤其是在低配机器上运行时,少一次磁盘I/O读取,就意味着更快的启动速度和更稳定的响应表现。
还有一个隐藏技巧:在OnPaint事件中加入条件判断,仅在首次绘制或窗体大小发生变化时才重新布局背景图,其他时候交由系统自动维持画面状态。这样可以显著降低CPU占用率。
写给自己也写给同行的话
我不是专业美工,也不是大厂程序员,只是一个喜欢捣鼓小玩意儿的极客爱好者。但正因为这份热爱,才让我愿意花三天时间研究怎么让一个小小的“关闭按钮”按下去的时候有个微缩动画。
也许有人会说:“这也值得写篇文章?”
我想说的是:每一个伟大的软件,都始于某个不起眼的执念。
真正的个性化,不是靠贴图遮盖,而是从底层重新定义规则。当你不再依赖系统默认样式,而是亲手打造每一个像素时,你就离“做出让人眼前一亮的产品”更近了一步。
在此也要感谢开源社区提供如此强大的AI能力,让我们普通人也能玩转图像修复。希望这篇小文对你有启发。
PS:光标文件你可以自行替换,比如下载“Windows变脸王”这类工具提取你喜欢的主题光标,放入程序目录的Cursors子文件夹即可生效。