按:编个小程序是为了度过松弛的业余生活,也是为了维持自己有记忆以来的性格特质:就是总得学点什么干点什么。我写这个图片浏览器,目的无非如此。因为有那么多的免费软件,想如何如何使何其难也。下面开始进行美化,第一步是按钮平面化。本文是平面化过程中碰到的一个小问题,此问题解决后再实现按钮的平面化。
按钮风格设计引发的透视计算问题
我的java版的按钮风格是鼠标移到按钮上面之后,按钮图片形成一个倒下的感觉,本质是一个图片的投影。因此,这涉及到如何完成一个图片的投影变化。因此这两天又上网查了一下资料,发现这个东西要做好了,还挺复杂,要首先计算出投影矩阵(透视矩阵)。是一个八个未知数的线性方程组。遥想当年,自己的数值分析差点儿不及格啊。真是汗颜,虽然印象当中还有什么高斯消去法,牛顿什么迭代法,什么龙格库塔法,但是早已忘得找不着北了。不妨先记下,然后在慢慢理会。
以下来自:https://www.cnblogs.com/faith0217/articles/5027490.html
要点如下:
四顶点校正透视变换的线性方程解
透视变换矩阵的形式为:
最终变换为:
ok,解上述方程就可以了。
我准备在我后续的图像编辑软件中将仔细研究上述方法,在本软件中,我将采用一个小trick,解决投影的问题。
我的的变化一个简单的投影,即一个矩形投影为一个等腰梯形,切底和高和矩形的底和高平行,底边不变。
如图示:
变化分成两步,第一步得到原图的压缩高度的图像,即A’BCD‘’,这个变化很简单。第二步,将压缩后的图像经过变换变成中间的投影,即那个梯形。我的方法如下:
按:实际上A"D"的长度是已知的,或者说是可以设定的。这样利用简单的相似三角形概念,可以依次计算出到底边距离H,对应的EF的长度。可以看出,梯形图像就是一系列EF线的积分,白话说法就是,梯形的图像是由高度为1 的一系列图像叠加而成。这样我们就可以近似得到这个梯形图像了。
代码如下
void CImgShape::perspectiveWindow(CImage &src, int tH, int tTopL, CString dir, CImage &dst) {
int src_width = src.GetWidth();
int src_height = src.GetHeight();
CImage img1;
float dstX, dstY, dstW, dstH, srcX, srcY, srcW, srcH;
int tBotL;
if (dir == "U" || dir == "D") {
dstH = tH;
dstW = src_width;
}
else {
dstH = src_height;
dstW = tH;
}
img1.Create(dstW, dstH, 24);
if (!dst.IsNull()) {
dst.Destroy();
}
dst.Create(img1.GetWidth(), img1.GetHeight(), 24);
CDC* pSrc0DC = CDC::FromHandle(src.GetDC());
CDC* pSrc1DC = CDC::FromHandle(img1.GetDC());
CDC* pDstDC = CDC::FromHandle(dst.GetDC());
int ModeOld = SetStretchBltMode(pSrc1DC->m_hDC, COLORONCOLOR);//设置指定设备环境中的位图拉伸模式, looks like STRETCH_HALFTONE mode, 0.16 ms or 0.00 ms
//1# get imag1
pSrc1DC->StretchBlt(0,0, dstW, dstH, pSrc0DC, 0,0, src_width, src_height, SRCCOPY);
SetStretchBltMode(pSrc1DC->m_hDC, ModeOld);
//1# get the dist image
ModeOld = SetStretchBltMode(pDstDC->m_hDC, COLORONCOLOR);//设置指定设备环境中的位图拉伸模式, looks like STRETCH_HALFTONE mode, 0.16 ms or 0.00 ms
if (dir == "U" || dir == "D") {
srcH = 1;
dstH = 1;
srcX = 0;
srcW = src_width;
tBotL = src_width;
}
else {
srcW = 1;
dstW = 1;
srcY = 0;
srcH = src_height;
tBotL = src_height;
}
int dL;// the difference of the two parallel edge of the trapezoid
int L;
for (int h = 0; h < tH;h++)
{
if (dir == "U" ) {
srcY = h;
dstY = srcY;
dL = (tBotL - tTopL) / 2.0 / tH * h;
L = tBotL - dL * 2;
dstW = L;
dstX = dL;
}else if(dir == "D"){
srcY = tH - h;
dstY = srcY;
dL = (tBotL - tTopL) / 2.0 / tH * h;
L = tBotL - dL * 2;
dstW = L;
dstX = dL;
}else if (dir == "L") {
srcX = h;
dstX = srcX;
dL = (tBotL - tTopL) / 2.0 / tH * h;
L = tBotL - dL * 2;
dstH = L;
dstY = dL;
}
else if (dir == "R") {
srcX = tH - h;
dstX = srcX;
dL = (tBotL - tTopL) / 2.0 / tH * h;
L = tBotL - dL * 2;
dstH = L;
dstY = dL;
}
else {
throw "Bad parameters...";
}
pDstDC->StretchBlt(dstX, dstY, dstW,dstH, pSrc1DC, srcX, srcY, srcW, srcH, SRCCOPY);
}
SetStretchBltMode(pDstDC->m_hDC, ModeOld);
src.ReleaseDC();
img1.ReleaseDC();
dst.ReleaseDC();
return ;
}
调用代码如下:
perspectiveWindow(m_myImg, GetHeight()/1.5,myImg.GetWidth()/2,L"U",dest);
参数说明:
CImage &src:源图像
int tH:变形后梯形的高
int tTopL: 变形后梯形的顶边长度
CString dir: 变化的方向UDLR, 四选一
CImage &dst: 变化后图像
因为支持四种方向的变化,因此程序用了一点小技巧。读者如果有问题可以提问,不再详述。
2020-04-07