Shades of gray, on lcd monochrome displays, can be obtained by turning on/off
pixels according to some pattern. Different patterns will show different gray
levels.
Patterns are obtained by bitplanes and the order they are displayed.
A bitplane is a monochrome bitmap. Using two bitplanes allows us to assign two bits
each pixel, one on the first plane, one on the second plane. Two bits allow us for
four shades of gray. 00 - white, 01 - light gray, 10 dark gray, 11 black.
We start to place these planes on the time axis. How about the order? Plane1, Plane2,
Plane1, Plane2, and so on? This wouldn't work. Every plane would stay "on" half the
time, so '10' and '01' would be the same level. We would end up getting only three
levels.
What we need is to give different weight to different planes. On each period, the
darker plane must be showed more.
P2, P1, P2, P1, P2. This sounds good. If the period is T, the first plane stays on for
(2/5) * T and the second plane (3/5) * T. This makes '10' and '01' combination different.
Notice that this is not a pattern. We get a pattern assigning this model a combination
of bits. If this sequence is repeated fast enough, our eyes get tricked. They perform
what is called a temporal integration.
Plane2 (3/5) | Plane1 (2/5) | Result |
0 | 0 | white 0 |
0 | 1 | light gray 2/5 |
1 | 0 | dark gray3/5 |
1 | 1 | black 5/5 |
It's fundamental that plane sequences are mixed in a uniform way.
Flicker. Now if we're not fast enough, it's likely we see the image flashing.
More planes -> more bytes to display -> more flicker. This may not be the only
reason. But BE romantic, a bit of flicker adds that little something to a picture.
The code shows a 16 gray levels picture viewer.
Show Asm Code
Variables. Here we have TCHSTS structure wearing its assembly dress.
DATA DSEG
tsts:
obj: dw 0
act: dw 0, 0
x: dw 0
y: dw 0
ext: dw 0
istr: dw 0
Focus your attention to 'planes' and 'order16'. 'Order16' defines the display order for
the planes. 'Counter' is its index. Notice that 'order16' doesn't store the frames
pointers, just indexes. Those indexes point to 'planes' which contains the frames
pointers. Notice that this implementation considers the plane, whose index is 0,
the darkest plane.
pLo: dw 0
pHi: dw 0
FRAMES equ 14
planes: dw 0x2580, 0x1900, 0x0C80, 0x0000
order16: dw 0, 1, 0, 2, 0, 3, 0, 1, 0, 2, 0, 1, 0
counter: dw 0
; ------------------------------------------------------------
GRAYPROC_TEXT CSEG
.186
Do you recognize that? PollEvent!!!!
;------------------------------------------------------------
; CheckTouch --> ax = 1, touched
;------------------------------------------------------------
CheckTouch:
mov ax, 0x0201
lea di, [tsts]
push ds
pop es
int 0x50
cmp [x].w, 0xFFFF
jne ct_true
cmp [y].w, 0xFFFF
jne ct_true
xor ax, ax
ret
ct_true:
mov ax, 1
ret
SetPlane copies the frame pointed by ds:[si] to the screen 0xF000:[0x0100]
;------------------------------------------------------------
; SetPlane (si = plane offset)
;------------------------------------------------------------
SetPlane:
push ds
add si, [pLo]
mov ax, [pHi]
mov ds, ax
mov ax, 0xF000
mov di, 0x0100
mov es, ax
mov cx, 1600
rep movsw
pop ds
ret
BX/AX is 'byte far *Picture'. We save this pointer. On every loop we give the user
a chance to exit by CheckingTouch.
;------------------------------------------------------------
; Grayscale(byte far *Picture)
;------------------------------------------------------------
Grayscale_::
mov [pLo], ax
mov [pHi], bx
xor ax, ax
mov [counter], ax
gv_loop:
call CheckTouch
cmp ax, 0
je gv_goon
retf
This does the translation counter -> frame and displays the selected frame.
gv_goon:
mov bx, [counter]
push bx
shl bx, 1
mov di, [order16 + bx]
shl di, 1
mov si, [planes + di]
call SetPlane
pop bx
inc bx
cmp bx, FRAMES-1
jne gv_1
xor bx, bx
gv_1:
mov [counter], bx
jmp gv_loop
Show C Code
The main program. Not much to say. The grayscale picture is stored as consecutive
bitmaps (4 planes). No need to specify the size, we take the whole screen.
This example includes an inline picture (download it). It's a static far byte
array. It is actually stored on slow memory (flash?), so we have to copy it to a
faster memory, Picturex (static) ---> Picture.
#include <stdrom.h>
#include "libc.h"
#include "Picture.c" /* <--- Click */
extern far Grayscale(byte far *);
byte far Picture[3200 * 4];
void main()
{
far_memcpy(Picture, Picturex, 3200 * 4);
LibClrDisp();
LibPutDisp();
Grayscale(Picture);
LibJumpMenu();
}
|