屏幕图象界面的艺术处理

Author: 王 涛 Date: 1995-01-06

        软件用户界面技术已经成为计算机技术中的一个重要分支,它除了计算机专业知识外,还包含了图形学、美术学、心理学等多种学科知识,是一门综合技术。良好界面的重要性也越来越引起人们的重视。对于一个用户来说,他所使用的软件首先呈现在他面前的是软件的界面,界面的好坏将会直接影响到用户的使用兴趣。正如现代社会的商品非常讲究包装、装璜一样,一个优秀的软件其界面也必然是很优秀的,这个软件也一定更易于推广、使用。良好的用户界面往往会给用户带来很美的艺术感受,使用户兴趣大增。对于软件开发者来说,若对软件的用户界面、软件封面等进行一些艺术化处理,构筑一个优美良好的界面环境,无疑会对整个软件起到画龙点睛的作用,收到极好的效果。下面就谈一谈这方面的一些问题。
        二、VGA 13H模式的硬件特性
        要设计出一个好的用户界面,需要有硬件和软件两方面的支持。对于硬件来说,PC机的图形显示系统经历了MDA、CGA、EGA、VGA等几代产品的发展。早期的显示系统分辩率低、 颜色数少、功能单一,不能满足用户日益增长的需求,随着EGA的出现使PC显示系统进入了一个新的领域。 特别是后来推出的VGA显示系统,以其丰富的色彩、较高的分辩率、良好的兼容性和优越的性能,现在已经成为了PC机的显示标准。
        标准VGA可以支持19种显示模式,最高分辩率达到640×480,最多可显示256K种颜色。其中模式13H分辨率为320×200, 可同时显示256种颜色,此模式下的图象丰富、自然、逼真,与普通彩色照片显示质量相差无几,所以受到越来越多的人的喜爱。众多软件开发者也在此模式下开发了大量的软件。本文就以此模式为基础,介绍在此模式下对屏幕图象界面进行艺术化处理的一些设计方法。
        首先,介绍VGA 13H模式下的屏幕缓冲区。
        VGA 13H模式采用线性内存结构,屏幕缓冲区起始地址为A0000H, 共占用64K字节。屏幕上所有象素按屏幕从左到右、从上到下与屏幕缓冲区地址形成一一对应。每个象素对应1个字节(8位),其ASCII值即为该象素所取的颜色号(0~255),因此,同一屏可显示最多256种不同的颜色。
        VGA的另一个重要特性是数模转换器DAC。DAC是VGA所特有的寄存器, 它能将色彩数字信号转换成模拟信号以驱动VGA的模拟显示器。因此VGA的色彩变化可以非常平滑, 很适合人的主观视觉感受。通过对DAC编程能够获得较为逼真的色彩效果。
        DAC包括三个转换寄存器(红、绿、兰各一个)。分别控制红、绿、兰三种基色的亮度值 (RGB值) 。每个寄存器占6位, 其亮度范围为0~63,3个寄存器组合总共可以配制643(266144)种颜色。同时,DAC必须从查色表中取得颜色编号以决定屏幕当前颜色,而查色表占8位,DAC一次只能从查色表中转换28(256)种颜色。所以DAC虽然能够配制643种颜色,但只能同时选取其中的256种颜色在屏幕上显示。
        对DAC的编程方法通常为I/O端口操作和BIOS功能调用,由于端口操作速度快,所以本文采用这种方法。DAC所使用的几个端口是:
        I/O地址  寄存器
        3C7         查色表读索引寄存器
        3C8         查色表写索引寄存器
        3C9         查色表数据寄存器
        当向端口3C7写入一个8位索引值(代表颜色号),再从端口3C9读出3个6位值取得该颜色号的RGB值。当向端口3C8写入一个8位索引值(代表颜色号) ,再向端口3C8写入3个6位值就相当于设置了该颜色号的RGB值。色彩变化就会立即显示在屏幕上。
        综上所述,要显示一幅VGA图象, 必须先定义各个彩色寄存器之值并写入DAC,然后再将图象数据送入屏幕缓冲区即可。
        三、艺术化处理屏幕图象界面的方法、技巧
        了解了以上知识,我们就可以设计出一些具有艺术化效果的屏幕图象界面了。下面分几个部分介绍其设计方法和编程技巧。(所用语言为TURBO PASCAL 6.0, 读者也可用其它语言如C语言、汇编语言编写)
        (一)汉字的显示:
        关于西文图形模式下的汉字显示,不少报刊都有过介绍,但大都是针对12H模式,即16色640×480分辩率模式。13H模式下的汉字显示比较容易实现,根据其屏幕缓冲区特性,可以知道,屏幕上坐标为(X,Y)的某个象素其对应的屏幕缓冲区偏移地址为:VADDR=320×Y+X。要在屏幕上显示汉字,只需将汉字的字模数据读出,依次取出各位,根据上面的公式,若值为1,则将汉字颜色值写入对应缓冲区地址;若为0,则将背景色值写入对应地址,重复若干次,即完成一个汉字在屏幕上的显示。由于直接对存储器操作,所以显示速度非常快。
        下面给出实现汉字显示的源程序:
        const get-point:array[1..8]of byte
        =(1,2,4,8,16,32,64,128);
        type hf=record
        re:array [1..32] of byte;
        end;
        PROCEDURE SHOWHZ(X,Y:INTEGER;HZSTR:STRING;color,bcolor:byte);
        BEGIN
        cc:=1;addr:=(y-1)*320+x+16;
        while cc<length(hzstr) do
        begin
        QM:=ord(hzstr[cc])-160;
        WM:=ord(hzstr[cc+1])-160;
        recno:=((qm-1)*94+wm)-1;
        seek(hzkfile,recno);
        read(hzkfile,cn);
        for i:=1 to 32 do
        begin
        if i mod 2<>0 then addr:=addr+320-16;
        for j:=8 downto 1 do
        begin
        pp:=getpoint[j] and cn.re[i];
        if pp=getpoint[j] then mem[$a000:addr]:=color
        else mem[$a000:addr]:=bcolor;
        addr:=addr+1;
        end;
        end;
        cc:=cc+2;addr:=addr-16*319;
        end;
        END;
        VGA 13H模式下的汉字显示, 虽然分辨率不太高,但颜色丰富、编写简单,所以非常适用于进行软件封面、图象界面的设计。利用上述方法,还很容易实现汉字放大、立体空心、旋转、倾斜等修饰功能,将会使用户界面变得更加非常美观、醒目。
        (二)图象的存储、显示:
        了解了VGA 13H模式的一些特性后, 我们可以自己编程设计一些图形、图象,但是手工编程往往比较复杂,效率不高,而且并不是人人都有较高的美术基础。另一方面,我们发现, 现在不少的软件如一些游戏软件中大都采用了VGA 13H显示模式,且不少图象都是利用扫描仪输入生成的,画面相当优美。如果我们充分利用现有资源,将这些图象画面以文件形式保存下来,加在我们自己设计的软件中,作为软件封面、菜单背景等,这样呈现在用户面前的屏幕将是充满了情趣、亲切感的。
        下面谈谈对图象进行存储、显示的设计方法。
        1.图象的存储:
        VGA 13H模式下的图象存储较容易实现。可以事先编制一个TSR内存驻留程序,程序运行后,遇到需保存的画面时,按下热键,击活程序,首先将当前DAC的256种颜色值保存到文件中,然后将屏幕缓冲区中的数据共64K保存到文件中。
        由于内存驻留技术已很成熟,所以下面只给出实现图象存储过程的程序:
        var pav:array[1..768] of byte;
        procedure savepic;
        var f1:file;
        result:integer;
        begin
        assign(f1,name);
        rewrite(f1,1);
        port[$3c7]:=0;
        for i:=1 to 768 do pav[i]:=port[$3c9];
        blockwrite(f1,pav,768);
        blockwrite(f1,mem[$a000:0000],64000,result);
        close(f1);
        end;
        2.图象的显示:
        图象显示实际上是图象存储的逆过程,实现起来并不复杂。但是如果将图象数据直接送屏幕缓冲区显示图象会显得很单调、呆板。如果我们将其显示过程作一些艺术化处理,采用动态过程显示,将会大大增强屏幕画面的美学艺术效果。笔者在工作中,摸索出这方面的一些技巧,现总结出以下几种方式:
        ①淡入、淡出式
        淡入、淡出显示技术是一种极具艺术效果的特技显示技术,现被很多游戏软件所采用。淡入是指图象由最暗逐渐增强亮度和饱和度,产生出图象由远至近,由暗到明缓缓升起的效果,非常生动、鲜明;而淡出则是淡入的逆过程,图象由明至暗逐渐消隐,直至消失。
        实现淡出的基本思想是,将当前DAC数据读取到缓冲区中, 然后将缓冲区中DAC数据的RGB值分别减1,再重新设置到DAC寄存器中,循环上述过程,直到RGB值全部减为0为止。淡入的实现过程则相反,首先将DAC数据置为全0,然后将RGB值分别加1,重新设置到DAC寄存器,循环上述过程,直到RGB值等于原DAC中的RGB值。
        按照上述方法在具体实现过程中发现,淡出过程完全正确,而淡入过程不十分理想。经过和淡出过程对比分析后发现, 淡入不理想的原因在于初始DAC值全为0,递增加1时是RGB值同时加1,结果成了每一种颜色的RGB值都相同,达不到原来DAC色彩的正常变化。所以正确的方法应该是, 先比较每种颜色RGB值的大小,只有最大值的那种基色才加1,重新设置到DAC寄存器,然后再将最大值减1,重复上述过程,直到RGB值等于原DAC中的RGB值。可见,在编程时,淡入过程并不完全是淡出过程的逆过程,还需要加一些修正语句。下面给出实现上述方法的源程序:
        type bt=array[1..64000]of byte;
        var  dac,plate:
        array[1..768]of byte;
        i,j,k,m:word;
        f1:file;
        result:integer;
        picdat:^bt;
        procedure vgaload;  {图象数据送缓冲区}
        begin
        port[$3c8]:=0;
        for i:=1 to 768 do port[$3c9]:=0;
        move(picdat^,mem[$a000:0],64000);
        end;
        procedure appear(ytime:word);  {淡入过程}
        var r,g,b:word;
        rok,gok,bok:boolean;
        begin
        for i:=1 to 768 do plate[i]:=0;
        for j:=0 to 63 do
        begin
        for i:=0 to 255 do
        begin
        r:=i*3+1;g:=i*3+2;b:=i*3+3;
        port[$3c8]:=i;
        port[$3c9]:=plate[r];
        port[$3c9]:=plate[g];
        port[$3c9]:=plate[b];
        if (plate[r]<dac[r]) and (dac[r]>=dac[b]-plate[b]) 
        and (dac[r]>=dac[g]-plate[g]) then rok:=true else rok:=false;
        if (plate[g]<dac[g]) and (dac[g]>=dac[r]-plate[r])
        and (dac[g]>=dac[b]-plate[b]) then gok:=true else gok:=false;
        if (plate[b]<dac[b]) and (dac[b]>=dac[r]-plate[r])
        and (dac[b]>=dac[g]-plate[g]) then bok:=true else bok:=false;
        if rok then plate[r]:=plate[r]+1;
        if gok then plate[g]:=plate[g]+1;
        if bok then plate[b]:=plate[b]+1;
        end;
        delay(ytime);   {延时}
        end;
        end;
        procedure disappear(ytime:word);  {淡出过程}
        begin
        for j:=63 downto 0 do
        begin
        for i:=0 to 255 do
        begin
        port[$3c8]:=i;
        port[$3c9]:=plate[i*3+1];
        port[$3c9]:=plate[i*3+2];
        port[$3c9]:=plate[i*3+3];
        if plate[i*3+1]>0 then plate[i*3+1]:=plate[i*3+1]-1;
        if plate[i*3+2]>0 then plate[i*3+2]:=plate[i*3+2]-1;
        if plate[i*3+3]>0 then plate[i*3+3]:=plate[i*3+3]-1;
        end;
        delay(ytime);
        end;
        end;
        ②百页窗式
        此方式是模拟百页窗,将图象画面划分成若干个平行横栏,然后象拉百页窗一样,一栏一栏地将画面展开,直到画面占满屏幕。画面清除过程相反,尤如关闭百页窗一样,一栏一栏地消失。
        此方式非常美观,富有情趣。源程序如下:
        procedure bwin;
        begin     {展开}
        for i:=0 to 9 do
        begin
        for j:=0 to 19 do
        for k:=0 to 319 do
        mem[$a000:(i+j*10)*320+k]:=picdat^[(i+j*10)*320+k+1];
        delay(180);
        end;
        ch:=readkey;
        for i:=0 to 9 do  {关闭}
        begin
        for j:=0 to 19 do
        for k:=0 to 319 do
        mem[$a000:(i+j*10)*320+k]:=0;
        delay(280);
        end;
        end;
        ③帷幕落下式
        此方式是模拟舞台帷幕落下,画面从屏幕顶端冒出,自顶向下,逐渐平滑下落,直至屏幕底端。此方式生动、形象。源程序如下:
        procedure screendecline;
        begin
        for i:=0 to 199 do
        begin
        for j:=0 to i do
        move(picdat^[(199-j)*320+1],mem[$a000:(i-j)*320],320);
        delay(40);
        end;
        ch:=readkey;
        end;
        ④雨点下坠式
        此方式模拟雨点落下,画面自顶向下下落过程中,尤如流星雨样,每一个象素均有一长长的尾巴拖动痕迹,直到下坠至屏幕底端。
        此方式极具动感效果,非常醒目。源程序如下:
        procedure raindrop;
        begin
        for i:=199 downto 0 do
        begin
        for j:=0 to i do
        move(picdat^[i*320+1],mem[$a000:j*320],320);
        delay(20);
        end;
        ch:=readkey;
        end;
        ⑤中心展开式
        此方式下,画面自屏幕中心逐层向四周展开,直至充满屏幕。有一种由远至近、由小变大的视觉感受。源程序如下:
        procedure spread;
        var x,y:word;
        begin
        x:=160;y:=100;
        for i:=0 to 100 do
        begin
        for j:=x-i to x+i do mem[$a000:(y-i)*320+j]:=picdat^[(y-i)*320+j+1];
        for j:=x-i to x+i do mem[$a000:(y+i)*320+j]:=picdat^[(y+i)*320+j+1];
        for j:=y-i to y+i do mem[$a000:j*320+x+i]:=picdat^[j*320+x+i+1];
        for j:=y-i to y+i do mem[$a000:j*320+x-i]:=picdat^[j*320+x-i+1];
        delay(50);
        end;
        for i:=101 to 160 do
        begin
        for j:=0 to 199 do mem[$a000:j*320+x+i]:=picdat^[j*320+x+i+1];
        for j:=0 to 199 do mem[$a000:j*320+x-i]:=picdat^[j*320+x-i+1];
        delay(50);
        end;
        ch:=readkey;
        end;
        四、结束语
        高性能的VGA显示系统为我们提供了良好的硬件基础, 在此系统下,将软件中的屏幕图象界面作一些艺术化处理,将会使界面更加美观、生动、形象、友善,增加了用户的使用兴趣,使软件更具魅力。
        本文对此在方法和技巧上作了一些探索,由于水平有限,不妥之处在所难免,目的旨在抛砖引玉,相信读者只要充分发挥自己的想象力和创造力,一定会设计出更多、更美的图象界面,促进软件技术的普及、发展。