文字滚动的技术实现

在WinCE平台下,使用evc或vs2005写出文字滚动的代码并不是一件难事,甚至可以说非常简单。
   
    大体上来说,程序的失败与否取决于两个关键点:
   
    1.准确计算文本的长度以及宽度;
   
    2.定时刷新窗口。
   
    现在我们来看看这两点分别有什么注意的地方。
   
    首先是计算文本的长度。
   
    其实要做到这点也并非难事,因为WinCE给我们一个现成的函数:GetTextExtentPoint。
   
    函数的原型和解释如下:
   
  BOOL GetTextExtentPoint(
                          HDC hdc,
                          LPCTSTR lpString,
                          int cbString,
                          LPSIZE lpSize
                          );
                         
  hdc
    [in] Handle to the device context (DC).
 
  lpString
    [in] Long pointer to the string of text. The string does not need to be zero-terminated because cbString specifies the length of the string.

  cbString
    [in] Integer that specifies the number of characters in the string.

  lpSize
    [out] Long pointer to a SIZE structure that receives the dimensions of the string.
   
   
    我们需要注意的是lpString形参的解释:The string does not need to be zero-terminated because cbString specifies the length of the string。 翻译为中文的大概的意思是,字符串不需要以0为结尾,因为你可以在cbString形参定义字符串的长度。那么,如果我们以0为结尾又如何呢?该函数会不 会自动计算?cbString也没有正式表明如果该值为-1则自动计算。也许你现在的环境依次设置可能会计算出正确的长度,但并不代表以后WinCe的版 本甚至是不同CPU环境下也有相同的表现,所以我建议,以在cbString传入字符串个数的方式。
   
    现在我们先来看两段代码:
   
    SIZE size1 = {0};
    GetTextExtentPoint(hdc,TEXT("无换行"),_tcslen(TEXT("无换行")),&size1);
   
    SIZE size2 = {0};
    GetTextExtentPoint(hdc,TEXT("有换行/n/r第二行"),_tcslen(TEXT("有换行/n/r第二行")),&size2);
   
    直觉是不是告诉你,这代码应该为真: size2.cy > size1.cy ?
   
    但事实确实无情的,因为 size2.cy == size1.cy !
   
    也就是说,GetTextExtentPoint是将“TEXT("有换行/n/r第二行")”这个字符串当成一行来计算!所以,如果你想编写一段上移或下移的代码,你无法直接通过该函数获取其所需的高度。
   
    换个角度想,虽然GetTextExtentPoint获取的仅仅是一行的高度,但我们却可以自行判断有多少行,然后再用行数相乘每行的高度不就是所需要的总高度了么?
   
    例如:
    //Get the amount of the "/n"
  TCHAR *pFind = m_pszTxtInfo;
  int iCountLine = 1;
  while(TRUE)
  {
   pFind = _tcsstr(pFind,TEXT("/n"));
   
   if(pFind != NULL)
   {
    pFind += 1;
    iCountLine ++;
   }
   else
   {
    break;
   }
  }
  //所需要的总高度
  m_iTxtInfoHeight = size.cy * iCountLine;

 

    接下来的另一个难点就是采用什么方式刷新文字。
   
    不必说,最基本的自然是首先创建一个线程,然后在该线程中刷新文字。但,采用什么方式来刷新呢?
   
    熟悉SDK编码的朋友都不会陌生,我们可以调用DrawText来绘制文本。也许大家对该函数的lpRect形参比较感兴趣,因为该形参定义了绘制的坐标。一个很自然的想法是,我们在线程中不停调整坐标,然后再强制窗口重绘。
   
    举个简单的例子:
   
    线程代码:
   
    //右移一像素
    m_rcDraw.left += 1;
    //强制重绘
    InvalidateRect(m_hWnd,NULL,FALSE)
   
    在WM_PAINT响应消息中加入如下代码:
    DrawText(hdc,pszText,-1,&m_rcDraw,uFormat);
   
    很简单,也很方便,是不是?但其实这样做有一个问题被我们忽略了,就是调用DrawText耗费的时间!一段不过二十个字的字符串,调用该函数耗费的时间是调用BitBlt的三倍!更不用说绘制一个文本文件的内容了!
   
    我们再换另外一个角度来想,既然调用BitBlt的速度远远比DrawText的快,那么我们为何不先创建一个内存DC,然后在这DC中调用DrawText初始化文本,最后在使用的使用再将该DC绘制到目标DC中去呢?
   
    接下来的问题就出来了,这内存DC应该在哪里创建?大小如何确定?前面我们已经提到,如何计算显示文本的总宽度和总高度,现在这两个数值就能派上用场了。因为我们的内存DC主要是存储文本,所以我们的内存DC的长高完全可以用文本的总宽度和总高度。
   
    所以,在计算完毕文本所需要的区域大小后,我们可以马上创建内存DC:
   
  //Create the memory DC
 CMemDC memDC;
 SIZE size = {m_iTxtInfoWidth,m_iTxtInfoHeight};
 memDC.Create(hdc,&size);
 DrawText(memDC.GetDC(),pszText,-1,&m_rcDraw,uFormat);
 
    接下来我们只需要在WM_PAINT的处理函数中将该DC按坐标绘制出来即可:
    BitBlt(memDC.GetDC(),
      m_rcDraw.left,
      m_rcDraw.top,
      m_iTxtInfoWidth,
      m_iTxtInfoHeight,
      m_DCTxtInfo.GetDC(),
      0,
      0
      SRCCOPY);
      

    只要解决这两个关键问题,文字的滚动并不是一件非常困难的事情。
   
    在我另一篇《CTextWnd轻松实现文字的滚动》文章中,封装了一个CTextWnd窗口类,能够简便地实现文字的滚动,有兴趣的朋友可以参考。

转载于:https://www.cnblogs.com/nsoft/archive/2012/06/26/2564167.html

1.基本要求 能够实现如下功能: ‹ 首先用蓝色清屏 ‹ 在屏幕中央显示由字符串“-============#”组成的黄色的小球,#为球, 按下方向键可以控制上述小球球行方式在屏幕上行走 ‹ 在行进过程中,球只能左转、右转或继续前进,不能掉转 180 度 2.鼓励实现完整的彩球滚动游戏,鼓励有新的创意 3.提示 ①通过调用 INT 16H 的 0 号功能可以读取光标控制键的扩展码 光标控制键: ↑ ↓ ← → 扩展码(十进制): 72 80 75 77 ②在指定位置用指定属性显示字符的方法有两种:一是直接写显示缓冲区,二是利用 BIOS 屏显功能调用。解: 功能描述(基本上是全部功能):本程序有以下功能 共分为7关,可以手工选择关卡(带有输入异常处理)。走完一关后,如果后面还有关, 直接跳到下一关。否则,结束游戏。各个关之间的差别是速度不同。 按下方向键,球能够按照题的要求在屏幕上行走。按下 ESC 键,退出游戏,按下其他键, 程序不理会。 长时间不按键,球会自动前进。 能够产生随机数,作为蛋。球的初始大小为 14,当大小为 20 时,此关结束。 如果球运动到了边界,球死亡。游戏结束。 ① 设计思路 程序开始时,由用户指定一个关卡,进入游戏。 每次用清屏加显示字符的方式重新显示球和蛋。 当检测到有键子按下时,判断是什么键子,如果是 esc,退出游戏,如果是方向键,按 正确的方向走(如果方向键与球运动方向相反,不理会按键),如果按下的时其它键, 不理会。 设置一个等待时间,如果超过等待时间仍没有按键,球自动前行一步。否则,重新比较 时间。 每次球运动或有键子被按下时,判断是否撞到了自身和边界。 如果吃到了一个蛋,更新完球的位置后,将原球的位置加入球中。 如果球的长度达到了 20(设置的球的最大大小),判断后面是否还有关,没有了,就结 束程序,还有,就跳到下一关卡。 ② 算法说明 设置两个标记变量,分别记录球的大小 ssize 和球上次的大小 befor。设置标记变量, 分别记录蛋的横纵座标 xlabel 和 ylabel,设置标记变量,记录球的位置 tailx 和 taily。设置 snake 记录球各个部分的位置,设置球的最大大小为 20。设置变量 TIME 为等待按键时间。 关卡的选择:程序开始时,从键盘读入一个数字,当作关卡,根据读入的数据,设置等 待时间,也就实现了对球的速度的控制。 清屏和显示小球和蛋。调用 bios 中断可以实现。每次输出 1 个球,下面说一下如何实现小球的手工移动(有按键输入时)。可以知道,如果把球看成一个 个单元,球每移动一次,它的身体的位置都等于它的前一个身体单元的上一步 的位置,因此,可以从尾部进行循环,把前一节的位置给后一节。这样循环 ssize-1 次 就更新了身体,再根据输入的按键判断如何如移动头部,如果按键是左或者右, 只需将球的列加减 1,如果按键是上或者下,只需将球的行加减 1。至此,完成了 对球的显示位置的更新,之后重新清屏、显示,可以使球移动了。 在判断球是否向相反方向走时,可以采用如下算法:已知按键了(以向上为例),检查 球和身体第一节的行号,如果球行号大,说明此时设在向下运动,按键无效。 判断球撞到边界的算法如下(以向上键为例):判断球此时的行号是否为 0,如果是 0, 又按下了向上键,结束游戏,输出“I AM DEAD!!!”。如果球运动过程中撞到了自己, 也同撞到边界的操作。算法是这样的:取出球的位置,依次和每个身体和尾巴的位置 进行比较(从球开始比较),如果相等,说明撞上了,结束游戏。 如何实现球的自动移动。可以用 INT 10H 的 1 号功能检测是否有按键输入,如果有,转 到手工移动模块,否则,调用 INT 1AH 中的 00 号功能,读取当前时间。与上次读的时 间相比,如果小于设定的时间,重新比较,否则,球自动前移。实现前移的算法与手工 移动相似,也是将身体的某一单元的位置置成塔前一单元上一次的位置,之后判断球 方向。 产生并输出蛋。相当于产生一个随机数,我已经读去了当前时间,可以利用 DX 移位(防 止溢出)以后,对 80 和 25 取余,获得位置的随机数。之后判断球的大小 ssize 和 befor, 先让 ssize 为 14,befor 为 13,每产生一个随机数,befor 加 1,每吃一个蛋,ssize 加 1。比较 ssize 和 befor,如果相等,证明球没有吃蛋,不用产生新的随机数,仍在 原位置输出随机数,否则,产生新的随机数。球的大小的变化。每次吃到一个蛋后,ssize加1,并且将更新前球的位置加入到snake, 这样下次输出就能够多输出一个球,实现了大小的增加。 关卡的切换:每当球吃了一个蛋以后,判断是否达到了球的最大大小20,如果达到了, 继续判断是否的达到了等待时间的最小值(也就是最高的一关),如果没有达到,就更 新等待时间,进入下一关卡之前,还要将球的大小 ssize 和初始大小befor 分别设为 14 和 13。将记录球位置的内存 snake 的前 14 个字更新到屏幕中央。之后,就可以进入下 一关了。如果已经达到了最高关,并且通过了,就结束程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值