在前面的文章中,我们已经介绍了如何使用OpenGL在窗口中渲染中文文字。将这些文字放到窗口内指定的位置,便是GUI中常见的“文字标签”。
在本篇文章中,我们将进一步介绍如何用OpenGL来实现另外一种常见GUI控件“按钮”。
“基础按钮”控件的实现
要实现一个“基础按钮”控件非常简单,在想要放置按钮的位置画出一个矩形,当鼠标放置到该矩形上并做出点击操作时改变其绘图(比如把矩形缩小),并在后台运行相应的单击响应代码即可。具体的步骤如下:
判断鼠标左键按下的位置是否在按钮所在的位置
判断函数
bool OnMouseDown(int mousex, int mousey)
{
if (mousex > m_fPosX && mousex < m_fPosX + m_fWidth &&
mousey > m_fPosY && mousey < m_fPosY + m_fHeight)
{
m_bPressed = true;
return true;
}
return false;
}
调用函数
//左键按下:
if (pBtn->OnMouseDown(x, y))
std::cout << "Mouse pos---x:" << x << " y:" << y << std::endl;//做出相应的反应
根据按钮的状态渲染矩形
void Render()
{
glMatrixMode(GL_PROJECTION);//将当前矩阵指定为投影矩阵,以便进行投影操作
glLoadIdentity();//将操作矩阵设为单位矩阵
//设置一个平行投影控件,如果不设置,OpenGL的窗口被定义为横向-1->1,纵向-1->1的范围,不直观
//设置按照窗体大小设置之后,则可以按照正常坐标设置对象位置
glOrtho(0, g_fWidth, 0, g_fHeight, 0, 100);
glColor3f(1.0f, 0.0f, 0.0f); //设置绘图颜色
if (m_bPressed)//当鼠标按下时
{
//缩小绘制范围以显示区别
float scalefx = m_fWidth * 0.03;
float scalefy = m_fHeight * 0.03;
glRectf(m_fPosX + scalefx, (g_fHeight -m_fPosY) - m_fHeight + scalefy,
m_fPosX + m_fWidth - scalefx * 2, (g_fHeight - m_fPosY) - scalefy * 2); //绘制矩形
}
else//鼠标没有按下
{
glRectf(m_fPosX, (g_fHeight - m_fPosY)- m_fHeight, m_fPosX + m_fWidth, (g_fHeight - m_fPosY)); //绘制矩形
}
}
这样就实现了一个简单的按钮。完整的代码如下:
glut_Button.hpp
#pragma once
class glut_button
{
private:
UCHAR r, g, b;
public:
float m_fPosX; //表示在正交投影坐标系(左下角为坐标原点)的坐标,
float m_fPosY;
float m_fWidth; //屏幕像素单位
float m_fHeight;
float g_fWidth; //父窗体宽度
float g_fHeight; //父窗体高度
bool m_bPressed;
//按钮构造函数
glut_button(float PosX, float PosY, float Width, float Height)
{
m_bPressed = false;
m_fPosX = PosX;
m_fPosY = PosY;
m_fWidth = Width;
m_fHeight = Height;
r = 0xff;
g = 0x00;
b = 0x00;
}
//重设父窗口大小
void setfatherWinSize(int w, int h)
{
g_fWidth = w;
g_fHeight = h;
}
//重设父窗口大小
void setcolor(UCHAR set_r, UCHAR set_g, UCHAR set_b)
{
r = set_r;
g = set_g;
b = set_b;
}
//按钮渲染
void Render()
{
glMatrixMode(GL_PROJECTION);//将当前矩阵指定为投影矩阵,以便进行投影操作
glLoadIdentity();//将操作矩阵设为单位矩阵
//设置一个平行投影控件,如果不设置,OpenGL的窗口被定义为横向-1->1,纵向-1->1的范围,不直观
//设置按照窗体大小设置之后,则可以按照正常坐标设置对象位置
glOrtho(0, g_fWidth, 0, g_fHeight, 0, 100);
glColor3f(r/255.0, g / 255.0, b / 255.0); //设置绘图颜色
if (m_bPressed)//当鼠标按下时
{
//缩小绘制范围以显示区别
float scalefx = m_fWidth * 0.03;
float scalefy = m_fHeight * 0.03;
glRectf(m_fPosX + scalefx, (g_fHeight -m_fPosY) - m_fHeight + scalefy,
m_fPosX + m_fWidth - scalefx * 2, (g_fHeight - m_fPosY) - scalefy * 2); //绘制矩形
}
else//鼠标没有按下
{
glRectf(m_fPosX, (g_fHeight - m_fPosY)- m_fHeight, m_fPosX + m_fWidth, (g_fHeight - m_fPosY)); //绘制矩形
}
}
//按钮被按下
bool OnMouseDown(int mousex, int mousey)
{
if (mousex > m_fPosX && mousex < m_fPosX + m_fWidth &&
mousey > m_fPosY && mousey < m_fPosY + m_fHeight)
{
m_bPressed = true;
return true;
}
return false;
}
//按钮松开
void OnMouseUp()
{
m_bPressed = false;
}
};
使用“基础按钮”
#include <iostream>
#include <GL/freeglut.h>
#include "glut_Button.hpp"//按钮对象
glut_button* pBtn;
void init(void)
{
glShadeModel(GL_SMOOTH);
pBtn = new glut_button(0, 0, 60, 20);//按钮的位置以及宽高
pBtn->setcolor(0x54,0x48,0x6E);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);//清空颜色缓冲池
pBtn->Render();
//填充缓冲区表面
glFlush();
//缓冲区翻转显示图像
glutSwapBuffers();
}
void mouse(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON)
switch (state)
{
case GLUT_DOWN:
//左键按下:
if (pBtn->OnMouseDown(x, y))
std::cout << "Mouse pos---x:" << x << " y:" << y << std::endl;
break;
case GLUT_UP:
pBtn->OnMouseUp();
break;
}
glutPostRedisplay();
}
void changeSize(int w, int h)
{
glViewport(0, 0, w, h);//设定视口的大小
pBtn->setfatherWinSize(w, h);//通知字体渲染对象窗口大小改变了
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);//设置绘图模式
glutInitWindowSize(500, 500);//设置窗口大小
glutInitWindowPosition(100, 100);//设置窗口弹出位置
glutCreateWindow("按钮Demo");//创建窗口
init();//GUI初始化
glutDisplayFunc(display);//定义渲染回调函数
glutMouseFunc(mouse);//定义鼠标响应函数
glutReshapeFunc(changeSize);//注册窗口大小改变响应函数
glutMainLoop();//进入消息循环
return 0;
}
效果如下:
“文字按钮”控件的实现
我们常用的一类按钮为“文字按钮”,也就是在前面“普通按钮”的基础上加上文字的说明。结合我们上一篇文章的工作,实现“文字按钮”其实就是在按钮渲染的时候把文字也渲染出来。
具体的实现如下:
glut_Font.hpp
#pragma once
#include <Windows.h>
#include <gl/GL.h>
#include <gl/GLU.h>
#include <map>
#include <math.h>
class CFontData
{
public:
float m_Width, m_Height;
float m_OrigX, m_OrigY;
float m_FontWidth;
GLuint m_TextureID;
CFontData()
{
m_Width = 0.0f;
m_Height = 0.0f;
m_TextureID = 0;
m_FontWidth = 0.0f;
m_OrigX = 0.0f;
m_OrigY = 0.0f;
}
};
class CFontPrinter
{
private:
int m_FontSize;//字体大小
std::wstring m_FontName;//字体名称
std::map<wchar_t, CFontData*> m_FontMap;//文字映射表
HFONT m_Font;//字体对象
//字体颜色
unsigned char red;
unsigned char green;
unsigned char blue;
//背景颜色
unsigned char bg_r = 0;
unsigned char bg_g = 0;
unsigned char bg_b = 255;
//父窗口大小
float fwinwidth;
float fwinheight;
public:
CFontPrinter(int fontSize, std::wstring fontName, int fwwidth, int fwheight)
{
m_FontName = fontName;
m_FontSize = fontSize;//获得字体大小
m_Font = NULL;
red = 255;
green = 255;
blue = 255;
fwinwidth = (float)fwwidth;
fwinheight = (float)fwheight;
}
bool makeChar(wchar_t wChar)//生成单个文字对象,并放入映射表中
{
HDC hdc = CreateCompatibleDC(wglGetCurrentDC());
if (!hdc)
return false;
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, 1, 1);
HBITMAP hbitmapOld = (HBITMAP)SelectObject(hdc, hbitmap);
if ((DWORD)hbitmapOld == GDI_ERROR)
return false;
//CFontData* fontData = new CFontData();
m_FontMap[wChar] = new CFontData();
glGenTextures(1, &m_FontMap[wChar]->m_TextureID);//生成纹理,放入m_TextureID中
glBindTexture(GL_TEXTURE_2D, m_FontMap[wChar]->m_TextureID);//绑定生成的纹理
//设置纹理的拉伸收缩方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
//指定OpenGL如何从数据缓冲区中解包图像数据
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
int iTexWidth = m_FontSize;
int iTexHeight = m_FontSize;
int iLevel = 0;
if (!m_Font)//创建字体
{
m_Font = CreateFontW(-m_FontSize, 0, 0, 0, 500, false, false, false,
DEFAULT_CHARSET, OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | (m_FontName.c_str() ? FF_DONTCARE : FF_SWISS), m_FontName.c_str());
if (!m_Font)//检查创建结果
return false;
}
HFONT hfontOld = (HFONT)SelectObject(hdc, m_Font);
if ((DWORD)hfontOld == GDI_ERROR)
return false;
GLYPHMETRICS gm = { 0, };//存储字符相关信息
MAT2 const matrix22_identity = { {0,1},{0,0},{0,0},{0,1} };
BYTE* pBuff = new BYTE[m_FontSize * m_FontSize];
BYTE* pSwapBuff = new BYTE[m_FontSize * m_FontSize * 3];
memset(pBuff, 0xff, m_FontSize * m_FontSize);
memset(pSwapBuff, 0xff, m_FontSize * m_FontSize * 3);
DWORD dwBuffSize = GetGlyphOutlineW(hdc, wChar, GGO_GRAY8_BITMAP, &gm, m_FontSize * m_FontSize, pBuff, &matrix22_identity);
if (dwBuffSize == GDI_ERROR)
{
delete[] pSwapBuff;
delete[] pBuff;
return false;
}
// 原本字体的灰度值为0~64,经过调整映射到0~255
unsigned int const uiRowSize = dwBuffSize / gm.gmBlackBoxY;
BYTE* pPtr;
BYTE* pSPtr;
for (unsigned int nY = 0; nY < gm.gmBlackBoxY; nY++)
{
pPtr = pBuff + uiRowSize * nY;
pSPtr = pSwapBuff + gm.gmBlackBoxX * 3 * nY;
for (unsigned int nX = 0; nX < gm.gmBlackBoxX; nX++)
{
if (*pPtr == 0)//背景
{
*pSPtr = bg_r;
pSPtr++;
*pSPtr = bg_g;
pSPtr++;
*pSPtr = bg_b;
pSPtr++;
}
else if (*pPtr == 64)
{
*pSPtr = red;
pSPtr++;
*pSPtr = green;
pSPtr++;
*pSPtr = blue;
pSPtr++;
}
else
{
float time = (*pPtr) / (float)64;
int blend_r = unsigned char(red * time) + unsigned char(bg_r * (1 - time));
if (blend_r > 255)
{
blend_r = 255;
}
*pSPtr = blend_r;
pSPtr++;
int blend_g = unsigned char(green * time) + unsigned char(bg_g * (1 - time));
if (blend_g > 255)
{
blend_g = 255;
}
*pSPtr = blend_g;
pSPtr++;
int blend_b = unsigned char(blue * time) + unsigned char(bg_b * (1 - time));
if (blend_b > 255)
{
blend_b = 255;
}
*pSPtr = blend_b;
pSPtr++;
}
pPtr++;
}
//std::cout << std::endl;
}
// 设置纹理,将字体装入其中
// iLevel表示图像级别,GL_LUMINANCE表示亮度
glTexImage2D(GL_TEXTURE_2D, iLevel, GL_RGB, gm.gmBlackBoxX, gm.gmBlackBoxY, 0, GL_RGB, GL_UNSIGNED_BYTE, pSwapBuff);
/*glTexImage2D(GL_TEXTURE_2D, iLevel, GL_LUMINANCE8, gm.gmBlackBoxX, gm.gmBlackBoxY, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pSwapBuff);*/
// 将字符对象加入到映射地图中,这样就不需要反复创建
// 字体所占宽度比例,比如设置9而所占96则最后得到的就是1
m_FontMap[wChar]->m_FontWidth = (float)gm.gmCellIncX / (float)m_FontSize;
// 字体实际宽度比例,一般获得的字体宽度要比我们设定的窄
m_FontMap[wChar]->m_Width = (float)gm.gmBlackBoxX / (float)m_FontSize;
// 字体实际高度比例
m_FontMap[wChar]->m_Height = (float)gm.gmBlackBoxY / (float)m_FontSize;
// 字体实际左边x坐标与设定字尺寸的比例,因为字实际高度比设定的要窄,因此这个起点就定义了实际上字是从哪个坐标上开始的,且左右等宽
m_FontMap[wChar]->m_OrigX = (float)gm.gmptGlyphOrigin.x / (float)m_FontSize;
// 字体实际上边y坐标与设定字尺寸的比例,与x不同,字符在纵向上结束的位置为最尾部的位置,即从开始位置到结束,因此算其居中间隙的话需要除以2
m_FontMap[wChar]->m_OrigY = (1.0f - (float)gm.gmptGlyphOrigin.y / (float)m_FontSize)/2;
//m_FontMap[wChar] = fontData;
//释放对象
DeleteObject(hbitmapOld);
DeleteObject(hbitmap);
DeleteDC(hdc);
delete[] pBuff;
delete[] pSwapBuff;
return true;
}
void setFontColor(unsigned char r, unsigned char g, unsigned char b)//设置文字颜色
{
red = r;
green = g;
blue = b;
}
void setBGColor(unsigned char r, unsigned char g, unsigned char b)//设置背景颜色
{
bg_r = r;
bg_g = g;
bg_b = b;
}
void setfatherWinSize(int w, int h)//设置父窗口大小
{
fwinwidth = (float)w;
fwinheight = (float)h;
}
void printTextMid(float x, float y, float width, float height, const wchar_t* Text)//上下、左右居中绘制文字。x和y为文本框的左上角,width和height为文本框的大小
{
glColor3f(0xff / (float)255, 0xff / (float)255, 0xff / (float)255);//设置颜色空间为白色,否则会影响绘图
size_t iTextLenth = wcslen(Text);
//计算空格的个数
wchar_t space[] = L" ";//空格处理
int spacenum = 0;
for (size_t i = 0; i < iTextLenth; i++)
{
if (Text[i] == space[0])//处理空格
{
spacenum++;
}
}
//启用2D纹理
glEnable(GL_TEXTURE_2D);
//居中绘制文字
float fX = x + width / 2 - (spacenum + (iTextLenth - spacenum) * 2) * m_FontSize / 2 / 2;
//float fY = fwinheight - y - height / 2 + m_FontSize/2;
float fY = y - height / 2 + m_FontSize / 2;
float fZ = 0;
for (size_t i = 0; i < iTextLenth; i++)
{
if (m_FontMap[Text[i]] == NULL && Text[i] != space[0])
{
makeChar(Text[i]);
}
// 绘制字体
CFontData* fontData = m_FontMap[Text[i]];
if (fontData)
{
glMatrixMode(GL_PROJECTION);//将当前矩阵指定为投影矩阵,以便进行投影操作
glLoadIdentity();//将操作矩阵设为单位矩阵
glOrtho(0, fwinwidth, 0, fwinheight, 0, 100);
float iWidth = fontData->m_Width * m_FontSize;
float iOrigX = fontData->m_OrigX * m_FontSize;
float iHeight = fontData->m_Height * m_FontSize;
float iOrigY = fontData->m_OrigY * m_FontSize;
glBindTexture(GL_TEXTURE_2D, fontData->m_TextureID);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(fX + iOrigX, fY - iOrigY - iHeight, fZ);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(fX + iOrigX, fY - iOrigY, fZ);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(fX + iOrigX + iWidth, fY - iOrigY, fZ);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(fX + iOrigX + iWidth, fY - iOrigY - iHeight, fZ);
glEnd();
fX += fontData->m_FontWidth * m_FontSize;
glLoadIdentity();// 重置当前的模型观察矩阵,消除投影矩阵调用带来的影响
}
else if (Text[i] == space[0])//处理空格
{
fX += m_FontSize / 2;
}
}
glDisable(GL_TEXTURE_2D);
}
void printTextLeft(float x, float y, const wchar_t* Text)//左对齐绘制文字
{
glColor3f(0xff / (float)255, 0xff / (float)255, 0xff / (float)255);//设置颜色空间为白色,否则会影响绘图
size_t iTextLenth = wcslen(Text);
//计算空格的个数
wchar_t space[] = L" ";//空格处理
int spacenum = 0;
for (size_t i = 0; i < iTextLenth; i++)
{
if (Text[i] == space[0])//处理空格
{
spacenum++;
}
}
//启用2D纹理
glEnable(GL_TEXTURE_2D);
//居中绘制文字
float fX = x;
float fY = fwinheight - y;
float fZ = 0;
for (size_t i = 0; i < iTextLenth; i++)
{
if (m_FontMap[Text[i]] == NULL && Text[i] != space[0])
{
makeChar(Text[i]);
}
// 绘制字体
CFontData* fontData = m_FontMap[Text[i]];
if (fontData)
{
glMatrixMode(GL_PROJECTION);//将当前矩阵指定为投影矩阵,以便进行投影操作
glLoadIdentity();//将操作矩阵设为单位矩阵
glOrtho(0, fwinwidth, 0, fwinheight, 0, 100);
float iWidth = fontData->m_Width * m_FontSize;
float iOrigX = fontData->m_OrigX * m_FontSize;
float iHeight = fontData->m_Height * m_FontSize;
float iOrigY = fontData->m_OrigY * m_FontSize;
glBindTexture(GL_TEXTURE_2D, fontData->m_TextureID);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(fX + iOrigX, fY - iOrigY - iHeight, fZ);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(fX + iOrigX, fY - iOrigY, fZ);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(fX + iOrigX + iWidth, fY - iOrigY, fZ);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(fX + iOrigX + iWidth, fY - iOrigY - iHeight, fZ);
glEnd();
fX += fontData->m_FontWidth * m_FontSize;
glLoadIdentity();// 重置当前的模型观察矩阵,消除投影矩阵调用带来的影响
}
else if (Text[i] == space[0])//处理空格
{
fX += m_FontSize / 2;
}
}
glDisable(GL_TEXTURE_2D);
}
};
glut_Button.hpp
#pragma once
class glut_button
{
private:
//背景对象
UCHAR r, g, b;
//文字对象
CFontPrinter* font = NULL;
int mfontsize = 0;
CFontPrinter* fontmini = NULL;
int mfontminisize = 0;
UCHAR font_r = 0;
UCHAR font_g = 0;
UCHAR font_b = 0;
std::wstring fonttext;
bool drawFont;
public:
float m_fPosX; //表示在正交投影坐标系(左下角为坐标原点)的坐标,
float m_fPosY;
float m_fWidth; //屏幕像素单位
float m_fHeight;
float g_fWidth; //父窗体宽度
float g_fHeight; //父窗体高度
bool m_bPressed;
//按钮构造函数
glut_button(float PosX, float PosY, float Width, float Height, float winWidth, float winHeight)
{
m_bPressed = false;
m_fPosX = PosX;
m_fPosY = PosY;
m_fWidth = Width;
m_fHeight = Height;
g_fWidth = winWidth;
g_fHeight = winHeight;
r = 0xff;
g = 0x00;
b = 0x00;
drawFont = false;//默认不渲染文字
}
//按钮析构函数
~glut_button()
{
if (drawFont)
{
delete font;
delete fontmini;
}
}
//重设父窗口大小
void setfatherWinSize(int w, int h)
{
g_fWidth = w;
g_fHeight = h;
font->setfatherWinSize(w, h);
fontmini->setfatherWinSize(w, h);
}
//重设父窗口大小
void setcolor(UCHAR set_r, UCHAR set_g, UCHAR set_b)
{
r = set_r;
g = set_g;
b = set_b;
}
//文字设置
void setfont(int fontsize, std::wstring fontname, std::wstring setfonttext)
{
//文字对象创建
mfontsize = fontsize;
mfontminisize = fontsize * 0.95;
font = new CFontPrinter(mfontsize, fontname.c_str(), g_fWidth, g_fHeight);//创建字体对象
fontmini = new CFontPrinter(mfontminisize, fontname.c_str(), g_fWidth, g_fHeight);//创建字体对象
drawFont = true;
font->setBGColor(r, g, b);//设置背景色
fontmini->setBGColor(r, g, b);//设置背景色
font->setFontColor(255, 0, 0);
fontmini->setFontColor(255, 0, 0);
fonttext = setfonttext;
}
//按钮渲染
void Render()
{
glMatrixMode(GL_PROJECTION);//将当前矩阵指定为投影矩阵,以便进行投影操作
glLoadIdentity();//将操作矩阵设为单位矩阵
//设置一个平行投影控件,如果不设置,OpenGL的窗口被定义为横向-1->1,纵向-1->1的范围,不直观
//设置按照窗体大小设置之后,则可以按照正常坐标设置对象位置
glOrtho(0, g_fWidth, 0, g_fHeight, 0, 100);
glColor3f(r / 255.0, g / 255.0, b / 255.0); //设置绘图颜色
if (m_bPressed)//当鼠标按下时
{
//缩小绘制范围以显示区别
float scalefx = m_fWidth * 0.01;
float scalefy = m_fHeight * 0.01;
glRectf(m_fPosX + scalefx, (g_fHeight - m_fPosY) - m_fHeight + scalefy,
m_fPosX + m_fWidth - scalefx * 2, (g_fHeight - m_fPosY) - scalefy * 2); //绘制矩形
if (drawFont)
{
int pos_x = m_fPosX + scalefx;
int pos_y = m_fPosY - mfontminisize / 2 - mfontminisize * 0.1;
fontmini->printTextMid(pos_x, (g_fHeight - m_fPosY) - scalefy * 2, m_fWidth - scalefx * 2, m_fHeight - scalefy * 2, fonttext.c_str());
}
}
else//鼠标没有按下
{
glRectf(m_fPosX, (g_fHeight - m_fPosY) - m_fHeight, m_fPosX + m_fWidth, (g_fHeight - m_fPosY)); //绘制矩形
glLoadIdentity();// 重置当前的模型观察矩阵,消除投影矩阵调用带来的影响
if (drawFont)
{
font->printTextMid(m_fPosX, g_fHeight - m_fPosY, m_fWidth, m_fHeight, fonttext.c_str());
}
}
}
//按钮被按下
bool OnMouseDown(int mousex, int mousey)
{
if (mousex > m_fPosX && mousex < m_fPosX + m_fWidth &&
mousey > m_fPosY && mousey < m_fPosY + m_fHeight)
{
m_bPressed = true;
return true;
}
return false;
}
//按钮松开
void OnMouseUp()
{
m_bPressed = false;
}
};
使用“文字按钮”
#include <iostream>
#include <GL/freeglut.h>
#include "Font.h"//按钮对象
#include "glut_button.hpp"//按钮对象
glut_button* pBtn;
void init(void)
{
glShadeModel(GL_SMOOTH);
pBtn = new glut_button(100, 0, 220, 80, 500, 500);//按钮的位置以及宽高
pBtn->setcolor(0xff,0xc8,0x7a);//设置按钮背景颜色
pBtn->setfont(40, "隶书", L"力拔山兮");//设置按钮文字大小、字体和内容
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);//清空颜色缓冲池
pBtn->Render();
//填充缓冲区表面
glFlush();
//缓冲区翻转显示图像
glutSwapBuffers();
}
void mouse(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON)
switch (state)
{
case GLUT_DOWN:
//左键按下:
if (pBtn->OnMouseDown(x, y))
std::cout << "Mouse pos---x:" << x << " y:" << y << std::endl;
break;
case GLUT_UP:
pBtn->OnMouseUp();
break;
}
glutPostRedisplay();
}
void changeSize(int w, int h)
{
glViewport(0, 0, w, h);//设定视口的大小
pBtn->setfatherWinSize(w, h);//通知字体渲染对象窗口大小改变了
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);//设置绘图模式
glutInitWindowSize(500, 500);//设置窗口大小
glutInitWindowPosition(100, 100);//设置窗口弹出位置
glutCreateWindow("按钮Demo");//创建窗口
init();//GUI初始化
glutDisplayFunc(display);//定义渲染回调函数
glutMouseFunc(mouse);//定义鼠标响应函数
glutReshapeFunc(changeSize);//注册窗口大小改变响应函数
glutMainLoop();//进入消息循环
return 0;
}
效果如下:
“贴图按钮”控件的实现
大多数不规则的按钮的本质其实就是在一个矩形框中贴一张带有透明度的图片,在上一篇博客中我们已经介绍了如何用freeglut绘制带透明度的图片,因此实现贴图按钮非常简单这里就不再单独赘述。实现代码如下:
glut_button.hpp
#pragma once
class glut_button
{
private:
//背景对象
UCHAR r, g, b;
//文字对象
CFontPrinter* font = NULL;
int mfontsize = 0;
CFontPrinter* fontmini = NULL;
int mfontminisize = 0;
UCHAR font_r = 0;
UCHAR font_g = 0;
UCHAR font_b = 0;
std::wstring fonttext;
bool drawFont;
cv::Mat imgbgra;//贴图对象
public:
float m_fPosX; //表示在正交投影坐标系(左下角为坐标原点)的坐标,
float m_fPosY;
float m_fWidth; //屏幕像素单位
float m_fHeight;
float g_fWidth; //父窗体宽度
float g_fHeight; //父窗体高度
bool m_bPressed;
//按钮构造函数
glut_button(float PosX, float PosY, float Width, float Height, float winWidth, float winHeight)
{
m_bPressed = false;
m_fPosX = PosX;
m_fPosY = PosY;
m_fWidth = Width;
m_fHeight = Height;
g_fWidth = winWidth;
g_fHeight = winHeight;
r = 0xff;
g = 0x00;
b = 0x00;
drawFont = false;//默认不渲染文字
}
//按钮析构函数
~glut_button()
{
if (drawFont)
{
delete font;
delete fontmini;
}
}
//重设父窗口大小
void setfatherWinSize(int w, int h)
{
g_fWidth = w;
g_fHeight = h;
font->setfatherWinSize(w, h);
fontmini->setfatherWinSize(w, h);
}
//重设父窗口大小
void setcolor(UCHAR set_r, UCHAR set_g, UCHAR set_b)
{
r = set_r;
g = set_g;
b = set_b;
}
//文字设置
void setfont(int fontsize, std::wstring fontname, std::wstring setfonttext)
{
//文字对象创建
mfontsize = fontsize;
mfontminisize = fontsize * 0.95;
font = new CFontPrinter(mfontsize, fontname.c_str(), g_fWidth, g_fHeight);//创建字体对象
fontmini = new CFontPrinter(mfontminisize, fontname.c_str(), g_fWidth, g_fHeight);//创建字体对象
drawFont = true;
font->setBGColor(r, g, b);//设置背景色
fontmini->setBGColor(r, g, b);//设置背景色
font->setFontColor(255, 0, 0);
fontmini->setFontColor(255, 0, 0);
fonttext = setfonttext;
}
//图片设置
void setimg(std::string imgpath)
{
imgbgra = cv::imread(imgpath.c_str(), cv::IMREAD_UNCHANGED);
//判断读入数据格式,如果不带透明色则重新读入
int channels = imgbgra.channels();
if (channels != 4)
{
cv::Mat img = cv::imread(imgpath);
cv::cvtColor(img, imgbgra, cv::COLOR_BGR2RGBA);
}
//根据图像数据生成一个2D纹理
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imgbgra.cols, imgbgra.rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, imgbgra.data);
//设置纹理参数,放大和缩小采取的插值方式为线性插值
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
//图片按钮渲染
void imgRander()
{
glMatrixMode(GL_PROJECTION);//将当前矩阵指定为投影矩阵,以便进行投影操作
glLoadIdentity();//将操作矩阵设为单位矩阵
//设置一个平行投影控件,如果不设置,OpenGL的窗口被定义为横向-1->1,纵向-1->1的范围,不直观
//设置按照窗体大小设置之后,则可以按照正常坐标设置对象位置
glOrtho(0, g_fWidth, 0, g_fHeight, 0, 100);
//启用混合
glEnable(GL_BLEND);
//计算透明色
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//启用一个2D纹理
glEnable(GL_TEXTURE_2D);
//将纹理映射到四边形上
glBegin(GL_QUADS);
if (m_bPressed)//当鼠标按下时
{
//缩小绘制范围以显示区别
float scalefx = m_fWidth * 0.01;
float scalefy = m_fHeight * 0.01;
//纹理的坐标和四边形顶点的对应,可以通过设置四边形的位置调整图像在窗体的位置
glTexCoord2f(0.0, 0.0); glVertex2f(m_fPosX + scalefx, g_fHeight - m_fPosY - scalefy);
glTexCoord2f(0.0, 1.0); glVertex2f(m_fPosX + scalefx, g_fHeight - m_fPosY - m_fHeight + scalefy);
glTexCoord2f(1.0, 1.0); glVertex2f(m_fPosX - scalefx + m_fWidth, g_fHeight - m_fPosY - m_fHeight + scalefy);
glTexCoord2f(1.0, 0.0); glVertex2f(m_fPosX - scalefx + m_fWidth, g_fHeight - m_fPosY - scalefy);
}
else//鼠标没有按下
{
//纹理的坐标和四边形顶点的对应,可以通过设置四边形的位置调整图像在窗体的位置
glTexCoord2f(0.0, 0.0); glVertex2f(m_fPosX, g_fHeight - m_fPosY);
glTexCoord2f(0.0, 1.0); glVertex2f(m_fPosX, g_fHeight - m_fPosY - m_fHeight);
glTexCoord2f(1.0, 1.0); glVertex2f(m_fPosX + m_fWidth, g_fHeight - m_fPosY - m_fHeight);
glTexCoord2f(1.0, 0.0); glVertex2f(m_fPosX + m_fWidth, g_fHeight - m_fPosY);
}
glEnd();
//关闭融合模式和2D纹理
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}
//按钮渲染
void Render()
{
glMatrixMode(GL_PROJECTION);//将当前矩阵指定为投影矩阵,以便进行投影操作
glLoadIdentity();//将操作矩阵设为单位矩阵
//设置一个平行投影控件,如果不设置,OpenGL的窗口被定义为横向-1->1,纵向-1->1的范围,不直观
//设置按照窗体大小设置之后,则可以按照正常坐标设置对象位置
glOrtho(0, g_fWidth, 0, g_fHeight, 0, 100);
glColor3f(r / 255.0, g / 255.0, b / 255.0); //设置绘图颜色
if (m_bPressed)//当鼠标按下时
{
//缩小绘制范围以显示区别
float scalefx = m_fWidth * 0.01;
float scalefy = m_fHeight * 0.01;
glRectf(m_fPosX + scalefx, (g_fHeight - m_fPosY) - m_fHeight + scalefy,
m_fPosX + m_fWidth - scalefx * 2, (g_fHeight - m_fPosY) - scalefy * 2); //绘制矩形
if (drawFont)
{
int pos_x = m_fPosX + scalefx;
int pos_y = m_fPosY - mfontminisize / 2 - mfontminisize * 0.1;
fontmini->printTextMid(pos_x, (g_fHeight - m_fPosY) - scalefy * 2, m_fWidth - scalefx * 2, m_fHeight - scalefy * 2, fonttext.c_str());
}
}
else//鼠标没有按下
{
glRectf(m_fPosX, (g_fHeight - m_fPosY) - m_fHeight, m_fPosX + m_fWidth, (g_fHeight - m_fPosY)); //绘制矩形
glLoadIdentity();// 重置当前的模型观察矩阵,消除投影矩阵调用带来的影响
if (drawFont)
{
font->printTextMid(m_fPosX, g_fHeight - m_fPosY, m_fWidth, m_fHeight, fonttext.c_str());
}
}
}
//按钮被按下
bool OnMouseDown(int mousex, int mousey)
{
if (mousex > m_fPosX && mousex < m_fPosX + m_fWidth &&
mousey > m_fPosY && mousey < m_fPosY + m_fHeight)
{
m_bPressed = true;
return true;
}
return false;
}
//按钮松开
void OnMouseUp()
{
m_bPressed = false;
}
};
使用“图片按钮”
#include <iostream>
#include <GL/freeglut.h>
#include <opencv2/opencv.hpp>
#include "Font.h"//按钮对象
#include "glut_button.hpp"//按钮对象
glut_button* pBtn;
void init(void)
{
glShadeModel(GL_SMOOTH);
pBtn = new glut_button(100, 0, 220, 80, 500, 500);//按钮的位置以及宽高
pBtn->setcolor(0xff, 0xc8, 0x7a);//设置按钮背景颜色
pBtn->setfont(40, "隶书", L"力拔山兮");//设置按钮文字大小、字体和内容
pBtn->setimg("1.jpeg");//设置贴图图片
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);//清空颜色缓冲池
//pBtn->Render();
pBtn->imgRander();
//填充缓冲区表面
glFlush();
//缓冲区翻转显示图像
glutSwapBuffers();
}
void mouse(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON)
switch (state)
{
case GLUT_DOWN:
//左键按下:
if (pBtn->OnMouseDown(x, y))
std::cout << "Mouse pos---x:" << x << " y:" << y << std::endl;
break;
case GLUT_UP:
pBtn->OnMouseUp();
break;
}
glutPostRedisplay();
}
void changeSize(int w, int h)
{
glViewport(0, 0, w, h);//设定视口的大小
pBtn->setfatherWinSize(w, h);//通知字体渲染对象窗口大小改变了
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);//设置绘图模式
glutInitWindowSize(500, 500);//设置窗口大小
glutInitWindowPosition(100, 100);//设置窗口弹出位置
glutCreateWindow("按钮Demo");//创建窗口
init();//GUI初始化
glutDisplayFunc(display);//定义渲染回调函数
glutMouseFunc(mouse);//定义鼠标响应函数
glutReshapeFunc(changeSize);//注册窗口大小改变响应函数
glutMainLoop();//进入消息循环
return 0;
}