4 图片阅览器(harib27d)
话说,到现在为止的几个应用程序,都比昨天的invader.hrb要小,感觉非常对不起今天这一章的标题(高级的应用程序)。不过,体积大也未必说明它比较高级。虽说如此,我们还是来做一个规模大一点的应用程序吧。
其实invader.hrb之所以比较大,并不是因为内容比较高级,而是因为使用了sprintf函数。sprintf是一个很大的函数,如果可以避免使用这个函数的话,大约又可以缩小500多字节。
于是我们来做一个图片阅览器吧。用这一个程序,就可以查看BMP和JPEG两种格式的图片。我们先来看看实际运行的画面。
![]() | ![]() | |
“gview night.bmp” | “gview fujisan.jpg” |
怎么样,显示效果不错吧?呵呵,小菜一碟(笑)。
关于程序,如果要给大家讲解BMP和JPEG的文件格式的话,篇幅又要变得很~长了,于是我们只好又省略了。而且用来解释BMP和JPEG文件格式的程序我们也不重新编写了,而是直接使用OSASK中的应用程序所引用的代码。
OSASK中有一个叫做PICTURE0.BIN的应用程序,这就是一个用来查看BMP和JPEG格式图片的图片阅览器。从这个程序的源代码中,我们提取了bmp.nasm和jpeg.c(这剧情怎么跟tek那时候差不多啊)。
这两个源程序看起来无需修改就可以直接使用,其中bmp.nasm的作者是I.Tak.,jpeg.c的作者是nikq、笔者、I.Tak.和Kumin。在这里要向I.Tak.、nikq和Kumin表示感谢,这个应用程序能很快编写出来都是你们的功劳。
于是,剩下的只需要载入文件并显示出来而已,这些程序刷刷刷就写出来了,放在这里了哦(关于bmp.nasm和jpeg.c的源代码,请大家查阅附送的光盘)。
gview.c
#include "apilib.h"
struct DLL_STRPICENV { /* 64KB */
int work[64 * 1024 / 4];
};
struct RGB {
unsigned char b, g, r, t;
};
/* bmp.nasm */
int info_BMP(struct DLL_STRPICENV *env, int *info, int size, char *fp);
int decode0_BMP(struct DLL_STRPICENV *env, int size, char *fp, int b_type, char *buf, int skip);
/* jpeg.c */
int info_JPEG(struct DLL_STRPICENV *env, int *info, int size, char *fp);
int decode0_JPEG(struct DLL_STRPICENV *env, int size, char *fp, int b_type, char *buf, int skip);
unsigned char rgb2pal(int r, int g, int b, int x, int y);
void error(char *s);
void HariMain(void)
{
struct DLL_STRPICENV env;
char filebuf[512 * 1024], winbuf[1040 * 805];
char s[32], *p;
int win, i, j, fsize, xsize, info[8];
struct RGB picbuf[1024 * 768], *q;
/*命令行解析*/
api_cmdline(s, 30);
for (p = s; *p > ' '; p++) { } /*一直读到空格为止*/
for (; *p == ' '; p++) { } /*跳过空格*/
/*文件载入*/
i = api_fopen(p); if (i == 0) { error("file not found.\n"); }
fsize = api_fsize(i, 0);
if (fsize > 512 * 1024) {
error("file too large.\n");
}
api_fread(filebuf, fsize, i);
api_fclose(i);
/*检查文件类型*/
if (info_BMP(&env, info, fsize, filebuf) == 0) {
/*不是BMP */
if (info_JPEG(&env, info, fsize, filebuf) == 0) {
/*也不是JPEG */
api_putstr0("file type unknown.\n");
api_end();
}
}
/*上面其中一个info函数调用成功的话,info中包含以下信息 */
/*info[0]:文件类型(1:BMP、2:JPEG)*/
/*info[1]:颜色数信息*/
/*info[2]:xsize */
/*info[3]:ysize */
if (info[2] > 1024 || info[3] > 768) {
error("picture too large.\n");
}
/*窗口准备*/
xsize = info[2] + 16;
if (xsize < 136) {
xsize = 136;
}
win = api_openwin(winbuf, xsize, info[3] + 37, -1, "gview");
/*将文件内容转换为图像数据*/
if (info[0] == 1) {
i = decode0_BMP (&env, fsize, filebuf, 4, (char *) picbuf, 0);
} else {
i = decode0_JPEG(&env, fsize, filebuf, 4, (char *) picbuf, 0);
}
/*b_type = 4表示struct RGB格式*/
/*skip设为0即可*/
if (i != 0) {
error("decode error.\n");
}
/*显示*/
for (i = 0; i < info[3]; i++) {
p = winbuf + (i + 29) * xsize + (xsize - info[2]) / 2;
q = picbuf + i * info[2];
for (j = 0; j < info[2]; j++) {
p[j] = rgb2pal(q[j].r, q[j].g, q[j].b, j, i);
}
}
api_refreshwin(win, (xsize - info[2]) / 2, 29, (xsize - info[2]) / 2 + info[2], 29 + info[3]);
/*等待结束*/
for (;;) {
i = api_getkey(1);
if (i == 'Q' || i == 'q') {
api_end();
}
}
}
unsigned char rgb2pal(int r, int g, int b, int x, int y)
{
static int table[4] = { 3, 1, 0, 2 };
int i;
x &= 1; /*判断是偶数还是奇数*/
y &= 1;
i = table[x + y * 2]; /*用于生成中间色的常量*/
r = (r * 21) / 256; /*结果为0~20*/
g = (g * 21) / 256;
b = (b * 21) / 256;
r = (r + i) / 4; /*结果为0~5*/
g = (g + i) / 4;
b = (b + i) / 4;
return 16 + r + g * 6 + b * 36;
}
void error(char *s)
{
api_putstr0(s);
api_end();
}
比较难懂的地方……大概就是info_BMP这里了吧。info_BMP是用来解释BMP文件格式,并获取图片分辨率等信息的函数。其中env是info_BMP工作用的内存空间(必须确保有64KB的空间)。另外,decode0_BMP函数用来读取BMP文件,并转换为统一的容易显示的形式。info_JPEG等函数就是对JPEG文件执行上述操作的版本,功能和上述函数都是相同的。
关于变量info,gview.c中只使用到了info[3],看起来貌似声明到info[4]就足够了,不过info_BMP等函数需要任意使用info[4~7]的内存空间,如果不声明的话可能不会正常工作,因此我们还是声明为info[8]。
rgb2pal就是直接用了color2.hrb中的代码,有了这一算法fujisan.jpg也可以显示得很漂亮了。
■■■■■
关于bmp.nasm这里要稍微说明一下。这个程序是针对NASM这个汇编器编写的,用笔者准备的工具是无法进行汇编的。没办法,笔者向该程序的作者I.Tak.索取了汇编后生成的bmp.obj1,只要有了.obj文件就可以进行连接,这样就OK了。
1 索取了bmp.obj:由于觉得NASM写的源代码各位读者可能比较难懂,笔者本来准备将这段程序整个用C语言改写一遍,不过由于本书面临截稿,便没能实现这一设想,十分抱歉。至少能将格式比较简单的BMP改写成比较易懂的形式也好啊……
另外,在编译jpeg.c的时候,不知为何会出现很多警告信息,非常抱歉。不过jpeg.c即便忽略这些警告信息也没有什么问题,请大家不要在意就是了。
night.bmp和fujisan.jpg这两个文件都使用了tek进行压缩。如果想要边看night.bmp边听音乐的话,笔者推荐kirakira.mml。至于fujisan.jpg的音乐……用不着笔者告诉大家了吧(笑)。
最终生成的gview.hrb,大小为3865字节,即3.77KB,我们终于做出了比昨天的invader.hrb更大的应用程序了呢(笑)。不过即便如此,这样一个图片阅览器只要3.75KB,“纸娃娃系统”真是个不错的系统呢。