直接控制显存

位图结构看起来是这样的:

typedef struct BITMAP
{
   int w, h;               - 以点为单位的位图大小
   int clip;               - 如果剪裁打开了,它为一非零值
   int cl, cr, ct, cb;     - 剪裁矩形左,右,上,下
   int seg;                - 行指针用到的段
   unsigned char *line[];  - 每一行的开始指针数组
} BITMAP;

结构里还有些其它的东东, 但是不能随便的改变, 你不能使用除上面外的任何东东. 剪裁矩形 包括了上和左边界 (0 允许画到位置 0) 但是排除了下和右边界(10 允许画到位置 9, 不是 10). 注意它和你传入 set_clip() 的格式不同, 那个包括了所有四个边界.

有好几种方法来控制位图的用到的内存, 根据你使用的位图类型而有很大的不同.


最容易的是只工作在内存位图上 (从 create_bitmap(), grabber 的 数据文件, 和图片文件得到的) 和内存位图的 子位图. 这个使用了一个 char 指针的表, 被称做 'line', 它是位图结构的一部分, 包含了图片每一行的开始指针. 例如, 一个简单的内存位图的 putpixel 函数是:

   void memory_putpixel(BITMAP *bmp, int x, int y, int color)
   {
      bmp->line[y][x] = color;
   }


真彩模式下你将 line 指针设置成合适的类型, 例如:

   void memory_putpixel_15_or_16_bpp(BITMAP *bmp, int x, int y, int color)
   {
      ((short *)bmp->line[y])[x] = color;
   }

void memory_putpixel_32(BITMAP *bmp, int x, int y, int color) { ((long *)bmp->line[y])[x] = color; }


如果你想像操作内存位图那样写向屏幕, 就需要使用 far 指针. 用 sys/farptr.h 里的例程重写上面的函数 将是得它能使用在屏幕位图, 但是先决条件是不需要块切换 (就是指 13h 模式屏幕和线形页缓冲的 SVGA 屏幕). 使用 far 指针的 putpixel 位:

   #include <sys/farptr.h>

void farptr_putpixel(BITMAP *bmp, int x, int y, int color) { _farpokeb(bmp->seg, (unsigned long)bmp->line[y]+x, color); }

对于真彩模式,显而易见的是,你将 _farpokeb() 换成 _farpokew() 或 _farpokel(), 并将 x 偏移量乘上每个点的字节数.



然而这还不能在分块的 SVGA 模式下使用. 为了绕道控制位图内存, 你需要调用块切换函数:


unsigned long bmp_write_line(BITMAP *bmp, int line);
选择你将作图的位图行.

unsigned long bmp_read_line(BITMAP *bmp, int line);
选择你将读取的位图行.

这些是在线汇编写的例程, 因此它们并不像他们看起来的那么低效率. 如果位图不需要块切换, (即: 它是一个内存位图, 13h 模式屏幕, 等等), 这些函数只是返回 bmp->line[line].

虽然 SVGA 位图是分块的, Allegro 提供了每个扫描行的线形控制, 因此你仅仅需要将 y 轴传入这些函数. x 位置变量可以简单的将 x 坐标简单的加上返回地址得到. 返回值是一个无符号长整形 而不是一个字符指针,这是因为位图内存不一定在你的数据段内, 而你需要用 far 指针来控制它. 例如: 使用块切换的 putpixel 函数为:

   #include <sys/farptr.h>

void banked_putpixel(BITMAP *b, int x, int y, int color) { unsigned long address = bmp_write_line(bmp, y); _farpokeb(bmp->seg, address+x, color); }

你将注意到 Allegro 提供了分开的读写块的函数. 辨认它们对你很重要, 因为一些图形卡可以分别设置读写块, 而在不同的地址读写显存. 然而生活从来不象我们希望的那样简单 (这可是真理, 即使我们 _不_ 去谈论图形编程 :-) 一些卡仅仅提供单一的块. 在这些卡上读写块函数 将做同样的时, 因此你不能假定你可以 从显存的一部分读而在同时向另一部分写. 你可以调用 bmp_read_line(), 读你想在这一行读的任何东东, 然后 以相同或不同的行数字调用 bmp_write_line() , 写你想在这第二个行上想写的任何东东, 但是你绝对不能同时调用 bmp_read_line() 和 bmp_write_line() 来是你从一行读而同时写向另一行. 如果这有可能,很漂亮, 但如果你这样做了, 你的代码就只能工作在单一块的 SVGA 卡上了.


下面是 mode-X. 如果你从来没有对任何的 mode-X 图形编程过, 可能你并不理解它, 但是这儿有为 想了解 Allegro 怎样设置 mode-X 屏幕位图的你写的东东, 从这儿开始...

line 指针依然有, 它们包含了平面地址, 即: 你控制的行的第一点的实际地址. 这些地址被保证为 4 对齐, 因此你可以将 x 坐标除以四, 加在行指针上, 设置为写平面. 例如: mode-X 的 putpixel 为:

   #include <pc.h>
   #include <sys/farptr.h>

void modex_putpixel(BITMAP *b, int x, int y, int color) { outportw(0x3C4, (0x100<<(x&3))|2); _farpokeb(bmp->seg, (unsigned long)bmp->line[y]+(x>>2), color); }


Oh 对了: 用 nearptr. 我个人不是很喜欢这种方式, 但是许多人发誓要用它 因为它是你可以通过正规 C 指针来控制屏幕内存. 警告:这个方法只能工作于 VGA 13h 模式和线形页缓冲模式!

用你设置的代码:

   #include <sys/nearptr.h>

unsigned char *screenmemory; unsigned long screen_base_addr;

__djgpp_nearptr_enable();

__dpmi_get_segment_base_address(screen->seg, &screen_base_addr);

screenmemory = (unsigned char *)(screen_base_addr + screen->line[0] - __djgpp_base_address);

然后:

   void nearptr_putpixel(int x, int y, int color)
   {
      screenmemory[x + y*SCREEN_W] = color;
   }




返回