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);
   不过,估计这语句是永远也执行不到了.^_^
posted @ 2006-04-29 12:46  我的一天  阅读(207)  评论(0编辑  收藏  举报