simplify bmx loading

This commit is contained in:
Irmen de Jong 2023-11-29 21:44:04 +01:00
parent 5d9caef45f
commit afe521b0c9
3 changed files with 76 additions and 159 deletions

View File

@ -1,12 +1,9 @@
; Routines to load and save "BMX" files (commander X16 bitmap format) Version 1.
; Only uncompressed images, of width 320 or 640, are supported for now.
; Only uncompressed images are supported for now.
; BMX Specification: https://cx16forum.com/forum/viewtopic.php?t=6945
; TODO: refactor loading: bmx.open() - reads header . bmx.continue_load() - reads the palette+bitmap. bmx.close() -cleanup bmx.save() stays the same
; TODO: ability to save "stamps" , bitmaps that are only a region of the screen.
; TODO: load 4bpp/2bpp/1bpp images
; TODO: save 4bpp/2bpp/1bpp images
; TODO: ability to just load the palette from a BMX file
%import diskio
@ -20,52 +17,31 @@ bmx {
uword width
uword height
ubyte border
ubyte palette_entries ; 0 means 256, all of them
uword palette_entries ; 1-256
ubyte palette_start
ubyte compression
uword error_message ; pointer to error message, or 0 if all ok
uword max_width = 0 ; should you want load() to check for this
uword max_height = 0 ; should you want load() to check for this
uword palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram
sub load(ubyte drivenumber, str filename, ubyte vbank, uword vaddr) -> bool {
; Loads a BMX bitmap image and palette into vram. (and Header info into the bmx.* variables)
; Parameters:
; the drive number and filename to load,
; and the vram bank and address where the bitmap data should go,
; You can set the max_width and max_height variables first, if you want this routine to check those.
; Note: does not change vera screen mode or colordepth! You have to do that yourself!
; Returns: success status. If false, error_message points to the error message string.
uword error_message ; pointer to error message, or 0 if all ok
ubyte old_drivenumber
sub open(ubyte drivenumber, str filename) -> bool {
; Open a BMX bitmap file and reads the header information.
; Returns true if all is ok, false otherwise + error_message will be set.
error_message = 0
ubyte old_drivenumber = diskio.drivenumber
old_drivenumber = diskio.drivenumber
diskio.drivenumber = drivenumber
if diskio.f_open(filename) {
diskio.reset_read_channel()
if read_header() {
if parse_header() {
if max_width and width>max_width {
error_message = "image too large"
goto load_end
}
if max_height and height>max_height {
error_message = "image too large"
goto load_end
}
if width!=320 and width!=640 {
; note: use load_stamp() to read other sizes
error_message = "width not 320 or 640"
goto load_end
}
if compression {
error_message = "compression not supported"
goto load_end
}
if read_palette() {
if not read_bitmap(vbank, vaddr)
error_message = "bitmap error"
if palette_entries>0 {
if width<=640 {
return true
} else
error_message = "image too large"
} else
error_message = "palette error"
error_message = "invalid bmx file"
} else
error_message = "invalid bmx file"
} else
@ -73,110 +49,60 @@ bmx {
} else
error_message = diskio.status()
load_end:
close()
return false
}
sub close() {
; if you want to close the file before actually loading palette or bitmap data.
diskio.f_close()
diskio.drivenumber = old_drivenumber
}
sub continue_load(ubyte vbank, uword vaddr) -> bool {
; Continues loading the palette and bitmap data from the opened BMX file.
; Parameters: the vram bank and address where the bitmap data should go.
; You can set palette_buffer_ptr if you want the palette buffered rather than directly into vram.
; Note: does not change vera screen mode or colordepth! You have to do that yourself!
; Returns true if all is ok, false otherwise + error_message will be set.
error_message = 0
diskio.reset_read_channel()
if width==320 or width==640 {
if compression==0 {
if read_palette() {
if not read_bitmap(vbank, vaddr)
error_message = "bitmap error"
} else
error_message = "palette error"
} else
error_message = "compression not supported"
} else
error_message = "width not 320 or 640" ; note: use continue_load_stamp() to read other sizes
close()
return error_message==0
}
sub load_stamp(ubyte drivenumber, str filename, ubyte vbank, uword vaddr, uword screenwidth) -> bool {
; Loads a BMX bitmap "stamp" image and palette into vram. (and Header info into the bmx.* variables)
sub continue_load_stamp(ubyte vbank, uword vaddr, uword screenwidth) -> bool {
; Continues loading the palette and bitmap "stamp" data from the opened BMX file.
; "Stamp" means: load an image that is smaller than the screen (so we need to pad around it)
; Parameters:
; the drive number and filename to load,
; and the vram bank and address where the bitmap data should go,
; finally the screen width that the stamp image is loaded into.
; You can set the max_width and max_height variables first, if you want this routine to check those.
; Parameters:the vram bank and address where the bitmap data should go,
; and the screen width that the stamp image is loaded into.
; You can set palette_buffer_ptr if you want the palette buffered rather than directly into vram.
; Note: does not change vera screen mode or colordepth! You have to do that yourself!
; Returns: success status. If false, error_message points to the error message string.
; Returns true if all is ok, false otherwise + error_message will be set.
error_message = 0
ubyte old_drivenumber = diskio.drivenumber
diskio.drivenumber = drivenumber
if diskio.f_open(filename) {
diskio.reset_read_channel()
if read_header() {
if parse_header() {
if max_width and width>max_width {
error_message = "image too large"
goto load_end
}
if max_height and height>max_height {
error_message = "image too large"
goto load_end
}
if width>screenwidth {
error_message = "image too large"
goto load_end
}
if compression {
error_message = "compression not supported"
goto load_end
}
if read_palette() {
if not read_bitmap_padded(vbank, vaddr, screenwidth)
error_message = "bitmap error"
} else
error_message = "palette error"
} else
error_message = "invalid bmx file"
diskio.reset_read_channel()
if compression==0 {
if read_palette() {
if not read_bitmap_padded(vbank, vaddr, screenwidth)
error_message = "bitmap error"
} else
error_message = "invalid bmx file"
error_message = "palette error"
} else
error_message = diskio.status()
error_message = "compression not supported"
load_end:
diskio.f_close()
diskio.drivenumber = old_drivenumber
return error_message==0
}
sub load_header(ubyte drivenumber, str filename) -> bool {
; Loads just the header data from a BMX bitmap image into the bmx.* variables.
; Parameters: the drive number and filename to load.
; Returns: success status. If false, error_message points to the error message string.
error_message = 0
ubyte old_drivenumber = diskio.drivenumber
diskio.drivenumber = drivenumber
if diskio.f_open(filename) {
diskio.reset_read_channel()
if read_header() {
if not parse_header()
error_message = "invalid bmx file"
} else
error_message = "invalid bmx file"
} else
error_message = diskio.status()
load_end:
diskio.f_close()
diskio.drivenumber = old_drivenumber
return error_message==0
}
sub load_palette(ubyte drivenumber, str filename) -> bool {
; Loads just the palette from a BMX bitmap image into vram or the palette buffer.
; (and Header info into the bmx.* variables).
; Parameters: the drive number and filename to load.
; Returns: success status. If false, error_message points to the error message string.
error_message = 0
ubyte old_drivenumber = diskio.drivenumber
diskio.drivenumber = drivenumber
if diskio.f_open(filename) {
diskio.reset_read_channel()
if read_header() {
if parse_header() {
if not read_palette()
error_message = "palette error"
} else
error_message = "invalid bmx file"
} else
error_message = "invalid bmx file"
} else
error_message = diskio.status()
load_end:
diskio.f_close()
diskio.drivenumber = old_drivenumber
close()
return error_message==0
}
@ -197,7 +123,7 @@ load_end:
error_message = "width not 320 or 640"
goto save_end
}
ubyte old_drivenumber = diskio.drivenumber
old_drivenumber = diskio.drivenumber
diskio.drivenumber = drivenumber
if diskio.f_open_w(filename) {
cx16.r0 = diskio.status()
@ -223,13 +149,8 @@ save_end:
}
sub set_bpp(ubyte bpp) {
bitsperpixel = bpp
vera_colordepth = 0
when bpp {
2 -> vera_colordepth = 1
4 -> vera_colordepth = 2
8 -> vera_colordepth = 3
}
ubyte[8] depths = [0,1,1,2,2,2,2,3]
vera_colordepth = depths[bpp-1]
}
sub set_vera_colordepth(ubyte depth) {
@ -253,7 +174,7 @@ save_end:
; otherwise it is read directly into the palette in vram.
cx16.vaddr(1, $fa00+palette_start*2, 0, 1)
cx16.r3 = palette_buffer_ptr
cx16.r2L = palette_entries
cx16.r2L = lsb(palette_entries)
do {
cx16.r4L = cbm.CHRIN()
cx16.r4H = cbm.CHRIN()
@ -319,7 +240,7 @@ save_end:
; if palette_buffer_ptr is not 0, the palette data is read from that memory buffer,
; otherwise it is read directly from the palette in vram.
cx16.r3 = palette_buffer_ptr
cx16.r2L = palette_entries
cx16.r2L = lsb(palette_entries)
cx16.vaddr(1, $fa00+palette_start*2, 0, 1)
do {
if cx16.r3 {
@ -374,6 +295,8 @@ save_end:
width = peekw(&header+6)
height = peekw(&header+8)
palette_entries = header[10]
if palette_entries==0
palette_entries=256
palette_start = header[11]
; the data offset is not needed: data_offset = peekw(&header+12)
compression = header[14]
@ -388,9 +311,7 @@ save_end:
; build the internal BMX header structure
; normally you don't have to call this yourself
sys.memset(header, sizeof(header), 0)
uword data_offset = 512 ; full palette of 256 entries
if palette_entries
data_offset = palette_entries*$0002
uword data_offset = palette_entries*$0002
data_offset += sizeof(header)
header[0] = FILEID[0]
header[1] = FILEID[1]
@ -402,7 +323,7 @@ save_end:
header[7] = msb(width)
header[8] = lsb(height)
header[9] = msb(height)
header[10] = palette_entries
header[10] = lsb(palette_entries)
header[11] = palette_start
header[12] = lsb(data_offset)
header[13] = msb(data_offset)

View File

@ -138,9 +138,9 @@ asmsub FREADUY (ubyte value @Y) {
asmsub parse_f(str value @AY) -> float @FAC1 {
; -- parse a string value of a number to float in FAC1
; warning: uses an internal BASIC routine that may be rom version dependent
; warning: on older <R47 kernals it uses an internal BASIC routine that is ROM version dependent,
; ($deb6 is inside the routine for VAL at $deb3) See basic.sym from x16-rom
; If at any time in the future the official VAL_1() routine from the kernal starts working, we should use that here!
; TODO add a check to see if the VAL_1 kernal jump entry is valid if so, then use that instead
%asm {{
sta $a9
sty $aa

View File

@ -1,8 +1,4 @@
; Viewer program for BMX image files.
; This program shows *one* way to do it, by checking the header upfront,
; and loading the palette into system ram first. The simplest way to load
; a BMX file is to just read everything into vram directly using a single bmx.load() call.
;
; BMX file format: see https://cx16forum.com/forum/viewtopic.php?t=6945
%import textio
@ -19,13 +15,15 @@ main {
txt.print("\nenter bmx image filename: ")
if txt.input_chars(&filename) {
if bmx.load_header(8, filename) {
if bmx.open(8, filename) {
txt.print("\nsize: ")
txt.print_uw(bmx.width)
txt.chrout('*')
txt.print_uw(bmx.height)
txt.print(" bpp: ")
txt.print_uw(bmx.bitsperpixel)
txt.print(" num colors: ")
txt.print_uw(bmx.palette_entries)
txt.nl()
sys.wait(100)
@ -35,8 +33,6 @@ main {
bmx.palette_buffer_ptr = memory("palette", 512, 0)
sys.memset(bmx.palette_buffer_ptr, 512, 0)
palette.set_rgb(bmx.palette_buffer_ptr, 256)
bmx.max_width = 320
bmx.max_height = 240
; switch to bitmap screen mode and color depth: 320*240
void cx16.screen_mode($80, false) ; we're lazy and just use a kernal routine to set up the basics
@ -45,7 +41,7 @@ main {
; now load the image
if bmx.width==320 {
; can use the fast, full-screen load routine
if bmx.load(8, filename, 0, 0) {
if bmx.continue_load(0, 0) {
if bmx.height<240 {
; fill the remaining bottom part of the screen
cx16.GRAPH_set_colors(bmx.border, bmx.border, 99)
@ -61,7 +57,7 @@ main {
; need to use the slower load routine that does padding
; center the image on the screen nicely
uword offset = (320-bmx.width)/2 + (240-bmx.height)/2*320
if bmx.load_stamp(8, filename, 0, offset, 320) {
if bmx.continue_load_stamp(0, offset, 320) {
activate_palette()
void txt.waitkey()
}
@ -85,7 +81,7 @@ main {
cx16.VERA_DC_BORDER = bmx.border
cx16.r1 = bmx.palette_buffer_ptr
cx16.r2L = bmx.palette_start
cx16.r3L = bmx.palette_entries
cx16.r3L = lsb(bmx.palette_entries)
do {
palette.set_color(cx16.r2L, peekw(cx16.r1))
cx16.r1+=2