屏幕图象界面的艺术处理
二、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个寄存器组合总共可以配制643(266144)种颜色。同时,DAC必须从查色表中取得颜色编号以决定屏幕当前颜色,而查色表占8位,DAC一次只能从查色表中转换28(256)种颜色。所以DAC虽然能够配制643种颜色,但只能同时选取其中的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显示系统为我们提供了良好的硬件基础, 在此系统下,将软件中的屏幕图象界面作一些艺术化处理,将会使界面更加美观、生动、形象、友善,增加了用户的使用兴趣,使软件更具魅力。
本文对此在方法和技巧上作了一些探索,由于水平有限,不妥之处在所难免,目的旨在抛砖引玉,相信读者只要充分发挥自己的想象力和创造力,一定会设计出更多、更美的图象界面,促进软件技术的普及、发展。