| January 2003 Laboratory Notes: Computer Engineering II | ||
|---|---|---|
| Prev | Chapter 18 Introduction to PModeLib | Next |
Now for the fun stuff! High-resolution graphics is where protected mode really shows off its full capabilities. Most of what we'll do in this section is nearly impossible in real mode due to 64k segment limitations. We're going to make a very short program which will load a 640x480 graphics file from disk and display it on the screen.
We won't go into all the details of the various graphics file formats here, but let's briefly list the formats supported by PModeLib and its two graphics file libraries:
BMP - Windows Bitmap format. Uncompressed, no alpha support. This format is only provided for completeness, and for the ability to save images. PNG and JPG provide compression and alpha channel support. The PModeLib functions LoadBMP() and SaveBMP() support loading and saving of 8-bit and 24-bit images.
PNG - Portable Network Graphics format. Non-lossy compression, alpha channel support, and many bit depths. It's good for sprites and non-photographic images. The PModeLib function LoadPNG() can load any PNG image.
JPG - JPEG image format. Lossy compression, no alpha support, only 24-bit images. It's excellent for photographic images, as very high compression rates can be achieved with little quality loss. The PModeLib function LoadJPG() can load any JPG image.
All of these functions assume an internal pixel format of uncompressed 32-bit RGBA (the loaded A channel is 0 for formats that don't support it). This may or may not be the same format as the video mode selected, so it may be necessary to write conversion functions to convert between the pixel format used by these functions and the pixel format of the display.
In real mode, we used BIOS Interrupt 10h to set graphics modes and segment B800h or A000h to address the graphics memory. In protected mode, we'll use PModeLib functions to both set the graphics mode and copy image data to the screen. In fact, there isn't a way to directly access the graphics memory, so it's necessary to do double-buffering (although it's possible to double-buffer just regions of the screen rather than the entire display area).
There's another issue with the graphics drivers we use: they require that the keyboard be remapped to a different IRQ and I/O port than the normal keyboard interface (which is at IRQ 1, I/O port 60h). The InitGraphics() function returns the remapped values in the variables whose addresses are passed to it. This will be clearer when we look at the code.
Under Windows 2000/XP, we need to load a special graphics driver called EX291 before running any program that uses PModeLib graphics. Just enter EX291 at the command prompt before running the program to load the driver. Currently, PModeLib graphics require Windows 2000 or Windows XP, they cannot work on Windows 98 or Windows ME.
Okay, here's the complete code to a simple program that loads a 640x480 image named image.jpg and displays it on the 640x480 display. Since this program uses PModeLib graphics, we'll need to load the EX291 driver before running it on Windows 2000/XP.
This program combines all of the concepts earlier in this chapter, except we're using a specialized image loading function instead of general file I/O to load the image.
Example 18-6. Displaying an Image
%include "lib291.inc"
GLOBAL _main
imagesize equ 640*480*4 ; 640x480 image, 32 bits per pixel
SECTION .bss ; Uninitialized data
imageoff resd 1 ; Offset of the image data
doneflag resb 1 ; =1 when we're ready to exit (set by KeyboardHandler)
kbINT resb 1 ; keyboard interrupt number (standard = 9)
kbIRQ resb 1 ; keyboard IRQ (standard = 1)
kbPort resw 1 ; keyboard port (standard = 60h)
SECTION .data ; Initialized data
imagefn db "image.jpg",0 ; image file to read (notice 0-terminated)
SECTION .text
KeyboardHandler
; Indicate that we're finished on any keypress
; If we wanted to check the key, we'd need to use [kbPort], not 60h.
mov byte [doneflag], 1
; Acknowledge interrupt to PIC.
; As the IRQ might be >=8 (a high IRQ), we may need to
; out A0h, 20h, in addition to the normal out 20h, 20h.
mov al, 20h
cmp byte [kbIRQ], 8
jb .lowirq
out 0A0h, al
.lowirq:
out 20h, al
xor eax, eax ; Don't chain to old handler
ret
KeyboardHandler_end
_main
push esi ; Save registers
call _LibInit ; You could use invoke here, too
test eax, eax ; Check for error (nonzero return value)
jnz near .initerror
; Allocate memory for image
invoke _AllocMem, dword imagesize
cmp eax, -1 ; Check for error (-1 return value)
je near .error
mov [imageoff], eax ; Save offset
; Load image
invoke _LoadJPG, dword imagefn, dword [imageoff], dword 0, dword 0
test eax, eax ; Check for error (nonzero return value)
jnz near .error
; Initialize graphics (and find remapped keyboard info)
invoke _InitGraphics, dword kbINT, dword kbIRQ, dword kbPort
test eax, eax ; Check for error (nonzero return value)
jnz near .error
; Lock up memory the handler will access
invoke _LockArea, ds, dword doneflag, dword 1
test eax, eax ; Check for error (nonzero return value)
jnz near .exitgraphics
invoke _LockArea, ds, dword kbIRQ, dword 1
test eax, eax ; Check for error (nonzero return value)
jnz near .exitgraphics
; Lock the interrupt handler itself.
invoke _LockArea, cs, dword KeyboardHandler, \
dword KeyboardHandler_end-KeyboardHandler
test eax, eax ; Check for error (nonzero return value)
jnz near .exitgraphics
; Install the keyboard handler
movzx eax, byte [kbINT]
invoke _Install_Int, dword eax, dword KeyboardHandler
test eax, eax ; Check for error (nonzero return value)
jnz near .exitgraphics
; Find 640x480x32 graphics mode, allowing driver-emulated modes
invoke _FindGraphicsMode, word 640, word 480, word 32, dword 1
cmp ax, -1 ; Did we find a mode? If not, exit.
je near .uninstallkb
; Go into graphics mode (finally :)
invoke _SetGraphicsMode, ax
test eax, eax ; Check for error (nonzero return value)
jnz near .uninstallkb
; Copy the image to the screen
invoke _CopyToScreen, dword [imageoff], dword 640*4, \
dword 0, dword 0, dword 640, dword 480, dword 0, dword 0
; Wait for a keypress
.loop:
cmp byte [doneflag], 0
jz .loop
; Get out of graphics mode
invoke _UnsetGraphicsMode
.uninstallkb:
; Uninstall the keyboard handler
movzx eax, byte [kbINT]
invoke _Remove_Int, dword eax
.exitgraphics:
; Shut down graphics driver
invoke _ExitGraphics
.error:
call _LibExit
.initerror:
pop esi ; Restore registers
ret ; Return to DOS