本篇讲述如何进行界面调色。界面调色一般有两种方法,调色板和HSL色彩变换。调色板局限于256色,这里不采用,因此用HSL色彩变换实现。首先要了解一下什么是HSL色彩空间,完整且详尽的知识请到维基百科去看,链接地址:http://zh.wikipedia.org/wiki/HSL%E5%92%8CHSV%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4,这里简单讲一下(摘自维基百科):
HSL 和 HSV(也叫做 HSB)是对RGB 色彩空间中点的两种有关系的表示,它们尝试描述比 RGB 更准确的感知颜色联系,并仍保持在计算上简单。HSL 表示 hue(色相)、saturation(饱和度)、lightness(亮度),HSV 表示 hue、 saturation、value 而 HSB 表示 hue、saturation、brightness(明度)。如下图:

HSL 和 HSV 二者都把颜色描述在圆柱体内的点,这个圆柱的中心轴取值为自底部的黑色到顶部的白色而在它们中间是的灰色,绕这个轴的角度对应于“色相”,到这个轴的距离对应于“饱和度”,而沿着这个轴的距离对应于“亮度”,“value”或“明度”。
把RGB颜色转换到HSL色系,进行调色就很简单了,改变色彩只要转个角度,改变亮度就是沿轴升降进行“切片”,饱和度就是改变到中心轴的距离,3点定位到一个颜色,再转回RGB颜色就完成了界面调色。对需要调色的贴图进行这么一个变换,再重新贴图刷新一下界面就完成了调色功能。

并不是所有图片都需要调色,象标题栏右边的沙滩图案,如果调色就很难看了,会象照片的负片效果,因此用户头像,底纹图案,文字,图标都不需要调色,只需要把背景图案,按钮的高亮和按下状态图片,菜单背景和高亮图案进行色彩变换就行了。系统按钮需要局部调色,把最小化和最大化,还原按钮的高亮和按下状态的背景进行调色,因此准备了另外一张图片,一共5张图片需要调色:

最后一张图片是调色后透明绘制到系统按钮源图象上,实现了局部调色。

HSL色彩变换的实现是通过挂接图象库的滤镜插件类实现,实现原理请看RingSDK的帮助文档,调用代码极其简单,m_dibBkg.GETFILTER(dibFilterEFFECT)->AdjustHSL(h,s,l);就完成了背景的调色。图象库实现了两个滤镜插件类,dibFilterALPHA和dibFilterEFFECT,实现图象的各种ALPHA混合效果和亮度对比度,色度调整等。这里把关键的调色代码贴出来,其中m_rdib->Data()为已加载的32位色图象数据:

[cpp] view plaincopy
1. //调整色调,参数范围:-180~180(度),=0不作调整
2. //调整饱和度,参数范围0~200(建议,最大值可>200),=100不作调整
3. //调整亮度,亮度参数范围0~200(建议,最大值可>200),=100不作调整
4. BOOL dibFilterEFFECT::AdjustHSL(int degHue,int perSaturation,int perLuminosity)
5. {
6. if(!m_rdib->Data())
7. return FALSE;
8.
9. if(perSaturation < 0 || perLuminosity < 0)
10. return FALSE;
11.
12. if(degHue == 0 && perSaturation == 100 && perLuminosity == 100)
13. return TRUE; //未作调整,直接返回
14.
15. LPBYTE pRed, pGrn, pBlu,pBuf=(LPBYTE)m_rdib->Data();
16. UINT loop = m_rdib->Width() * m_rdib->Height();
17. COLORREF *cr = (COLORREF*)m_rdib->Data();
18.
19. pRed=pBuf++;pGrn=pBuf++;pBlu=pBuf;
20. float H,S,L;
21.
22. for (UINT i=0;i<loop;i++)
23. {
24. RGBtoHSL(*pRed,*pGrn,*pBlu,&H,&S,&L);
25.
26. H += degHue;
27. S = (S*perSaturation/100.0f);
28. L = (L*perLuminosity/100.0f);
29.
30. *cr = HSLtoRGB(H,S,L);
31.
32. pRed+=4;
33. pGrn+=4;
34. pBlu+=4;
35. cr ++;
36. }
37. return TRUE;
38. }
39.
40. void dibFilterEFFECT::RGBtoHSL(BYTE R,BYTE G,BYTE B,float* H,float* S,float* L)
41. {
42. BYTE minval = min(R,min(G,B));
43. BYTE maxval = max(R,max(G,B));
44. float mdiff = float(maxval) - float(minval);
45. float msum = float(maxval) + float(minval);
46.
47. *L = msum / 510.0f;
48.
49. if (maxval == minval)
50. {
51. *S = 0.0f;
52. *H = 0.0f;
53. }
54. else
55. {
56. float rnorm = (maxval - R) / mdiff;
57. float gnorm = (maxval - G) / mdiff;
58. float bnorm = (maxval - B) / mdiff;
59.
60. *S = (*L <= 0.5f) ? (mdiff / msum) : (mdiff / (510.0f - msum));
61.
62. if(R == maxval)
63. *H = 60.0f * (6.0f + bnorm - gnorm);
64. if(G == maxval)
65. *H = 60.0f * (2.0f + rnorm - bnorm);
66. if(B == maxval)
67. *H = 60.0f * (4.0f + gnorm - rnorm);
68. if (*H > 360.0f)
69. *H -= 360.0f;
70. }
71. }
72.
73. COLORREF dibFilterEFFECT::HSLtoRGB(float H,float S,float L)
74. {
75. BYTE r,g,b;
76.
77. L = min(1,L);
78. S = min(1,S);
79.
80. if(S == 0.0)
81. {
82. r = g = b = (BYTE)(255 * L);
83. }
84. else
85. {
86. float rm1, rm2;
87.
88. if (L <= 0.5f)
89. rm2 = L + L * S;
90. else
91. rm2 = L + S - L * S;
92. rm1 = 2.0f * L - rm2;
93.
94. r = HueToRGB(rm1, rm2, H + 120.0f);
95. g = HueToRGB(rm1, rm2, H);
96. b = HueToRGB(rm1, rm2, H - 120.0f);
97. }
98. return RGB(r,g,b);
99. }
100.
101. BYTE dibFilterEFFECT::HueToRGB(float rm1,float rm2,float rh)
102. {
103. while(rh > 360.0f)
104. rh -= 360.0f;
105. while(rh < 0.0f)
106. rh += 360.f;
107.
108. if(rh < 60.0f)
109. rm1 = rm1 + (rm2 - rm1) * rh / 60.0f;
110. else if(rh < 180.0f)
111. rm1 = rm2;
112. else if(rh < 240.0f)
113. rm1 = rm1 + (rm2 - rm1) * (240.0f - rh) / 60.0f;
114.
115. float n = rm1*255;
116. int m = min((int)n,255);
117. m = max(0,m);
118. return (BYTE)m;
119. }

实现了界面调色功能,接下来就得实现调色的设置界面,QQ2009的调色设置是预置了8种基色,可以选择一种基色,也可以对HSL分别调色,但是这8种基色是随着底纹和皮肤的选择变动的,皮肤的选择影响可选择的底纹和基色,一开始没怎么搞懂,后来下了一个QQ2009的主题包,结果是一个自定义格式的打包文件,没办法解开看里面图片,就没研究了,再下了一个2008版的主题包,可以看到所用的图片,大致清楚其为什么这么做了,里面的底纹图片是BMP格式,用紫红做透明色,由于没有ALPHA混合通道,这样的图片透明绘制到背景,边缘是有锯齿的,除非背景颜色跟图象边缘颜色相近才看不出来,因此选择的皮肤会限定底纹图片和背景颜色,选择底纹会同时变动背景颜色,再调色则对背景和底纹一起调色,底纹图案尽量采用RGB色差不大的图案,使调色效果不会太明显,这样整个界面调色底纹图案就没有了锯齿,变化也不怎么看得出来,是一种比较巧妙的做法。当然这是QQ以前版本的做法,2009应该已经改进了。这里演示的界面调色功能由于底纹图案是采用了带ALPHA通道的PNG格式图片,因此不存在这样的限制,同时为了简单起见,代码易于理解,就不学QQ的做法了,取消皮肤的设定,调色和底纹的选择互不影响,不做联动。在资源里预置了5张底纹图片可供选择,有兴趣的可以自己加。这里只是为简单起见而将底纹加到了资源里面,实际为了实现换肤并可扩展应该是自己实现一个换肤的配置文件和资源包,这里就不演示了。

现在实现调色的设置界面,首先响应“更改外观”按钮的弹起事件(WM_LBUTTONUP消息里面),弹出设置窗口,这个窗口是资源里面调色和底纹对话框的大小,不可拖动和调整大小,需要在失去焦点时自动隐藏,因此需要在其WM_ACTIVATE消息里面判断一下,如果是WA_INACTIVE状态就发送一个退出消息把自己关闭。加载调色和底纹两个对话框,显示一个,把另一个隐藏,调色和底纹的选择看起来应该是TAB控件,不过得自绘,这里用图片控件模拟TAB反而简单,反正只有两个选择,判断一下鼠标位置,换个图片,切换一下两个对话框的显示状态,看代码就知道了。

这里主要讲解一下调色对话框上4个Slider控件的自绘。控件自绘需要先子类化,RingSDK界面库已经封装好了,继承相应的控件类,子类化是自动的,可以重载其RingdowProc函数,即控件的窗口过程,想做什么都可以,给了你最大的自由度,未处理的消息返回DefaultProc或基类的RingdowProc就可以。自绘的Slider控件是继承自RingTrackBar,只要响应其WM_PRINTCLIENT和WM_PAINT消息,把背景图案和滑块图案画上去就OK了,代码很简单:

[cpp] view plaincopy
1. LRESULT RingTrackBarEx::RingdowProc(HWND hWnd,RINGPARAMS param)
2. {
3. switch(param.uMsg)
4. {
5. case WM_PRINTCLIENT:
6. case WM_PAINT:
7. {
8. RECT rc,rcc;
9. HDC hdc,hMemDC;
10. PAINTSTRUCT ps;
11. GetWindowRect(m_hWnd,&rc);
12. OffsetRect(&rc,-rc.left,-rc.top);
13. hdc = param.wParam?(HDC)param.wParam:BeginPaint(hWnd,&ps);
14. FillRect(hdc,&rc,m_brush);
15.
16. hMemDC = CreateCompatibleDC(hdc);
17. SelectObject(hMemDC,m_hbmLine);
18. GetChannelRect(&rcc);
19. StretchBlt(hdc,rcc.left,(rc.bottom - m_sizeLine.cy)/2,rcc.right-rcc.left,m_sizeLine.cy,
20. hMemDC,0,0,m_sizeLine.cx,m_sizeLine.cy,SRCCOPY);
21. SelectObject(hMemDC,m_hbmThumb);
22. GetThumbRect(&rc);
23. BitBlt(hdc,rc.left,rc.top,rc.right-rc.left,rc.bottom-rc.top,hMemDC,0,0,SRCCOPY);
24.
25. DeleteDC(hMemDC);
26. if(param.wParam == 0)
27. EndPaint(hWnd, &ps);
28. return 0;
29. }
30. }
31. return DefaultProc(param);
32. }

因为调色函数AdjustHSL的参数关系,设置调整色调的控件调整范围值为-180 ~ 180,初始值为0,饱和度和亮度的范围都是0 ~ 200,初始值为100。关于透明度的调整,怎么使窗口半透明网上介绍文章有很多,这里就不罗嗦了,界面库对此做了封装,调用SetLayeredAlpha就可以了,不过范围值是设成了25 ~ 255,不能0 ~ 255,全透明的话窗口就不能操作了。

调整色调的图片是展开的HSL色环,如下图:

注意因为默认的背景是兰色,因此兰色是在图案的中间,如果默认背景是别的颜色,就需要循环平移这些色彩,使背景色处在中间位置,否则拖动滑块调色,实际的效果跟滑块所在的颜色就对不上号了。

底纹图案的选择很简单,重新加载选择的图案就OK,看代码就知道了。

至此完成了界面调色功能,界面中间部分因为以后会有控件遮盖,因此不需要调色。现在看看程序的截图:

最后说一下怎么做带ALPHA通道的PNG图片和加到程序里面:
首先找一张自己满意的图片,调整到高度95,QQ标题栏的高度,宽度随意,只要图案不怎么变形就可以,然后加一个蒙板,如图,按一下图中的按纽:

要使图片透明,需要去除背景,如果是新建图片并复制了图象过来,把背景层删掉,如果是直接打开的图片,需要复制一层然后把背景层删掉。加了蒙板后选择渐变工具,从右往左一拉,然后把图片保存为PNG格式就可以了,如图:

做完PNG图片还要做一张31*31的预览BMP图片,按资源里的格式做就可以了,把PNG加入为“PNG”类型的资源,ID值必须与resource.h里IDP_SEA等5张预置图片的ID值连续,预览图片的ID值必须与IDB_TATOO1~IDB_TATOO5的值连续,在wnduioption.cpp里面找到gszTatooInfo的初始化代码,把图片说明加到里面,编译一下程序就OK了。

现在这个仿QQ界面的程序已经完成了标题栏部分的功能,接下来要完成客户区和底栏部分的功能,留待下一篇再讲了。

程序代码下载地址:http://download.csdn.net/source/1995651

转载于:https://www.cnblogs.com/blogpro/p/11459952.html

循序渐进实现仿QQ界面(三):界面调色与控件自绘相关推荐

  1. vc循序渐进实现仿QQ界面(三):界面调色与控件自绘

    本篇讲述如何进行界面调色.界面调色一般有两种方法,调色板和HSL色彩变换.调色板局限于256色,这里不采用,因此用HSL色彩变换实现.首先要了解一下什么是HSL色彩空间,完整且详尽的知识请到维基百科 ...

  2. Android仿QQ,网易邮箱客户端收件人控件

    效果图 实现思路 这个功能首先想到的是安卓原生控件AutoCompleteTextView和MultiAutoCompleteTextView 前者用的还是比较多的,比如字符联想,最近搜索记录等用的都 ...

  3. Android 仿QQ可展开的分组列表控件(ExpandableListview)的使用

    ExpandableListview的体现最常见于QQ分组,或者联系人分组

  4. C#仿QQ皮肤-Label与ListBox 控件实现----寻求滚动条的解决方案

    阅读全文:http://www.sufeinet.com/thread-2110-1-1.html 大家还是先来看看效果吧 这次之所以一次写两个控件,其实主要是因为Label控件实在是太简单了没有必要 ...

  5. android 仿qq修改头像,Qt:小项目仿QQ修改头像界面,技术点记录

    最近写了一个修改头像功能的UI,布局参考了QQ目前的修改头像界面.如下图 这里主要说明一下两个地方的技术:1.头像图片上层的遮罩层,圆形外部为灰色,内部为全透明:2.上传图片宽高比例可以通过鼠标拖拽移 ...

  6. android开发之仿QQ拖拽界面效果(侧滑面板)

    仿QQ拖拽界面效果(侧滑面板),我们一般继承Layout,不会直接去继承ViewGroup,而是继承FrameLayout,为什么五大布局我们偏偏只继承FrameLayout呢? 第一,FrameLa ...

  7. 仿QQ登入界面增加反应

    上次,咋们做了一个仿QQ登入界面,现在咋们的目标就是让这个界面上的组件有所反应. 这个交互功能需要监听器这个方法来实现. 监听器的实现三步骤 1.事件源:当前动作所发生的组件. 2.监听器:动作方法监 ...

  8. Qt界面优化:Qt窗体控件设置

    Qt界面优化:Qt窗体控件设置 文章目录 Qt界面优化:Qt窗体控件设置 一.效果图 1.鼠标移动点击效果 2. Qt托盘效果 二.使用步骤 1. 背景图片的添加 2. 控件样式的更改 3. Qt窗体 ...

  9. qpython3可视图形界面_python GUI库图形界面开发之PyQt5窗口控件QWidget详细使用方法...

    QWidget基本介绍 基础窗口控件QWidget类是所有用户界面对象的基类,所有的窗口或者控件都直接或者间接的继承自QWidget类. 窗口坐标系统 PyQt使用统一的坐标系统来定位窗口控件的位置和 ...

最新文章

  1. 常考数据结构与算法:买卖股票的最好时机
  2. split | notes in java
  3. __I、__O、__IO什么意思和处于keil里面什么位置
  4. GitHub Research:超过50%的Java记录语句写错了
  5. c++for循环求最大公约数_C/C++编程笔记:C语言 for 循环精讲!实例讲解带你吃透...
  6. 【SLAM笔记】三维刚体运动
  7. 让一个软件系统同时支持多个多种数据库灵活配置,让客户可以任意配置各个核心数据库部分...
  8. Oracle Database 11g 下载
  9. Canvas beginPath()、moveTo()、 lineTo() 、stroke() 绘制直线路径,fillText() 绘制填色的文本
  10. 谷歌安装FeHelper插件
  11. 【转载】PowerDomain简介
  12. M1版本Photoshop2021原生ARM完整安装方法下载(适配M1芯片全系Mac教程)
  13. char *p 定义时指定的值不可以修改
  14. JMeter Sampler之BeanShellSampler的使用
  15. string find的用法详解
  16. 现代金融业务--计算题合集(自用,quiz的解答瞎写的)
  17. 基于Multisim的buck降压斩波电路仿真
  18. Ctrix卸载ReceiverCleanupUtility.exe
  19. 乌云安全所有资料(百度云打包)pdf
  20. C++ strtok_s() 分割字符串

热门文章

  1. 【Ansible】 Ansible 模块 setup 与 stat 模块用法
  2. 微软 Teams 等服务大规模宕机
  3. B3U3 Text A Language Focus 共32词
  4. python实现简单的图书管理系统
  5. JAVA web中的一点东西
  6. Win10文件或目录损坏且无法读取修复方法
  7. word论文格式整理
  8. 至快乐的11.11---牛X光棍的呐喊!(转)
  9. java 办公_Java003-协同办公OA
  10. arcgis中字段的融合_利用ArcGis进行地理处理之三(融合)