制作可点击的异形窗口(1)
设计丢来一个系统升级界面UI图,是个异形窗口,即有窗体圆角又有个火箭头凸出来,挺带感的。旧貌换新颜,这事值一做。
一. 预处理
首先想到的实现方式是填个底色,然后设置窗体的TransparentColor为该底色即可,毕竟软件启动时的splash屏就是这么实现的。
在PhotoShop里填充上黄色底色:
导出为WEB格式时,选择PNG8格式,并仔细裁剪掉边缘杂色。最后存储为png图像文件。
二. 通过TransparentColor属性实现异形窗体
设置窗体TransparentColor=true、TransparentColorValue=黄色底色,补上点击和窗体移动代码,运行测试效果。
显示效果倒是实现了,但有一个问题:点击窗体上半部分会穿透到下层窗口,下半部分却能正常响应。反复测试发现该现象与图片的宽高比例关系不大。
三. 通过RGN实现异形窗体
既然点击不正常,而delphi窗体自带的的实现方法不便修改,那就换种实现方式:直接读取像素信息,把底色对应的区域从窗体 RGN 中剔除。
快速撸了一段实现代码:
procedure TfrmUpdate.FormCreate(Sender: TObject); var bmp: TBitmap; FullRgn, LineRgn: HRGN; x, y, StartX: Integer; Row: PRGBTriple; //从ScanLine读取像素 IsTransparent: Boolean; TargetB, TargetG, TargetR: Byte; begin ClientWidth := Image1.Width; ClientHeight := Image1.Height; //底色的RGB分量 TargetB := $00; TargetG := $D8; TargetR := $FF; bmp := TBitmap.Create; try bmp.Assign(Image1.Picture.Graphic); //24位色深度,让PRGBTriple准确对位 bmp.PixelFormat := pf24bit; //初始化一个空RGN FullRgn := CreateRectRgn(0, 0, 0, 0); //水平扫描线像素 for y := 0 to bmp.Height - 1 do begin //依当前图片特性优化计算,跳过没有透明色的区域。其他图片注释掉这两行 if (y > 55) and (y < bmp.Height - 8) then Continue; Row := bmp.ScanLine[y]; //获取当前行的内存指针 x := 0; while x < bmp.Width do begin //判断是否为底色(透明色) IsTransparent := (Row.rgbtBlue = TargetB) and (Row.rgbtGreen = TargetG) and (Row.rgbtRed = TargetR); //如果是透明色,一直往右找直到找到不透明的像素 while (x < bmp.Width) and IsTransparent do begin Inc(x); Inc(Row); if x < bmp.Width then IsTransparent := (Row.rgbtBlue = TargetB) and (Row.rgbtGreen = TargetG) and (Row.rgbtRed = TargetR); end; //记录这段不透明区域的起点X坐标 StartX := x; //从不透明像素起往右找,直到再次遇到透明色或到达边界 while (x < bmp.Width) and (not IsTransparent) do begin Inc(x); Inc(Row); if x < bmp.Width then IsTransparent := (Row.rgbtBlue = TargetB) and (Row.rgbtGreen = TargetG) and (Row.rgbtRed = TargetR); end; //将这段不透明的水平线段(StartX 到 x)加到RGN if StartX < x then begin //创建一个1像素高的矩形区域 (左闭右开:y到y+1) LineRgn := CreateRectRgn(StartX, y, x, y + 1); //将这个细长条合并到总区域中 (RGN_OR并集) CombineRgn(FullRgn, FullRgn, LineRgn, RGN_OR); DeleteObject(LineRgn); end; end; end; //依当前图片特性优化计算,将没有透明色的区域加入RGN LineRgn := CreateRectRgn(0, 55, bmp.Width, bmp.Height - 8); CombineRgn(FullRgn, FullRgn, LineRgn, RGN_OR); DeleteObject(LineRgn); //计算完毕将RGN应用到窗体 SetWindowRgn(Handle, FullRgn, True); DeleteObject(FullRgn); finally bmp.Free; end; end;运行后功能正常,达到要求的效果。
四. 针对PNG8位色优化
既然用的PNG图片,没必须再转一次24位色的bmp来计算,直接基于8位索引色的PNG图片来运算性能更佳。
首先,要处理下图片,将底色转为透明。
