WINCE实现直接写屏(一)
//========================================================================
//TITLE:
// WINCE实现直接写屏
//AUTHOR:
// norains
//DATE:
// Friday 28-April-2006
//========================================================================
DOS下的直接写屏估计很多人都知道;如果真的不知道,在网上一搜,恩...有不少现成的代码.^_^今天要解决的是,在WINCE下实现直接写屏.
其实说穿了不值钱,原理极其简单,只要往显示缓存地址直接写值就OK.
首先说一下基本的配置.我们采用的是sharp LH7A404H的开发版,其视频总线是16bit;换而言之,就是一个象素用两个字节表示.最为重要的是,其硬件电路可以直接输出565格式16bit的位图.关于位图,根据色深域(biBitCount)取值可以分为六种,分别是1,4,8,16,24,32;而其中16bit的位图根据红,绿,蓝的掩码不同分为两种格式:555(R:0x7C00,G:0x03E0,B:001F),565(R:0xF800,G:0x07E0,B:0x001F).更详细的解说,可以参考别的资料;若要理解此篇而言,此知识点足矣.^_^
好了,我们就开始旅程吧.以下的工作,要实现这么一个过程:当接收到一个信号后,在屏幕中间绘制一副32*32的位图;停顿1秒之后,位图消失,回复原来屏幕本来面目.
我们直接在显示驱动中添加代码,对于LH7A404而言,是soclcd.cpp文件.
恩,最初,我们需要再初始化中建立一个线程:
if (CreateThread(0, 0, DisplayInfoThread, (void *)m_ulFrmBufVirt, 0, NULL) == NULL)
{
RETAILMSG(1,(TEXT("norains Failed/n/r")));
return ;
}
DisplayInfoThread是我们线程处理函数,m_ulFrmBufVirt是传给函数的型参,存储的是显示缓存的地址.
接下来便是重中之重,编写我们的处理函数了.
函数原型如此:static DWORD WINAPI DisplayInfoThread(LPVOID pData)
首先,我们先要创建一个信号量,只有接收到信号量的时候才开始写屏.创建信号量是一件很简单的事情:
HANDLE hdEvent = CreateEvent(NULL, FALSE, FALSE, L"MyLockInfo");
...
while(true)
{
WaitForSingleObject(hdEvent, INFINITE); //如果没有接收到信号量,则一直停在此处
...
}
在进入循环体之前,我们还有两件事情要做:一是分配一段内存用以保留原来屏幕的数据;二是将视频指针指向屏幕中间,恩,因为我们要在中间显示位图.
pSaveBuf = (PUCHAR) AllocPhysMem(TCHLK_SAVEBUFF_SIZE,PAGE_READWRITE|PAGE_NOCACHE,0,0,&ulOffset);
...
pVideoBase += ((m_iScreenHeight-TCHLK_LOG_HEIGHT)>>1)*m_iScreenWidth*2 + ((m_iScreenWidth-TCHLK_LOG_WIDTH)>>1)*2;
...
TCHLK_LOG_HEIGHT是我们自己定义的宏变量,代表的是位图的高度.为了大家看得明白,把其它宏定义也列出来吧:
#define TCHLK_LOG_WIDTH 32 //宽度
#define TCHLK_LOG_HEIGHT 32 //高度
#define TCHLK_SAVEBUFF_SIZE 0x2000 //缓存要占据的空间大小
好了,接下来我们要做的就是把屏幕的原来象素点保存下来:
ulOffset = 0;
pVideoBuf = pVideoBase;
while(ulOffset<TCHLK_LOG_HEIGHT*TCHLK_LOG_WIDTH*2)
{
memcpy((PUCHAR)(pSaveBuf+ulOffset), (PUCHAR)pVideoBuf, TCHLK_LOG_WIDTH*2);
pVideoBuf += m_iScreenWidth<<1;
ulOffset += TCHLK_LOG_WIDTH*2;
}
保存完毕,接下来不用说,肯定就是写屏了:
ulOffset = 0;
pVideoBuf = pVideoBase;
while(ulOffset<TCHLK_LOG_HEIGHT*TCHLK_LOG_WIDTH*2)
{
memcpy((PUCHAR)pVideoBuf, (PUCHAR)(pBmpInfo+ulOffset), TCHLK_LOG_WIDTH*2);
pVideoBuf += m_iScreenWidth<<1;
ulOffset += TCHLK_LOG_WIDTH*2;
}
恩,这里pBmpInfo是一个数组,存储的是以十六进制表示的BMP位图信息.虽然文中用到的是32*32的位图,但数据量也有8K!恩,如果在文章中间插入这个位图数据,影响美观是肯定的,所以我们还是把这数组定义放在文章最后吧.如果你迫不及待想看这数组定义,恩,就请往最后翻页吧.
当然咯,我们需要控制位图显示的时间.一个小小的语句段就可以解决问题:
iCount= 100; //about 1s
while (iCount -- > 0) HalStallExecution(10*10000);
最后嘛,时间到,不用说,就是恢复原来屏幕象素.和绘制位图一样,也是往缓存拷贝数据:
ulOffset = 0;
pVideoBuf = pVideoBase;
while(ulOffset<TCHLK_LOG_HEIGHT*TCHLK_LOG_WIDTH*2)
{
memcpy((PUCHAR)pVideoBuf, (PUCHAR)(pSaveBuf+ulOffset), TCHLK_LOG_WIDTH*2);
pVideoBuf += m_iScreenWidth<<1;
ulOffset += TCHLK_LOG_WIDTH*2;
}
不过,总有事情要出乎我们预料的.有时候,屏幕上的图片一闪而过,特别是播放电影的时候.这个问题嘛,是因为别的程序同时往缓存写数据.解决这个问题也很简单,不过就显得比较霸道.我们自己可以编写两个函数,一个用来关闭中断,另一个开中断.关闭中断的时候,谁都不能往屏幕写数据.只是这两个函数因不同硬件有不同实现,就此略过.我们在此使用的是LH7A404预先写好的两个函数:
BOOL fWasEnabled = InterruptsDisable();
...
InterruptsRestore(fWasEnabled);
好了,至此,一切大功告成!不过,可能有些执着的偏执狂对于之前分配的内存没有释放一直耿耿于怀,好吧,那我们就在循环体外对内存进行释放吧:
FreePhysMem((void *)pSaveBuf);
不过,估计这语句是永远也执行不到了.^_^
//TITLE:
// WINCE实现直接写屏
//AUTHOR:
// norains
//DATE:
// Friday 28-April-2006
//========================================================================
DOS下的直接写屏估计很多人都知道;如果真的不知道,在网上一搜,恩...有不少现成的代码.^_^今天要解决的是,在WINCE下实现直接写屏.
其实说穿了不值钱,原理极其简单,只要往显示缓存地址直接写值就OK.
首先说一下基本的配置.我们采用的是sharp LH7A404H的开发版,其视频总线是16bit;换而言之,就是一个象素用两个字节表示.最为重要的是,其硬件电路可以直接输出565格式16bit的位图.关于位图,根据色深域(biBitCount)取值可以分为六种,分别是1,4,8,16,24,32;而其中16bit的位图根据红,绿,蓝的掩码不同分为两种格式:555(R:0x7C00,G:0x03E0,B:001F),565(R:0xF800,G:0x07E0,B:0x001F).更详细的解说,可以参考别的资料;若要理解此篇而言,此知识点足矣.^_^
好了,我们就开始旅程吧.以下的工作,要实现这么一个过程:当接收到一个信号后,在屏幕中间绘制一副32*32的位图;停顿1秒之后,位图消失,回复原来屏幕本来面目.
我们直接在显示驱动中添加代码,对于LH7A404而言,是soclcd.cpp文件.
恩,最初,我们需要再初始化中建立一个线程:
if (CreateThread(0, 0, DisplayInfoThread, (void *)m_ulFrmBufVirt, 0, NULL) == NULL)
{
RETAILMSG(1,(TEXT("norains Failed/n/r")));
return ;
}
DisplayInfoThread是我们线程处理函数,m_ulFrmBufVirt是传给函数的型参,存储的是显示缓存的地址.
接下来便是重中之重,编写我们的处理函数了.
函数原型如此:static DWORD WINAPI DisplayInfoThread(LPVOID pData)
首先,我们先要创建一个信号量,只有接收到信号量的时候才开始写屏.创建信号量是一件很简单的事情:
HANDLE hdEvent = CreateEvent(NULL, FALSE, FALSE, L"MyLockInfo");
...
while(true)
{
WaitForSingleObject(hdEvent, INFINITE); //如果没有接收到信号量,则一直停在此处
...
}
在进入循环体之前,我们还有两件事情要做:一是分配一段内存用以保留原来屏幕的数据;二是将视频指针指向屏幕中间,恩,因为我们要在中间显示位图.
pSaveBuf = (PUCHAR) AllocPhysMem(TCHLK_SAVEBUFF_SIZE,PAGE_READWRITE|PAGE_NOCACHE,0,0,&ulOffset);
...
pVideoBase += ((m_iScreenHeight-TCHLK_LOG_HEIGHT)>>1)*m_iScreenWidth*2 + ((m_iScreenWidth-TCHLK_LOG_WIDTH)>>1)*2;
...
TCHLK_LOG_HEIGHT是我们自己定义的宏变量,代表的是位图的高度.为了大家看得明白,把其它宏定义也列出来吧:
#define TCHLK_LOG_WIDTH 32 //宽度
#define TCHLK_LOG_HEIGHT 32 //高度
#define TCHLK_SAVEBUFF_SIZE 0x2000 //缓存要占据的空间大小
好了,接下来我们要做的就是把屏幕的原来象素点保存下来:
ulOffset = 0;
pVideoBuf = pVideoBase;
while(ulOffset<TCHLK_LOG_HEIGHT*TCHLK_LOG_WIDTH*2)
{
memcpy((PUCHAR)(pSaveBuf+ulOffset), (PUCHAR)pVideoBuf, TCHLK_LOG_WIDTH*2);
pVideoBuf += m_iScreenWidth<<1;
ulOffset += TCHLK_LOG_WIDTH*2;
}
保存完毕,接下来不用说,肯定就是写屏了:
ulOffset = 0;
pVideoBuf = pVideoBase;
while(ulOffset<TCHLK_LOG_HEIGHT*TCHLK_LOG_WIDTH*2)
{
memcpy((PUCHAR)pVideoBuf, (PUCHAR)(pBmpInfo+ulOffset), TCHLK_LOG_WIDTH*2);
pVideoBuf += m_iScreenWidth<<1;
ulOffset += TCHLK_LOG_WIDTH*2;
}
恩,这里pBmpInfo是一个数组,存储的是以十六进制表示的BMP位图信息.虽然文中用到的是32*32的位图,但数据量也有8K!恩,如果在文章中间插入这个位图数据,影响美观是肯定的,所以我们还是把这数组定义放在文章最后吧.如果你迫不及待想看这数组定义,恩,就请往最后翻页吧.
当然咯,我们需要控制位图显示的时间.一个小小的语句段就可以解决问题:
iCount= 100; //about 1s
while (iCount -- > 0) HalStallExecution(10*10000);
最后嘛,时间到,不用说,就是恢复原来屏幕象素.和绘制位图一样,也是往缓存拷贝数据:
ulOffset = 0;
pVideoBuf = pVideoBase;
while(ulOffset<TCHLK_LOG_HEIGHT*TCHLK_LOG_WIDTH*2)
{
memcpy((PUCHAR)pVideoBuf, (PUCHAR)(pSaveBuf+ulOffset), TCHLK_LOG_WIDTH*2);
pVideoBuf += m_iScreenWidth<<1;
ulOffset += TCHLK_LOG_WIDTH*2;
}
不过,总有事情要出乎我们预料的.有时候,屏幕上的图片一闪而过,特别是播放电影的时候.这个问题嘛,是因为别的程序同时往缓存写数据.解决这个问题也很简单,不过就显得比较霸道.我们自己可以编写两个函数,一个用来关闭中断,另一个开中断.关闭中断的时候,谁都不能往屏幕写数据.只是这两个函数因不同硬件有不同实现,就此略过.我们在此使用的是LH7A404预先写好的两个函数:
BOOL fWasEnabled = InterruptsDisable();
...
InterruptsRestore(fWasEnabled);
好了,至此,一切大功告成!不过,可能有些执着的偏执狂对于之前分配的内存没有释放一直耿耿于怀,好吧,那我们就在循环体外对内存进行释放吧:
FreePhysMem((void *)pSaveBuf);
不过,估计这语句是永远也执行不到了.^_^