Direct access to video memory
Read chapter "Structures and types defined by Allegro" for an internal
description of the BITMAP structure. There are several ways to get direct
access to the image memory of a bitmap, varying in complexity depending on
what sort of bitmap you are using.
The simplest approach will only work with memory bitmaps (obtained from
create_bitmap(), grabber datafiles, and image files) and sub-bitmaps of
memory bitmaps. This uses a table of char pointers, called `line', which is
a part of the bitmap structure and contains pointers to the start of each
line of the image. For example, a simple memory bitmap putpixel function is:
void memory_putpixel(BITMAP *bmp, int x, int y, int color)
{
bmp->line[y][x] = color;
}
For truecolor modes you need to cast the line pointer to the appropriate
type, for example:
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;
}
If you want to write to the screen as well as to memory bitmaps, you need to
use some helper macros, because the video memory may not be part of your
normal address space. This simple routine will work for any linear screen,
eg. a VESA linear framebuffers:
void linear_screen_putpixel(BITMAP *bmp, int x, int y, int color)
{
bmp_select(bmp);
bmp_write8((unsigned long)bmp->line[y]+x, color);
}
For truecolor modes you should replace the bmp_write8() with bmp_write16(),
bmp_write24(), or bmp_write32(), and multiply the x offset by the number of
bytes per pixel. There are of course similar functions to read a pixel value
from a bitmap, namely bmp_read8(), bmp_read16(), bmp_read24() and
bmp_read32().
This still won't work in banked SVGA modes, however, or on platforms like
Windows that do special processing inside the bank switching functions. For
more flexible access to bitmap memory, you need to call the following
routines. They are implemented as inline assembler routines, so they are not
as inefficient as they might seem. If the bitmap doesn't require bank
switching (ie. it is a memory bitmap, mode 13h screen, etc), these functions
just return bmp->line[line].
More on banked direct memory access
Although SVGA bitmaps are banked, Allegro provides linear access to the
memory within each scanline, so you only need to pass a y coordinate to
these functions. Various x positions can be obtained by simply adding the x
coordinate to the returned address. The return value is an unsigned long
rather than a char pointer because the bitmap memory may not be in your data
segment, and you need to access it with far pointers. For example, a
putpixel using the bank switching functions is:
void banked_putpixel(BITMAP *bmp, int x, int y, int color)
{
unsigned long address = bmp_write_line(bmp, y);
bmp_select(bmp);
bmp_write8(address+x, color);
bmp_unwrite_line(bmp);
}
You will notice that Allegro provides separate functions for setting the
read and write banks. It is important that you distinguish between these,
because on some graphics cards the banks can be set individually, and on
others the video memory is read and written at different addresses. Life is
never quite as simple as we might wish it to be, though (this is true even
when we _aren't_ talking about graphics coding :-) and so of course some
cards only provide a single bank. On these the read and write bank functions
will behave identically, so you shouldn't assume that you can read from one
part of video memory and write to another at the same time. You can call
bmp_read_line(), and read whatever you like from that line, and then call
bmp_write_line() with the same or a different line number, and write
whatever you like to this second line, but you mustn't call bmp_read_line()
and bmp_write_line() together and expect to be able to read one line and
write the other simultaneously. It would be nice if this was possible, but
if you do it, your code won't work on single banked SVGA cards.
And then there's mode-X. If you've never done any mode-X graphics coding,
you probably won't understand this, but for those of you who want to know
how Allegro sets up the mode-X screen bitmaps, here goes...
The line pointers are still present, and they contain planar addresses, ie.
the actual location at which you access the first pixel in the line. These
addresses are guaranteed to be quad aligned, so you can just set the write
plane, divide your x coordinate by four, and add it to the line pointer. For
example, a mode-X putpixel is:
void modex_putpixel(BITMAP *b, int x, int y, int color)
{
outportw(0x3C4, (0x100<<(x&3))|2);
bmp_select(bmp);
bmp_write8((unsigned long)bmp->line[y]+(x>>2), color);
}
Oh yeah: the DJGPP nearptr hack. Personally I don't like this very much
because it disables memory protection and isn't portable to other platforms,
but a lot of people swear by it because it can give you direct access to the
screen memory via a normal C pointer. Warning: this method will only work
with the DJGPP library, when using VGA 13h or a linear framebuffer modes!
In your setup code:
#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);
Then:
void nearptr_putpixel(int x, int y, int color)
{
screenmemory[x + y*VIRTUAL_W] = color;
}