4 图片阅览器(harib27d)

话说,到现在为止的几个应用程序,都比昨天的invader.hrb要小,感觉非常对不起今天这一章的标题(高级的应用程序)。不过,体积大也未必说明它比较高级。虽说如此,我们还是来做一个规模大一点的应用程序吧。

其实invader.hrb之所以比较大,并不是因为内容比较高级,而是因为使用了sprintf函数。sprintf是一个很大的函数,如果可以避免使用这个函数的话,大约又可以缩小500多字节。

于是我们来做一个图片阅览器吧。用这一个程序,就可以查看BMP和JPEG两种格式的图片。我们先来看看实际运行的画面。

4 图片阅览器(harib27d) - 图1 4 图片阅览器(harib27d) - 图2
“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

  1. #include "apilib.h"
  2. struct DLL_STRPICENV { /* 64KB */
  3. int work[64 * 1024 / 4];
  4. };
  5. struct RGB {
  6. unsigned char b, g, r, t;
  7. };
  8. /* bmp.nasm */
  9. int info_BMP(struct DLL_STRPICENV *env, int *info, int size, char *fp);
  10. int decode0_BMP(struct DLL_STRPICENV *env, int size, char *fp, int b_type, char *buf, int skip);
  11. /* jpeg.c */
  12. int info_JPEG(struct DLL_STRPICENV *env, int *info, int size, char *fp);
  13. int decode0_JPEG(struct DLL_STRPICENV *env, int size, char *fp, int b_type, char *buf, int skip);
  14. unsigned char rgb2pal(int r, int g, int b, int x, int y);
  15. void error(char *s);
  16. void HariMain(void)
  17. {
  18. struct DLL_STRPICENV env;
  19. char filebuf[512 * 1024], winbuf[1040 * 805];
  20. char s[32], *p;
  21. int win, i, j, fsize, xsize, info[8];
  22. struct RGB picbuf[1024 * 768], *q;
  23. /*命令行解析*/
  24. api_cmdline(s, 30);
  25. for (p = s; *p > ' '; p++) { } /*一直读到空格为止*/
  26. for (; *p == ' '; p++) { } /*跳过空格*/
  27. /*文件载入*/
  28. i = api_fopen(p); if (i == 0) { error("file not found.\n"); }
  29. fsize = api_fsize(i, 0);
  30. if (fsize > 512 * 1024) {
  31. error("file too large.\n");
  32. }
  33. api_fread(filebuf, fsize, i);
  34. api_fclose(i);
  35. /*检查文件类型*/
  36. if (info_BMP(&env, info, fsize, filebuf) == 0) {
  37. /*不是BMP */
  38. if (info_JPEG(&env, info, fsize, filebuf) == 0) {
  39. /*也不是JPEG */
  40. api_putstr0("file type unknown.\n");
  41. api_end();
  42. }
  43. }
  44. /*上面其中一个info函数调用成功的话,info中包含以下信息 */
  45. /*info[0]:文件类型(1:BMP、2:JPEG)*/
  46. /*info[1]:颜色数信息*/
  47. /*info[2]:xsize */
  48. /*info[3]:ysize */
  49. if (info[2] > 1024 || info[3] > 768) {
  50. error("picture too large.\n");
  51. }
  52. /*窗口准备*/
  53. xsize = info[2] + 16;
  54. if (xsize < 136) {
  55. xsize = 136;
  56. }
  57. win = api_openwin(winbuf, xsize, info[3] + 37, -1, "gview");
  58. /*将文件内容转换为图像数据*/
  59. if (info[0] == 1) {
  60. i = decode0_BMP (&env, fsize, filebuf, 4, (char *) picbuf, 0);
  61. } else {
  62. i = decode0_JPEG(&env, fsize, filebuf, 4, (char *) picbuf, 0);
  63. }
  64. /*b_type = 4表示struct RGB格式*/
  65. /*skip设为0即可*/
  66. if (i != 0) {
  67. error("decode error.\n");
  68. }
  69. /*显示*/
  70. for (i = 0; i < info[3]; i++) {
  71. p = winbuf + (i + 29) * xsize + (xsize - info[2]) / 2;
  72. q = picbuf + i * info[2];
  73. for (j = 0; j < info[2]; j++) {
  74. p[j] = rgb2pal(q[j].r, q[j].g, q[j].b, j, i);
  75. }
  76. }
  77. api_refreshwin(win, (xsize - info[2]) / 2, 29, (xsize - info[2]) / 2 + info[2], 29 + info[3]);
  78. /*等待结束*/
  79. for (;;) {
  80. i = api_getkey(1);
  81. if (i == 'Q' || i == 'q') {
  82. api_end();
  83. }
  84. }
  85. }
  86. unsigned char rgb2pal(int r, int g, int b, int x, int y)
  87. {
  88. static int table[4] = { 3, 1, 0, 2 };
  89. int i;
  90. x &= 1; /*判断是偶数还是奇数*/
  91. y &= 1;
  92. i = table[x + y * 2]; /*用于生成中间色的常量*/
  93. r = (r * 21) / 256; /*结果为0~20*/
  94. g = (g * 21) / 256;
  95. b = (b * 21) / 256;
  96. r = (r + i) / 4; /*结果为0~5*/
  97. g = (g + i) / 4;
  98. b = (b + i) / 4;
  99. return 16 + r + g * 6 + b * 36;
  100. }
  101. void error(char *s)
  102. {
  103. api_putstr0(s);
  104. api_end();
  105. }

比较难懂的地方……大概就是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,“纸娃娃系统”真是个不错的系统呢。