C for Yourself - part 40

Mastering manual screen redrawing Steve Mumford explains

As I mentioned in passing last time round, a crucial part of the window-redrawing process involves the translation of internal coordinates relative to the workspace of a window into points that fit within the standard boundaries of the screen. For instance, imagine a window containing a solitary sprite. Its position is measured from the top left corner of the window's workspace as a pair of x and y displacement values (so x tends to be positive whereas y is negative).

However, because the window may only be displaying a portion of its workspace at any one time, several more parameters have to be used to describe its appearance on-screen.

We need to record the offset between the top left hand corner of the workspace and the first pixel that's actually visible through the window, and finally we need to know the coordinates of the top left corner of the window, as measured from the bottom left corner of the screen, Take a look at the diagram; it should help to make things a little clearer.

In order to convert from workspace coordinates to screen coordinates, we can use the following relationships:

screen_x = work x - scroll_x + x_min
screen_y = work_y - scroll_y + y_max

In the example on the cover disc I've used these equations within the redraw_window() function to calculate the screen coordinates for five sprites so they can be plotted at the same position relative to the window's work area no matter where the window may be on screen. Conversely, to convert a screen coordinate into one relative to the work area, the relationships can be rearranged to give:

work_x = screen_x + scroll_x - x_min
work_y = screen_y + scroll_y - y_max

The blocks returned by the Wimp_RedrawWindow and Wimp_GetRectangle system calls share the same format. On entry, register 1 should hold the address of the data block, and the first word of that block (bytes 0 to 3) contains the window handle.

Normally WimpPoll has filled this in so it's a matter of calling Wimp_RedrawWindow with the returned poll block. When the system call returns, register 0 is set to indicate whether another redraw is necessary, and if it's set to zero, no more areas of the window need attention.

The next four words in the data block (from bytes 4 to 19) contain the minimum x and y values between the screen origin and the window in question, followed by their maximum values.

The two words that follow (bytes 20 to 27) hold the x and y scroll offsets. Using the au_bytetoword() function, that converts four bytes from a raw data block into a C long integer, we can collect the values we need for the above equations as follows:

x_min = au_bytetoword(pollblock, 4);
y_max = au_bytetoword(pollblock, 16);
scroll_x = au_bytetoword(pollblock, 20);
scroll_y = au_bytetoword(pollblock, 24);

The last four words in the block (from bytes 28 to 43) store the minimum x and y screen coordinates, followed by their maxima, of the current rectangle being redrawn. After calling Wimp_GetRectangle, the graphics window is set to enclose this rectangle, so anything plotted outside those coordinates won't actually appear on screen.

If your task is complex and redraw speed is an issue, take a note of these values and only redraw those objects in the window that fall within this range, making the whole process that much more efficient.

The example application on the cover disc performs one other useful function - the function wimpmsg() listens out for the MESSAGE_MODECHANGE_WIMP message, and upon hearing it, the program recalculates the pixel translation tables being used to plot the sprites in the window.

All this means in practice is the sprites are plotted properly no matter what screen mode is being used, and if a mode with fewer colours than the source sprite is used, colours are picked from the reduced palette as appropriate. A simple function is provided to make the process of building a translation table easier once a block of memory has been set aside, a call to build translation_table(), with the name of the sprite and the location of the buffer, will do it all:

unsigned char translation_buffer[1024];
build_translation_table("testspr", translation_buffer);

That's about it for this month, but now we've broached the subject of manual screen redraw, there are many more problems we can tackle. I'll be examining some of the possibilities next time.


Source: Acorn User - 184 - August 1997
Publication: Acorn User
Contributor: Steve Mumford