仿酷狗音乐播放器开发日志二十一 开发动态调色板控件(附源代码)

转载请说明原出处,谢谢~~

上一篇仿酷狗日志结束后,整个换肤功能就仅仅剩下调色板功能没有做了。我本以为会非常easy。可是研究了酷狗的调色板功能后发现不是那么简单的事情。首先看一下酷狗的调色板的样子:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemh1aG9uZ3NodQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" >

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemh1aG9uZ3NodQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" >

我原本以为酷狗的主界面仅仅是一张图片。然后通过鼠标坐标来选择颜色,简单粗暴。

等我開始做这部分时发现情况不一样。

能够看到,酷狗的调色板分为两部分,上半部分是调色板的主界面,下半部分是调整亮度的工具栏,我这里分别给他们起名为Pallet和Bar方便说明。这个调色板的Pallet部分的总体亮度能够依据Bar的值而改变,而Bar的颜色会依据Pallet的选择的颜色而改变,这就加大了难度。

这个调色板动态生成了颜色值。假设要做出一样的功能,首先要解决的就是得知这个调色板的绘制算法,这个着实让我头疼,由于我曾经从来没有研究过调色板,也不喜欢搞这样的算法。

我首先对不同亮度下的酷狗调色板截图,然后放到PS中。打开ps的调色板后对图片上的各个颜色取色来研究他们的规律。

总算有些发现,这个大家最好自己实践一下:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemh1aG9uZ3NodQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" >

在PS中有多重画图模式:RGB、HSB、CMYK、LAB。測试了几个图片后。发现这个调色板的图片能够从HSB模式发现规律。大致的规律是:在一行中向右取色,HSB三个值中仅仅有H的值变化。在一列中。从上往下取色,S值变大、B值变小。并且S值在达到图片的中间部分后再往下就不会增大,我把它称作S的临界值,而图片亮度越低,S的临界值就越小!而B值变小的速度是总体越来越快。

在CodeProject中查了几个资料和demo后,对画图的方式有了一些了解。

首先我补充一下HSB的知识,具体资料请自行百度:

HSB模式中,H(hues)表示色相。S(saturation)表示饱和度。B(brightness)表示亮度。 HSB模式相应的媒介是人眼。

HSB与HSV同样。

色相(H,hue):在0~360°的标准色轮上。色相是按位置度量的。

在通常的使用中,色相是由颜色名称标识的,比方红、绿或橙色。黑色和白色无色相。

饱和度(S,saturation):表示色彩的纯度。为0时为灰色。

白、黑和其它灰色色彩都没有饱和度的。在最大饱和度时,每一色相具有最纯的色光

取值范围0~100%。

亮度(B,brightness或V,value):是色彩的明亮度。为0时即为黑色。最大亮度是色彩最鲜明的状态。

取值范围0~100%。

HSB模式中S和B呈现的数值越高。饱和度明度越高。页面色彩强烈艳丽。对视觉刺激是迅速的,醒目的效果,但不易于长时间的观看。以上两种颜色的S数值接近,是强烈的状态。H显示的度是代表在色轮表里某个角度所呈现的色相状态,相对于SB来说,意义不大。

了解了HSB的意思后,首先要知道RGB与HSB的转换算法。由于在duilib中才用RGB的画图模式。这里给出基本的转换算法(部分算法并不是我写的而是摘自网络):

struct RGBColor{
	int r;
	int g;
	int b;
	RGBColor():r(0),g(0),b(0){}
};

struct HSBColor{
	int h;
	int s;
	int b;
	HSBColor():h(0),s(0),b(0){}
};
COLORREF CPalletUI::GetColorFromHSB(int h,int s, int b)
{
	COLORREF color;
	double H = h;
	double B = (double)b / 100;
	double S = (double)s / 100;
	double rgbR,rgbG,rgbB;

	H = (H  >= 360) ?

0 : H ;  

	if(S == 0) {
		rgbR = B * 255;
		rgbG = B * 255;
		rgbB = B * 255;
	} else
	{
		int i = ((int)floor((double)H / 60)) % 6;
		double f = H / 60 - i;
		double p = B * (1 - S);
		double q = B * (1 - S * f);
		double t = B * (1 - S * (1 - f));  

		switch(i) {
		case 0:
			rgbR = B, rgbG = t, rgbB = p;
			break;
		case 1:
			rgbR = q; rgbG = B; rgbB = p;
			break;
		case 2:
			rgbR = p; rgbG = B; rgbB = t;
			break;
		case 3:
			rgbR = p; rgbG = q; rgbB = B;
			break;
		case 4:
			rgbR = t; rgbG = p; rgbB = B;
			break;
		case 5:
			rgbR = B; rgbG = p; rgbB = q;
			break;
		}
		rgbR = rgbR * 255;
		rgbG = rgbG * 255;
		rgbB = rgbB * 255;
	}  

	color = RGB(rgbR + 0.5,rgbG + 0.5,rgbB + 0.5);
	return color;
}
HSBColor CPalletUI::GetHSBFromColor(COLORREF color)
{
	HSBColor hsb;
	RGBColor rgb = GetRGBFromColor(color);
	double R = rgb.r, G = rgb.g, B = rgb.b;

	double var_Min = min(min(R, G), B);
	double var_Max = max(max(R, G), B);  

	double hsbH,hsbS,hsbB;

	if(var_Min == var_Max) {
		hsbH = 0;
	} else if(var_Max == R && G >= B) {
		hsbH = 60 * ( (G - B) / (var_Max - var_Min) );
	} else if(var_Max == R && G < B) {
		hsbH = 60 * ( (G - B) / (var_Max - var_Min) ) + 360;
	} else if(var_Max == G) {
		hsbH = 60 * ( (B - R) / (var_Max - var_Min) ) + 120;
	} else if(var_Max == B) {
		hsbH = 60 * ( (R - G) / (var_Max - var_Min) ) + 240;
	}  

	if(var_Max == 0) {
		hsbS = 0;
	} else {
		hsbS = 1 - (var_Min / var_Max);
	}  

	double var_R = (R / 255);
	double var_G = (G / 255);
	double var_B = (B / 255);  

	hsbB = max(max(var_R, var_G), var_B);
	hsbH = (hsbH >= 360) ? 0 : hsbH;  

	hsb.h = hsbH + 0.5;
	hsb.s = hsbS * 100 + 0.5;
	hsb.b = hsbB * 100 + 0.5;

	return hsb;  

}

 算法描写叙述:

1)依据HSB的概念,我在设计这个动态调色板的绘制算法时,H取值范围为0~360、S和B取值范围0~100。

2)整张图片从左到右的H值从0到360均匀变化。

3)S的初值为0,取S的临界值为总体图片的亮度值(这个值由下方的Bar来确定)。在每一列中,取中间位置为S的临界值所在位置;在上半部分中。S的值均匀增大。下半部分中。S的值一直取临界值

4)B的初值为100。在每一列中,B的值总是趁减小趋势,减小的速度越来越快。原酷狗的B的临界值会依据图片亮度的变化而改变位置。我这里做了简化,让B的临界值位置在中间,而取B的临界值的算法为 100 - (100 - 总体亮度)/ 2。

通过观察得知整张图片分为上下两部分,去其高度为200,所以每部分高度为100。

c++的描写叙述代码为:

//m_nCurB为图片总体的亮度值,由Bar决定
int SValue = m_nCurB;						//取S的临界值为图片总体的亮度
int BValueTop = (100 - SValue)/2;			//取B的临界值为100 - (100 - 总体亮度)/ 2
int BValueBottom = 100 - BValueTop;

for(int i = 0; i < 100; ++i){				//i表示行数,因为总体高度为200,所以一次性绘制上下两部分的两行
	for(int j = 0; j < 360; ++j){			//j表示列数
		int BValue = (100 - i ) * BValueTop / 100 + BValueBottom;
		COLORREF color = GetColorFromHSB(j,(int)((double)(i * SValue / 100)),BValue);
		SetPixel(m_MemDc, m_rcItem.left + j,m_rcItem.top + i,   color); 

		BValue = i * BValueBottom / 100;
		color = GetColorFromHSB(j,SValue,BValue);
		SetPixel(m_MemDc, m_rcItem.left + j,m_rcItem.top + 199 - i,   color); 

	}
}

以上是调色板主界面的绘制算法。还有剩下的Bar的绘制算法,这个比較简单。Bar的总体颜色的H和S值是通过Pallet部分获取的,剩下的就是从左到右指定不同的B值就能够了。详见源代码。

这个算法弄好后,剩下的就是将其封装为duilib的调色板控件,我继承了CControlUI控件,重写了DoInit()、DoEvent()、DoPaint()函数。

为了让控件在用户点击了Pallet或者Bar的一部分后能够动态变化,须要在DoEvent函数中处理WM_LBUTTONDOWN、WM_LBUTTONUP、WM_MOUSEMOVE消息。先设置两个标记变量,表示鼠标是否在Pallet或者Bar中

1)当用户单击控件的界面时,推断鼠标是否在Pallet或者Bar中。假设在,就设置对应的标记变量为真,用于WM_MOUSEMOVE中继续处理。

2)当用户松开鼠标时,设置标记变量都为假。

3)当用户在控件上移动时,推断是否在Pallet或者Bar中,然后处理对应的消息。假设在Pallet中,依据鼠标坐标动态算出鼠标所在位置的颜色值,用来设置bar的颜色。

假设在Bar中。依据鼠标坐标计算整张图片的亮度值。用来设置Pallet的亮度值。

调色板控件的封装:

核心的算法我米描写叙述完了,剩下仅仅是用c++代码描写叙述一下。我将封装控件命名为CPalletUI,为了设置了两个属性。名为palletheight和barheight,用于设置Pallet的高度和Bar的高度。建议将Pallet的高度设置为200的整数,否则终于效果图easy出现失真。Pallet和Bar的宽度为整个控件的宽度。

另外为控件封装了名为GetSelectColor的函数,用来获取用户终于选择的颜色。

给出一个使用控件的xml描写叙述代码:

<VerticalLayout height="230"><!-- 主体 -->
   	<Pallet name="Pallet" width="506" height="220" palletheight="200" barheight="14" padding="7,5,0,0" bkcolor="#FFFFFFFF" />
</VerticalLayout> 

在头文件里我写好了常量来表示控件的类名和接口名。方便使用者编写CreateControl函数。

最后给出我模仿酷狗调色板的效果图:

 总结:

我个人不擅长算法和图像处理。所以终于的控件的代码质量可能不好。假设哪位朋友有更好的算法、资料或、建议或者发现什么错误,请联系我,不胜感激。

在这个控件编写后几天,网友“风之羽翼”在我原代码基础上,使用新的更合理的代码重写了控件,大幅提升了新控件的性能,在此表示感谢。

新控件的博文地址为:《仿酷狗音乐播放器开发日志二十二
动态调色板控件第二版(性能大幅提升附源代码)》
,原控件下载地址就不提供了,大家直接用新的。

     Redrain  2014.8.16

时间: 2024-10-12 04:07:43

仿酷狗音乐播放器开发日志二十一 开发动态调色板控件(附源代码)的相关文章

仿酷狗音乐播放器已开源!

转载请说明原出处,谢谢:http://blog.csdn.net/zhuhongshu/article/details/41037875 距离我发布测试版的Redrain音乐盒(仿酷狗播放器),现在正好刚2个月.博客留言和QQ群里也一直有网友关心这个小项目开源的问题.先感谢网友对这个小项目的支持.之前我一直担心版权问题而没有开源这个项目,因为我只是个在校大学生,不想惹麻烦,希望大家体谅! 关于这个Redrain音乐盒的发布程序的说明和使用方法,见<Redrain仿酷狗音乐播放器开发完毕,发布测试

关于仿酷狗音乐播放器开源:寻求一套音乐播放器素材,让仿酷狗开源

转载请说明原出处,谢谢~~ 距离公布測试版的仿酷狗音乐播放器.已经几个月过去了.期间非常多网友加我QQ来问我开源的问题,我也早有开源意向. 但我也一直没有得到可靠的信息,保证开源后没有不论什么问题. 所以我打算给这个软件全然换一套界面素材.放弃原来的酷狗素材. 这样开源也就没有不论什么问题了,问题是我个人PS水平太烂.仅仅会切切图.如今寻求一套完整的音乐播放器的素材,素材内容要全面,能够用来替代酷狗的素材. 假设有哪位朋友有这种素材,或者有时间能够做出一套素材让我使用,我将感激不尽.素材替换完成

仿酷狗音乐播放器开发日志二十四 选项设置窗体的实现(附328行xml布局源码)

转载请说明原出处,谢谢~~ 花了两天时间把仿酷狗的选项设置窗体做出来了,当然了只是做了外观.现在开学了,写代码的时间减少,所以整个仿酷狗的工程开发速度减慢了.今天把仿酷狗的选项设置窗体的布局代码分享出来,给学习duilib布局的朋友做个demo.现在编写的仿酷狗选项设置窗体和原酷狗的窗体不细看几乎看不出差别,控件的布局位置和原酷狗最多只有几个像素的位置差别. 先来看一下原酷狗的选项设置窗体的其中一个页面: 如果还不太会布局的朋友可以先看我前些日子写的关于duilib布局的博客<duilib各种布

Redrain仿酷狗音乐播放器开发完毕,发布测试程序

转载请说明原出处,谢谢~~ 从暑假到现在中秋刚过,我用duilib开发仿酷狗播放器大概经历了50天.做仿酷狗的意图只是看原酷狗的界面比较漂亮,想做个完整一些的工程来练习一下duilib.今天把写好的程序代码综合一下,发布测试版,现在一共写了10355行c++代码和大约2000行xml代码.测试版的程序肯定会有不少bug,而我自己也没有精心去测试程序,希望发现问题的朋友联系我一下,我去修正.程序在win8下运行会有问题,请在兼容性里调整一下!整个软件的开发过程可以看前面发布的博客! 软件界面上绝大

高仿天天动听音乐播放器,可联网下载歌词_Android源码

Android源码仿天天动听音乐播放器,可联网下载歌词 功能分类:影音 支持平台:Android 运行环境:Eclipse 开发语言:Java 开发工具:Eclipse 源码大小:1.94MB 下载地址:http://www.dwz.cn/wGVt2 源码简介 Android源码仿天天动听音乐播放器,可联网下载歌词. 源码运行截图

Android源码仿天天动听音乐播放器,可联网下载歌词

支持平台:Android      运行环境:Eclipse        开发语言:Java 下载地址:http://www.devstore.cn/code/info/203.html 源码简介 Android源码仿天天动听音乐播放器,可联网下载歌词. 源码运行截图

逗逼音乐播放器制作日志(一)&lt;附源码&gt;

我很喜欢编程,喜欢看到一个项目,从最初的几行代码慢慢变成好几个类,几千行代码.回头看看,这都是一个字母一个词语打出来的.就会觉得很开心... 曾经去报读过专业课程.可惜课程完毕后无缘成为一名码农,心里很是遗憾...现在记着的也不多了,不想就此放弃..于是决定制作一个简单的本地音乐播放器吧...之前已经写了点,网上参考了很多资料.希望各位大牛,多多指点...现在算是完成了3/1吧...我只能算是个半吊子,写不出什么精彩的代码.. 非常可惜的是,我在写的时候没有将Service加入,导致现在只能前台

仿网易云音乐播放器(磁盘转圈、背景虚化等等)

先看效果,CSDN的git传上去总是不动.不知道为什么. 主要思路: 1 . 除了 開始/暂停 .上一首.下一首 这三个icon.你看到的是一个ViewGroup ,这个ViewGroup里面有圆形封面.黑色圈圈磁盘.唱针,高斯模糊背景图 2. 凝视掉了磁盘一起转动的效果,如今的方案不是最好的,建议若是想实现,能够把圆形封面和磁盘合并成一张图(最好在CicicleImageView这里面做).给一个旋转动画.两个动画.两个View,帧的频率不会那么高 3.凝视掉了上一首.下一首切换的时候渐变的动

仿酷狗音乐列表点击item子控件展开功能

也是项目需要大家先来看看效果图 就是这样不过我的项目可能下面没有这么多项而已,说一下思路: 实际上下面展开的哪一块是被隐藏起来了的,可以获取itme的事件点击,接收点击事件展开(显示出来),点击另外的隐藏起来 下面是我的效果图(大家可以自己设置样式,我的比较丑) 好图片有点大,但是看的爽对不!下面贴代码 public class MainActivity extends Activity { private ListView mListView; private ListAdapter mAda