【今日插件】在 WindowMobile 上的模拟LED 显示屏插件
2009-08-09 23:31
435 查看
我在给一个对话框上的控件查找翻看合适的图标时,无形中看到了一个LED显示屏的图标,这里所说的LED显示屏是指由很多LED灯密集排列组成的点阵式LED屏,比如在股市交易所,公交车上,银行门口,我们经常能看到这样的滚动式显示屏。我不禁忽然想到,如果把它放在手机上显示,那效果是不是很别致呢?而且我在很久以前用 C# 模拟了这种LED显示屏的效果,因此技术上没有什么问题。不过现在,我想把它在手机上实现,而且我的想法是做成今日插件,因为相比普通的应用程序,插件更方便用户启用,禁用,可以在桌面上展示。然后,现在我要考虑使用 C++ 实现,在技术上的实现会和上一次在C#实现上略有不同,但本质原理是一致的,并且这一次实现的效果将更符合现实中我们看到的 LED 屏。这里我将会讲述一点 LED 屏在滚动时的原理和细节技巧。
首先来看,这是插件在手机上滚动显示的效果:
CreateLedBitmap
//根据指定的字符串创建实际的用于滚动的位图(比实际图像会增大4倍)
//返回创建的位图的未LED化像素长度(也就是实际字符串的绘制长度)
int CreateLedBitmap(HWND hWnd, TCHAR* text)
{
//创建字体
LOGFONT lf;
HFONT hFont = NULL, hOldFont = NULL;
SIZE size = {0,0};
BITMAPINFO bminfo;
HBITMAP hOldBitmap; //临时位图(为非LED化的实际像素大小)
HBRUSH hBrush =NULL;
RECT rc;
int i, j, stride, strideTemp;
//两个位图的像素数据地址
BYTE *lpBytes, *lpBytesTemp, *lpLed;
//设置位图信息
memset(&bminfo.bmiHeader, 0, sizeof(BITMAPINFOHEADER));
bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); //40
bminfo.bmiHeader.biPlanes = 1;
bminfo.bmiHeader.biBitCount = 24;
//测量字符串的大小,其决定作用的还有当前HDC中的字体
HDC hWndDC = GetDC(hWnd);
HDC hDC = CreateCompatibleDC(hWndDC);
ReleaseDC(hWnd, hWndDC);
HFONT hSysFont = (HFONT)GetStockObject(DEVICE_DEFAULT_FONT);
GetObject(hSysFont, sizeof(LOGFONT), &lf);
lf.lfWeight = FW_NORMAL;
lf.lfHeight = (long) -((11.0 * (double)GetDeviceCaps(hDC, LOGPIXELSY) / 72.0) + .5);
//lf.lfHeight = 11;
wcscpy(lf.lfFaceName, _T("宋体"));
// create the font
hFont = CreateFontIndirect(&lf);
//创建背景刷
hBrush = CreateSolidBrush(BKCOLOR_LED);
hOldFont = (HFONT)SelectObject(hDC, hFont);
//测量字符串,得到的高度应该是14,宽度取决于字符串
GetTextExtentPoint32(hDC, text, wcslen(text), &size);
//计算扫描行宽度
stride = (24 * size.cx * LEDSIZE + 31)/32 * 4;
strideTemp = (24* size.cx + 31)/32 * 4;
bminfo.bmiHeader.biWidth = size.cx * LEDSIZE;
//去掉上下的2行像素
bminfo.bmiHeader.biHeight = size.cy * LEDSIZE; //(14-2)*4=48pixels,
bminfo.bmiHeader.biSizeImage = bminfo.bmiHeader.biHeight * stride;
//创建位图
if(g_BitmapLed != NULL)
DeleteObject(g_BitmapLed);
if(g_BitmapTemp != NULL)
DeleteObject(g_BitmapTemp);
//注意WinCe平台上不支持内存映射,所以无法提供我们自己的位图数据,而是由系统负责分配
g_BitmapLed = CreateDIBSection(hDC, &bminfo, DIB_RGB_COLORS, (VOID**)&lpBytes, NULL, 0);
//是否创建成功?
if(g_BitmapLed == NULL)
{
goto CLEARNUP_EXIT;
}
//创建实际大小的位图
bminfo.bmiHeader.biWidth = size.cx;
bminfo.bmiHeader.biHeight = size.cy;
g_BitmapTemp = CreateDIBSection(hDC, &bminfo, DIB_RGB_COLORS, (VOID**)&lpBytesTemp, NULL, 0);
if(g_BitmapTemp == NULL)
{
goto CLEARNUP_EXIT;
}
//在实际绘图上进行绘制
hOldBitmap = (HBITMAP)SelectObject(hDC, g_BitmapTemp);
rc.left = rc.top = 0;
rc.right = size.cx;
rc.bottom = size.cy;
//填充白色背景
FillRect(hDC, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH));
SetTextColor(hDC, RGB(0,0,0)); //黑色字体
SetBkMode(hDC, TRANSPARENT);
//绘制字符串
DrawText(hDC, text, wcslen(text), &rc, DT_LEFT | DT_VCENTER| DT_NOCLIP | DT_SINGLELINE);
//现在再把LED图选入DC中去刷一次背景
SelectObject(hDC, g_BitmapLed);
//rc设置为LED图的大小
rc.right = size.cx * LEDSIZE;
rc.bottom = size.cy * LEDSIZE;
FillRect(hDC, &rc,hBrush);
//复原DC
SelectObject(hDC, hOldBitmap);
SelectObject(hDC, hOldFont);
//现在根据它进行拷贝到新的位图中,去掉了上下两行像素,i,j是在 bitmaptemp中的坐标
//每个LED灯实际上是 4 * 4像素的小矩形块!设置6个像素即可
for(j = 0; j< size.cy; j++)
{
for(i = 0; i < size.cx; i++)
{
//把指针定位到LED灯的左上角!
//注意这里行是倒序的!(所以距离内存起始点的最远的哪一行才是LED第一行,逐行向内存起始处返回)
lpLed = lpBytes + stride*(j*LEDSIZE+3) + i*3*LEDSIZE;
if(lpBytesTemp[strideTemp * j + i*3] == 0) //即,该通道为黑色,表示这里是字迹(应该点亮这个灯)
{
//点亮 9 * 9
//第一行 0-Blue, 1-Green, 2-Red
lpLed[0]= 76, lpLed[1]=154, lpLed[2]=186;
lpLed[3]=130, lpLed[4]=207, lpLed[5]=236;
lpLed[6]= 56, lpLed[7]=145, lpLed[8]=204;
//第二行
lpLed -= stride;
lpLed[0]=127, lpLed[1]=214, lpLed[2]=245;
lpLed[3]=193, lpLed[4]=232, lpLed[5]=247;
lpLed[6]= 43, lpLed[7]=167, lpLed[8]=241;
//第三行
lpLed -= stride;
lpLed[0]= 56, lpLed[1]=144, lpLed[2]=204;
lpLed[3]= 43, lpLed[4]=160, lpLed[5]=231;
lpLed[6]= 43, lpLed[7]=125, lpLed[8]=207;
}
else
{
//熄灭 9 * 9
//第一行 0-Blue, 1-Green, 2-Red
lpLed[0]= 65, lpLed[1]= 65, lpLed[2]= 65;
lpLed[3]= 84, lpLed[4]= 84, lpLed[5]= 84;
lpLed[6]= 65, lpLed[7]= 65, lpLed[8]= 65;
//第二行
lpLed -= stride;
lpLed[0]= 84, lpLed[1]= 84, lpLed[2]= 84;
lpLed[3]= 66, lpLed[4]= 66, lpLed[5]= 66;
//lpLed[6]= 65, lpLed[7]= 65, lpLed[8]= 65;
//第三行
lpLed -= stride;
lpLed[0]= 65, lpLed[1]= 65, lpLed[2]= 65;
//lpLed[3]=130, lpLed[4]=207, lpLed[5]=236;
//lpLed[6]= 56, lpLed[7]=145, lpLed[8]=204;
}
}
}
CLEARNUP_EXIT:
//清理
if(hBrush !=NULL)
DeleteObject(hBrush);
if(hFont != NULL)
DeleteObject(hFont);
if(hDC != NULL)
DeleteDC(hDC);
return size.cx;
}
(4)PC上的演示程序;
最后,本范例的代码是运行在 PDA (WINDOWS MOBILE)上的今日插件,但这里提到的LED滚屏技术是不局限平台的,因此我也做了一个在PC可以运行的对话框程序,来演示上面的代码和技术:对话框的截图如下:
![](http://images.cnblogs.com/cnblogs_com/hoodlum1980/LedScreen04.jpg)
(5)下载连接;
代码下载连接(包含PC端演示程序,和 WindowMobile 插件源代码):
http://files.cnblogs.com/hoodlum1980/JRL_LedScreen.rar
同时,我开发的插件也发布到 pdafans 论坛供网友测试和使用,因此也可以在 pdafans 论坛的以下帖子下载到本文提到的今日插件的最新 CAB 包,在我的博客上仅保留插件的较早版本。
一个模拟LED显示屏的桌面今日插件
by hoodlum1980 @ 2009
首先来看,这是插件在手机上滚动显示的效果:
CreateLedBitmap
//根据指定的字符串创建实际的用于滚动的位图(比实际图像会增大4倍)
//返回创建的位图的未LED化像素长度(也就是实际字符串的绘制长度)
int CreateLedBitmap(HWND hWnd, TCHAR* text)
{
//创建字体
LOGFONT lf;
HFONT hFont = NULL, hOldFont = NULL;
SIZE size = {0,0};
BITMAPINFO bminfo;
HBITMAP hOldBitmap; //临时位图(为非LED化的实际像素大小)
HBRUSH hBrush =NULL;
RECT rc;
int i, j, stride, strideTemp;
//两个位图的像素数据地址
BYTE *lpBytes, *lpBytesTemp, *lpLed;
//设置位图信息
memset(&bminfo.bmiHeader, 0, sizeof(BITMAPINFOHEADER));
bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); //40
bminfo.bmiHeader.biPlanes = 1;
bminfo.bmiHeader.biBitCount = 24;
//测量字符串的大小,其决定作用的还有当前HDC中的字体
HDC hWndDC = GetDC(hWnd);
HDC hDC = CreateCompatibleDC(hWndDC);
ReleaseDC(hWnd, hWndDC);
HFONT hSysFont = (HFONT)GetStockObject(DEVICE_DEFAULT_FONT);
GetObject(hSysFont, sizeof(LOGFONT), &lf);
lf.lfWeight = FW_NORMAL;
lf.lfHeight = (long) -((11.0 * (double)GetDeviceCaps(hDC, LOGPIXELSY) / 72.0) + .5);
//lf.lfHeight = 11;
wcscpy(lf.lfFaceName, _T("宋体"));
// create the font
hFont = CreateFontIndirect(&lf);
//创建背景刷
hBrush = CreateSolidBrush(BKCOLOR_LED);
hOldFont = (HFONT)SelectObject(hDC, hFont);
//测量字符串,得到的高度应该是14,宽度取决于字符串
GetTextExtentPoint32(hDC, text, wcslen(text), &size);
//计算扫描行宽度
stride = (24 * size.cx * LEDSIZE + 31)/32 * 4;
strideTemp = (24* size.cx + 31)/32 * 4;
bminfo.bmiHeader.biWidth = size.cx * LEDSIZE;
//去掉上下的2行像素
bminfo.bmiHeader.biHeight = size.cy * LEDSIZE; //(14-2)*4=48pixels,
bminfo.bmiHeader.biSizeImage = bminfo.bmiHeader.biHeight * stride;
//创建位图
if(g_BitmapLed != NULL)
DeleteObject(g_BitmapLed);
if(g_BitmapTemp != NULL)
DeleteObject(g_BitmapTemp);
//注意WinCe平台上不支持内存映射,所以无法提供我们自己的位图数据,而是由系统负责分配
g_BitmapLed = CreateDIBSection(hDC, &bminfo, DIB_RGB_COLORS, (VOID**)&lpBytes, NULL, 0);
//是否创建成功?
if(g_BitmapLed == NULL)
{
goto CLEARNUP_EXIT;
}
//创建实际大小的位图
bminfo.bmiHeader.biWidth = size.cx;
bminfo.bmiHeader.biHeight = size.cy;
g_BitmapTemp = CreateDIBSection(hDC, &bminfo, DIB_RGB_COLORS, (VOID**)&lpBytesTemp, NULL, 0);
if(g_BitmapTemp == NULL)
{
goto CLEARNUP_EXIT;
}
//在实际绘图上进行绘制
hOldBitmap = (HBITMAP)SelectObject(hDC, g_BitmapTemp);
rc.left = rc.top = 0;
rc.right = size.cx;
rc.bottom = size.cy;
//填充白色背景
FillRect(hDC, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH));
SetTextColor(hDC, RGB(0,0,0)); //黑色字体
SetBkMode(hDC, TRANSPARENT);
//绘制字符串
DrawText(hDC, text, wcslen(text), &rc, DT_LEFT | DT_VCENTER| DT_NOCLIP | DT_SINGLELINE);
//现在再把LED图选入DC中去刷一次背景
SelectObject(hDC, g_BitmapLed);
//rc设置为LED图的大小
rc.right = size.cx * LEDSIZE;
rc.bottom = size.cy * LEDSIZE;
FillRect(hDC, &rc,hBrush);
//复原DC
SelectObject(hDC, hOldBitmap);
SelectObject(hDC, hOldFont);
//现在根据它进行拷贝到新的位图中,去掉了上下两行像素,i,j是在 bitmaptemp中的坐标
//每个LED灯实际上是 4 * 4像素的小矩形块!设置6个像素即可
for(j = 0; j< size.cy; j++)
{
for(i = 0; i < size.cx; i++)
{
//把指针定位到LED灯的左上角!
//注意这里行是倒序的!(所以距离内存起始点的最远的哪一行才是LED第一行,逐行向内存起始处返回)
lpLed = lpBytes + stride*(j*LEDSIZE+3) + i*3*LEDSIZE;
if(lpBytesTemp[strideTemp * j + i*3] == 0) //即,该通道为黑色,表示这里是字迹(应该点亮这个灯)
{
//点亮 9 * 9
//第一行 0-Blue, 1-Green, 2-Red
lpLed[0]= 76, lpLed[1]=154, lpLed[2]=186;
lpLed[3]=130, lpLed[4]=207, lpLed[5]=236;
lpLed[6]= 56, lpLed[7]=145, lpLed[8]=204;
//第二行
lpLed -= stride;
lpLed[0]=127, lpLed[1]=214, lpLed[2]=245;
lpLed[3]=193, lpLed[4]=232, lpLed[5]=247;
lpLed[6]= 43, lpLed[7]=167, lpLed[8]=241;
//第三行
lpLed -= stride;
lpLed[0]= 56, lpLed[1]=144, lpLed[2]=204;
lpLed[3]= 43, lpLed[4]=160, lpLed[5]=231;
lpLed[6]= 43, lpLed[7]=125, lpLed[8]=207;
}
else
{
//熄灭 9 * 9
//第一行 0-Blue, 1-Green, 2-Red
lpLed[0]= 65, lpLed[1]= 65, lpLed[2]= 65;
lpLed[3]= 84, lpLed[4]= 84, lpLed[5]= 84;
lpLed[6]= 65, lpLed[7]= 65, lpLed[8]= 65;
//第二行
lpLed -= stride;
lpLed[0]= 84, lpLed[1]= 84, lpLed[2]= 84;
lpLed[3]= 66, lpLed[4]= 66, lpLed[5]= 66;
//lpLed[6]= 65, lpLed[7]= 65, lpLed[8]= 65;
//第三行
lpLed -= stride;
lpLed[0]= 65, lpLed[1]= 65, lpLed[2]= 65;
//lpLed[3]=130, lpLed[4]=207, lpLed[5]=236;
//lpLed[6]= 56, lpLed[7]=145, lpLed[8]=204;
}
}
}
CLEARNUP_EXIT:
//清理
if(hBrush !=NULL)
DeleteObject(hBrush);
if(hFont != NULL)
DeleteObject(hFont);
if(hDC != NULL)
DeleteDC(hDC);
return size.cx;
}
(4)PC上的演示程序;
最后,本范例的代码是运行在 PDA (WINDOWS MOBILE)上的今日插件,但这里提到的LED滚屏技术是不局限平台的,因此我也做了一个在PC可以运行的对话框程序,来演示上面的代码和技术:对话框的截图如下:
![](http://images.cnblogs.com/cnblogs_com/hoodlum1980/LedScreen04.jpg)
(5)下载连接;
代码下载连接(包含PC端演示程序,和 WindowMobile 插件源代码):
http://files.cnblogs.com/hoodlum1980/JRL_LedScreen.rar
同时,我开发的插件也发布到 pdafans 论坛供网友测试和使用,因此也可以在 pdafans 论坛的以下帖子下载到本文提到的今日插件的最新 CAB 包,在我的博客上仅保留插件的较早版本。
一个模拟LED显示屏的桌面今日插件
by hoodlum1980 @ 2009
相关文章推荐
- 在 WindowMobile 上的模拟LED 显示屏插件(转)
- 超大型LED显示屏(模拟)
- Led控件(2)——Led显示屏模拟
- 超大型LED显示屏
- Windows系统功能模拟 C++(EasyX插件)—— 8th 窗口(二)
- 使用firefox插件httperrequest,模拟发送及接收Json请求
- 调试今日插件,托盘程序,DLL
- SDUT 2610 LED显示屏
- 今日事今日毕,sublime编辑器的插件PlainTasks
- 如何调试今日插件,tray Icon,以及DLL。
- jQ模拟打字效果插件typetype
- chorme插件 ,在浏览器上模拟手机,pad 查看网页|前端技术开发必备插件
- LED显示屏二次开发接口的设计方案
- FCPX插件 Cine Looks Color Grading 3D LUTs 模拟电影胶片调色 Mac版 v1.0破解版
- JQUERY插件:仿QQ今日话题正反方投票百分比显示
- Chrome不用插件自定义user-agent,模拟手机浏览器
- Chrome浏览器插件Postman用法简介-Http请求模拟工具
- 在火狐浏览器上安装RESTClient插件可以模拟发送get、post请求
- 可模拟get和post提交的firefox插件。
- 今日头条首页模拟