AgonConsole8 / agon-vdp

Official Firmware for the Agon Console8: ESP32 VDP
MIT License
38 stars 13 forks source link

Compact format for two-color bitmaps #218

Closed jblang closed 1 month ago

jblang commented 2 months ago

I am proposing a new bitmap format that will allow two-color bitmaps to be sent in a much more compact way. Many 8-bit systems allowed defining two-color bitmaps with a foreground and background specified for a certain run of pixels (usually an 8x8 tile, but this isn't a limitation we need to carry over). Having the VDP simulate this format would make it easier to reuse graphics assets designed for other systems (TMS9918, C64, etc).

This can be simulated using RGBA2222 bitmaps, for example, but requires a lot of prep work on the CPU side to convert the data to the proper format (see the tile routines in my gradient library for example). RGBA2222 format also requires sending a lot more bytes to the VDP than is strictly necessary, which means longer loading times. For example an 8x8 tile in RGBA2222 format is 64 bytes, but would be only 9 bytes in my proposed format: two RGBA2222 bytes specifying the foreground and background color, and 8 bytes containing the 8x8 monchrome bitmap to use. The savings in bytes goes up even more if larger bitmaps are used.

stevesims commented 2 months ago

as commented on discord, adding in new bitmap formats is kinda costly... but it is doable

the VDP does already support a "mono/mask" bitmap format. this accepts a single colour value, which defines the pixel "on" colour when the bitmap is rendered to the screen. "off" pixels simply aren't rendered, so whatever had been on the screen where a bitmap off-pixel is positioned will remain when it is drawn. this format is essentially inherited from fab-gl

I guess the question is, should we aim to provide a two-colour bitmap format that requires two colours to be set against the bitmap, or should we have no colours set against the bitmap and instead pick the colours to use to draw at render-time, using the current "foreground" and "background" graphics colours

having no colours set against the bitmap is probably, overall, more powerful and useful. it may however be harder to implement, as the current bitmap drawing system inside vdp-gl doesn't allow for this additional information to be directly included. there is probably a way tho :grin:

stevesims commented 2 months ago

as also discussed on discord, an alternative approach would be to have a buffered command call that can expand out a monochrome bitmap into RGBA2222 format

technically this wouldn't really be a bitmap operation - just a way of expanding data, which could then be used as a bitmap. the command would iterate through a buffer a bit at a time, expanding that single bit out to a whole byte, using a list of bytes to pick from

such a command could also potentially be extended to support multiple bits mapping to bytes - thus allowing the conversion of 2-bit colour bitmaps into RGBA2222 format. support for 3-bit, 4-bit, 5-bit (etc) could also be added

the advantage of this approach would be that no new bitmap renderers would need to be written. rendering RGBA2222 format bitmaps is probably the fastest format to render for every screen mode, so performance would likely be better too

jblang commented 2 months ago

or should we have no colours set against the bitmap and instead pick the colours to use to draw at render-time, using the current "foreground" and "background" graphics colours

It might be useful to have both options, but for the use case I've currently got in mind (my plasma program), the ability to select the bitmap, including the colors with only a single byte is important, so i do want to tie my bitmaps to a specific foreground/background color. This is how the color and pattern table worked on TMS9918's tile mode; however in it's pseudo bitmap mode it is closer to what you proposed. You can specify an 8 bit mono bitmap and foreground and background colors independently for each 1x8 strip of pixels. On the C64, I believe you could independently specify the fg/bg colors for each 8x8 tile on the screen rather than it being an inherent property of the tile itself. You could also set a mode where each tile was 4x8 (stretched to 8x8 horizontally), with 4 colors.

stevesims commented 2 months ago

given the complexity involved in adding support for another bitmap format, I think my preferred solution for supporting other depths of bitmaps is to create RGBA2222 format bitmaps on the VDP from different formats of source data.

overall I feel that is likely the more useful approach, especially given that manipulating RGBA2222 format bitmaps on the VDP is much easier than formats with less bits per pixel. for instance, with existing buffered commands, it's easy to mirror an RGBA2222 format bitmap around the Y axis - essentially impossible to do that for a 1-bpp bitmap.

the challenge of course is coming up with a straightforward API for expanding out data. options will be needed to specify the width (in bits) of data in the source. bitmaps also may be byte-aligned at certain boundaries, such as the row of a bitmap, so that information needs to optionally be provided in an API call too. then there's the matter of how to provide the map of values those bits are getting expanded into. when expanding a monochrome bitmap just two values are needed, but more bits need more values. you may end up needing a map of 16 values, possibly even more. it may be useful to use a buffer as the source for the map to allow it to be re-used - although for expanding a mono image that would be overkill

short-term I think this just means that the API needs to support options, probably with bits in options bytes reserved for future use

stevesims commented 2 months ago

PR #221 adds an "expand bitmap" command, which will allow for bitmaps that are of a pixel width from 1-bpp to 7-bpp to be mapped to 8-bit values. given suitable mapping, this allows the creation of RGBA2222 from arbitrary bitmaps

this function also includes support for setting a "width" for the bitmap - a point at which after having processed that number of pixels the mapping routine will align to the next byte, and then start again.

the expansion routine currently assumes that all bitmaps will start from the top-most bit in a byte. I'm not sure how image alignment within bitmap data usually works. this alignment assumption is based off how fonts are handled in vdp-gl

stevesims commented 1 month ago

@jblang I think the new "expand bitmap" buffer command essentially addresses the underlying desire/need here

as a result I'll close this issue - please feel free to open up another issue if there's any extensions that would be useful