您的位置:首页 > 其它

【今日插件】在 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可以运行的对话框程序,来演示上面的代码和技术:对话框的截图如下:



(5)下载连接;

代码下载连接(包含PC端演示程序,和 WindowMobile 插件源代码):
http://files.cnblogs.com/hoodlum1980/JRL_LedScreen.rar

同时,我开发的插件也发布到 pdafans 论坛供网友测试和使用,因此也可以在 pdafans 论坛的以下帖子下载到本文提到的今日插件的最新 CAB 包,在我的博客上仅保留插件的较早版本。

一个模拟LED显示屏的桌面今日插件

by hoodlum1980 @ 2009
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: