mirror of
https://github.com/irmen/prog8.git
synced 2025-02-18 05:30:34 +00:00
simplify bmx loading
This commit is contained in:
parent
5d9caef45f
commit
afe521b0c9
@ -1,12 +1,9 @@
|
|||||||
; Routines to load and save "BMX" files (commander X16 bitmap format) Version 1.
|
; 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
|
; 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: ability to save "stamps" , bitmaps that are only a region of the screen.
|
||||||
; TODO: load 4bpp/2bpp/1bpp images
|
; TODO: ability to just load the palette from a BMX file
|
||||||
; TODO: save 4bpp/2bpp/1bpp images
|
|
||||||
|
|
||||||
%import diskio
|
%import diskio
|
||||||
|
|
||||||
@ -20,52 +17,31 @@ bmx {
|
|||||||
uword width
|
uword width
|
||||||
uword height
|
uword height
|
||||||
ubyte border
|
ubyte border
|
||||||
ubyte palette_entries ; 0 means 256, all of them
|
uword palette_entries ; 1-256
|
||||||
ubyte palette_start
|
ubyte palette_start
|
||||||
ubyte compression
|
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
|
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 {
|
uword error_message ; pointer to error message, or 0 if all ok
|
||||||
; Loads a BMX bitmap image and palette into vram. (and Header info into the bmx.* variables)
|
ubyte old_drivenumber
|
||||||
; Parameters:
|
|
||||||
; the drive number and filename to load,
|
sub open(ubyte drivenumber, str filename) -> bool {
|
||||||
; and the vram bank and address where the bitmap data should go,
|
; Open a BMX bitmap file and reads the header information.
|
||||||
; You can set the max_width and max_height variables first, if you want this routine to check those.
|
; Returns true if all is ok, false otherwise + error_message will be set.
|
||||||
; 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.
|
|
||||||
error_message = 0
|
error_message = 0
|
||||||
ubyte old_drivenumber = diskio.drivenumber
|
old_drivenumber = diskio.drivenumber
|
||||||
diskio.drivenumber = drivenumber
|
diskio.drivenumber = drivenumber
|
||||||
if diskio.f_open(filename) {
|
if diskio.f_open(filename) {
|
||||||
diskio.reset_read_channel()
|
diskio.reset_read_channel()
|
||||||
if read_header() {
|
if read_header() {
|
||||||
if parse_header() {
|
if parse_header() {
|
||||||
if max_width and width>max_width {
|
if palette_entries>0 {
|
||||||
error_message = "image too large"
|
if width<=640 {
|
||||||
goto load_end
|
return true
|
||||||
}
|
} else
|
||||||
if max_height and height>max_height {
|
error_message = "image too large"
|
||||||
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"
|
|
||||||
} else
|
} else
|
||||||
error_message = "palette error"
|
error_message = "invalid bmx file"
|
||||||
} else
|
} else
|
||||||
error_message = "invalid bmx file"
|
error_message = "invalid bmx file"
|
||||||
} else
|
} else
|
||||||
@ -73,110 +49,60 @@ bmx {
|
|||||||
} else
|
} else
|
||||||
error_message = diskio.status()
|
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.f_close()
|
||||||
diskio.drivenumber = old_drivenumber
|
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
|
return error_message==0
|
||||||
}
|
}
|
||||||
|
|
||||||
sub load_stamp(ubyte drivenumber, str filename, ubyte vbank, uword vaddr, uword screenwidth) -> bool {
|
sub continue_load_stamp(ubyte vbank, uword vaddr, uword screenwidth) -> bool {
|
||||||
; Loads a BMX bitmap "stamp" image and palette into vram. (and Header info into the bmx.* variables)
|
; 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)
|
; "Stamp" means: load an image that is smaller than the screen (so we need to pad around it)
|
||||||
; Parameters:
|
; Parameters:the vram bank and address where the bitmap data should go,
|
||||||
; the drive number and filename to load,
|
; and the screen width that the stamp image is loaded into.
|
||||||
; and 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.
|
||||||
; 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.
|
|
||||||
; Note: does not change vera screen mode or colordepth! You have to do that yourself!
|
; 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
|
error_message = 0
|
||||||
ubyte old_drivenumber = diskio.drivenumber
|
diskio.reset_read_channel()
|
||||||
diskio.drivenumber = drivenumber
|
if compression==0 {
|
||||||
if diskio.f_open(filename) {
|
if read_palette() {
|
||||||
diskio.reset_read_channel()
|
if not read_bitmap_padded(vbank, vaddr, screenwidth)
|
||||||
if read_header() {
|
error_message = "bitmap error"
|
||||||
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"
|
|
||||||
} else
|
} else
|
||||||
error_message = "invalid bmx file"
|
error_message = "palette error"
|
||||||
} else
|
} else
|
||||||
error_message = diskio.status()
|
error_message = "compression not supported"
|
||||||
|
|
||||||
load_end:
|
close()
|
||||||
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
|
|
||||||
return error_message==0
|
return error_message==0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +123,7 @@ load_end:
|
|||||||
error_message = "width not 320 or 640"
|
error_message = "width not 320 or 640"
|
||||||
goto save_end
|
goto save_end
|
||||||
}
|
}
|
||||||
ubyte old_drivenumber = diskio.drivenumber
|
old_drivenumber = diskio.drivenumber
|
||||||
diskio.drivenumber = drivenumber
|
diskio.drivenumber = drivenumber
|
||||||
if diskio.f_open_w(filename) {
|
if diskio.f_open_w(filename) {
|
||||||
cx16.r0 = diskio.status()
|
cx16.r0 = diskio.status()
|
||||||
@ -223,13 +149,8 @@ save_end:
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub set_bpp(ubyte bpp) {
|
sub set_bpp(ubyte bpp) {
|
||||||
bitsperpixel = bpp
|
ubyte[8] depths = [0,1,1,2,2,2,2,3]
|
||||||
vera_colordepth = 0
|
vera_colordepth = depths[bpp-1]
|
||||||
when bpp {
|
|
||||||
2 -> vera_colordepth = 1
|
|
||||||
4 -> vera_colordepth = 2
|
|
||||||
8 -> vera_colordepth = 3
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub set_vera_colordepth(ubyte depth) {
|
sub set_vera_colordepth(ubyte depth) {
|
||||||
@ -253,7 +174,7 @@ save_end:
|
|||||||
; otherwise it is read directly into the palette in vram.
|
; otherwise it is read directly into the palette in vram.
|
||||||
cx16.vaddr(1, $fa00+palette_start*2, 0, 1)
|
cx16.vaddr(1, $fa00+palette_start*2, 0, 1)
|
||||||
cx16.r3 = palette_buffer_ptr
|
cx16.r3 = palette_buffer_ptr
|
||||||
cx16.r2L = palette_entries
|
cx16.r2L = lsb(palette_entries)
|
||||||
do {
|
do {
|
||||||
cx16.r4L = cbm.CHRIN()
|
cx16.r4L = cbm.CHRIN()
|
||||||
cx16.r4H = 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,
|
; 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.
|
; otherwise it is read directly from the palette in vram.
|
||||||
cx16.r3 = palette_buffer_ptr
|
cx16.r3 = palette_buffer_ptr
|
||||||
cx16.r2L = palette_entries
|
cx16.r2L = lsb(palette_entries)
|
||||||
cx16.vaddr(1, $fa00+palette_start*2, 0, 1)
|
cx16.vaddr(1, $fa00+palette_start*2, 0, 1)
|
||||||
do {
|
do {
|
||||||
if cx16.r3 {
|
if cx16.r3 {
|
||||||
@ -374,6 +295,8 @@ save_end:
|
|||||||
width = peekw(&header+6)
|
width = peekw(&header+6)
|
||||||
height = peekw(&header+8)
|
height = peekw(&header+8)
|
||||||
palette_entries = header[10]
|
palette_entries = header[10]
|
||||||
|
if palette_entries==0
|
||||||
|
palette_entries=256
|
||||||
palette_start = header[11]
|
palette_start = header[11]
|
||||||
; the data offset is not needed: data_offset = peekw(&header+12)
|
; the data offset is not needed: data_offset = peekw(&header+12)
|
||||||
compression = header[14]
|
compression = header[14]
|
||||||
@ -388,9 +311,7 @@ save_end:
|
|||||||
; build the internal BMX header structure
|
; build the internal BMX header structure
|
||||||
; normally you don't have to call this yourself
|
; normally you don't have to call this yourself
|
||||||
sys.memset(header, sizeof(header), 0)
|
sys.memset(header, sizeof(header), 0)
|
||||||
uword data_offset = 512 ; full palette of 256 entries
|
uword data_offset = palette_entries*$0002
|
||||||
if palette_entries
|
|
||||||
data_offset = palette_entries*$0002
|
|
||||||
data_offset += sizeof(header)
|
data_offset += sizeof(header)
|
||||||
header[0] = FILEID[0]
|
header[0] = FILEID[0]
|
||||||
header[1] = FILEID[1]
|
header[1] = FILEID[1]
|
||||||
@ -402,7 +323,7 @@ save_end:
|
|||||||
header[7] = msb(width)
|
header[7] = msb(width)
|
||||||
header[8] = lsb(height)
|
header[8] = lsb(height)
|
||||||
header[9] = msb(height)
|
header[9] = msb(height)
|
||||||
header[10] = palette_entries
|
header[10] = lsb(palette_entries)
|
||||||
header[11] = palette_start
|
header[11] = palette_start
|
||||||
header[12] = lsb(data_offset)
|
header[12] = lsb(data_offset)
|
||||||
header[13] = msb(data_offset)
|
header[13] = msb(data_offset)
|
||||||
|
@ -138,9 +138,9 @@ asmsub FREADUY (ubyte value @Y) {
|
|||||||
|
|
||||||
asmsub parse_f(str value @AY) -> float @FAC1 {
|
asmsub parse_f(str value @AY) -> float @FAC1 {
|
||||||
; -- parse a string value of a number to float in 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
|
; ($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 {{
|
%asm {{
|
||||||
sta $a9
|
sta $a9
|
||||||
sty $aa
|
sty $aa
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
; Viewer program for BMX image files.
|
; 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
|
; BMX file format: see https://cx16forum.com/forum/viewtopic.php?t=6945
|
||||||
|
|
||||||
%import textio
|
%import textio
|
||||||
@ -19,13 +15,15 @@ main {
|
|||||||
txt.print("\nenter bmx image filename: ")
|
txt.print("\nenter bmx image filename: ")
|
||||||
if txt.input_chars(&filename) {
|
if txt.input_chars(&filename) {
|
||||||
|
|
||||||
if bmx.load_header(8, filename) {
|
if bmx.open(8, filename) {
|
||||||
txt.print("\nsize: ")
|
txt.print("\nsize: ")
|
||||||
txt.print_uw(bmx.width)
|
txt.print_uw(bmx.width)
|
||||||
txt.chrout('*')
|
txt.chrout('*')
|
||||||
txt.print_uw(bmx.height)
|
txt.print_uw(bmx.height)
|
||||||
txt.print(" bpp: ")
|
txt.print(" bpp: ")
|
||||||
txt.print_uw(bmx.bitsperpixel)
|
txt.print_uw(bmx.bitsperpixel)
|
||||||
|
txt.print(" num colors: ")
|
||||||
|
txt.print_uw(bmx.palette_entries)
|
||||||
txt.nl()
|
txt.nl()
|
||||||
sys.wait(100)
|
sys.wait(100)
|
||||||
|
|
||||||
@ -35,8 +33,6 @@ main {
|
|||||||
bmx.palette_buffer_ptr = memory("palette", 512, 0)
|
bmx.palette_buffer_ptr = memory("palette", 512, 0)
|
||||||
sys.memset(bmx.palette_buffer_ptr, 512, 0)
|
sys.memset(bmx.palette_buffer_ptr, 512, 0)
|
||||||
palette.set_rgb(bmx.palette_buffer_ptr, 256)
|
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
|
; 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
|
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
|
; now load the image
|
||||||
if bmx.width==320 {
|
if bmx.width==320 {
|
||||||
; can use the fast, full-screen load routine
|
; 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 {
|
if bmx.height<240 {
|
||||||
; fill the remaining bottom part of the screen
|
; fill the remaining bottom part of the screen
|
||||||
cx16.GRAPH_set_colors(bmx.border, bmx.border, 99)
|
cx16.GRAPH_set_colors(bmx.border, bmx.border, 99)
|
||||||
@ -61,7 +57,7 @@ main {
|
|||||||
; need to use the slower load routine that does padding
|
; need to use the slower load routine that does padding
|
||||||
; center the image on the screen nicely
|
; center the image on the screen nicely
|
||||||
uword offset = (320-bmx.width)/2 + (240-bmx.height)/2*320
|
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()
|
activate_palette()
|
||||||
void txt.waitkey()
|
void txt.waitkey()
|
||||||
}
|
}
|
||||||
@ -85,7 +81,7 @@ main {
|
|||||||
cx16.VERA_DC_BORDER = bmx.border
|
cx16.VERA_DC_BORDER = bmx.border
|
||||||
cx16.r1 = bmx.palette_buffer_ptr
|
cx16.r1 = bmx.palette_buffer_ptr
|
||||||
cx16.r2L = bmx.palette_start
|
cx16.r2L = bmx.palette_start
|
||||||
cx16.r3L = bmx.palette_entries
|
cx16.r3L = lsb(bmx.palette_entries)
|
||||||
do {
|
do {
|
||||||
palette.set_color(cx16.r2L, peekw(cx16.r1))
|
palette.set_color(cx16.r2L, peekw(cx16.r1))
|
||||||
cx16.r1+=2
|
cx16.r1+=2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user