Remove separate 8x2 class; double-buffered is a constructor arg

This commit is contained in:
Phillip Burgess 2023-02-15 08:05:07 -08:00
parent e36890e453
commit 1acf442ffa
4 changed files with 54 additions and 131 deletions

View file

@ -3,12 +3,8 @@
#include <PicoDVI.h>
// Double-buffered 8-bit and 1-bit are declared a little differently...
// 8-bit requires a distinct class for double-buffering, as its memory
// requirements are unique, while 1-bit has an extra constructor
// argument to enable or disable this.
// 8-bit currently supports 320x240 and 400x240 resolutions only.
DVIGFX8x2 display(DVI_RES_400x240p60, pimoroni_demo_hdmi_cfg);
DVIGFX8 display(DVI_RES_400x240p60, true, pimoroni_demo_hdmi_cfg);
#define N_BALLS 100 // 1-254 (not 255)
struct {

View file

@ -3,7 +3,7 @@
#include <PicoDVI.h>
// 8-bit currently supports 320x240 and 400x240 resolutions only.
DVIGFX8 display(DVI_RES_400x240p60, pimoroni_demo_hdmi_cfg);
DVIGFX8 display(DVI_RES_400x240p60, false, pimoroni_demo_hdmi_cfg);
void setup() {
Serial.begin(115200);

View file

@ -118,19 +118,31 @@ bool DVIGFX16::begin(void) {
// us, but allocated by GFX as part of the framebuffer all at once. The
// HEIGHT value is de-tweaked to the original value so clipping won't allow
// any drawing operations to spill into the 16-bit scanlines.
// This requires latest Adafruit_GFX as double-buffered mode plays games with
// the canvas pointer, wasn't possible until that was made protected (vs
// private). Drawing and palette-setting operations ONLY apply to the "back"
// state. Call swap() to switch the front/back buffers at the next vertical
// sync, for flicker-free and tear-free animation.
DVIGFX8::DVIGFX8(const DVIresolution r, const struct dvi_serialiser_cfg &c,
vreg_voltage v)
DVIGFX8::DVIGFX8(const DVIresolution r, const bool d,
const struct dvi_serialiser_cfg &c, vreg_voltage v)
: PicoDVI(dvispec[r].timing, c, v),
GFXcanvas8(dvispec[r].width, ((dvispec[r].height + 1) & ~1) + 4) {
GFXcanvas8(
dvispec[r].width,
(d ? (dvispec[r].height * 2) : ((dvispec[r].height + 1) & ~1)) + 4),
dbuf(d) {
HEIGHT = _height = dvispec[r].height;
buffer_save = buffer;
dvi_vertical_repeat = dvispec[r].v_rep;
dvi_monochrome_tmds = false;
}
DVIGFX8::~DVIGFX8(void) { gfxptr = NULL; }
DVIGFX8::~DVIGFX8(void) {
buffer = buffer_save; // Restore pointer so canvas destructor works
gfxptr = NULL;
}
static void scanline_callback_GFX8(void) {
static void __not_in_flash_func(scanline_callback_GFX8)(void) {
((DVIGFX8 *)gfxptr)->_scanline_callback();
}
@ -141,19 +153,32 @@ void __not_in_flash_func(DVIGFX8::_scanline_callback)(void) {
b16 = row565[rowidx]; // Next row to send
queue_add_blocking_u32(&dvi0.q_colour_valid, &b16); // Send it
scanline = (scanline + 1) % HEIGHT; // Next scanline
uint8_t *b8 = &getBuffer()[WIDTH * scanline]; // New src
rowidx = (rowidx + 1) & 1; // Swap row565[] bufs
b16 = row565[rowidx]; // New dest
if (++scanline >= HEIGHT) { // Next scanline...end of screen reached?
if (swap_wait) { // Swap buffers?
back_index = 1 - back_index; // Yes plz
buffer = buffer_save + WIDTH * HEIGHT * back_index;
swap_wait = 0;
}
scanline = 0;
}
// Refresh from front buffer
uint8_t *b8 = buffer_save + WIDTH * scanline; // New src
if (dbuf)
b8 += WIDTH * HEIGHT * (1 - back_index);
rowidx = (rowidx + 1) & 1; // Swap row565[] bufs
b16 = row565[rowidx]; // New dest
uint16_t *p16 = dbuf ? palette[1 - back_index] : palette[0];
for (int i = 0; i < WIDTH; i++)
b16[i] = palette[b8[i]];
b16[i] = p16[b8[i]];
}
bool DVIGFX8::begin(void) {
uint8_t *bufptr = getBuffer();
if ((bufptr)) {
gfxptr = this;
row565[0] = (uint16_t *)&bufptr[(WIDTH * HEIGHT + 1) & ~1];
row565[0] = (uint16_t *)&bufptr[dbuf ? WIDTH * HEIGHT * 2
: (WIDTH * HEIGHT + 1) & ~1];
row565[1] = row565[0] + WIDTH;
memset(palette, 0, sizeof palette);
// mainloop = mainloop8;
@ -172,100 +197,24 @@ bool DVIGFX8::begin(void) {
return false;
}
// DVIGFX8x2 (8-bit, color-indexed, double-buffered for animation)
// requires latest Adafruit_GFX as it plays games with the canvas pointer,
// wasn't possible until that was made protected (vs private). This is very
// similar to DVIGFX8 but effectively has two canvases and palettes ("front"
// and "back"). Drawing and palette-setting operations ONLY apply to the
// "back" state. Call swap() to switch the front/back buffers at the next
// vertical sync, for flicker-free and tear-free animation.
void DVIGFX8::swap(bool copy_framebuffer, bool copy_palette) {
if (dbuf) {
// Request buffer swap at next frame end, wait for it to happen.
for (swap_wait = 1; swap_wait;)
;
DVIGFX8x2::DVIGFX8x2(const DVIresolution r, const struct dvi_serialiser_cfg &c,
vreg_voltage v)
: PicoDVI(dvispec[r].timing, c, v),
GFXcanvas8(dvispec[r].width, dvispec[r].height * 2 + 4) {
HEIGHT = _height = dvispec[r].height;
buffer_save = buffer;
dvi_vertical_repeat = dvispec[r].v_rep;
dvi_monochrome_tmds = false;
}
DVIGFX8x2::~DVIGFX8x2(void) {
buffer = buffer_save; // Restore pointer so canvas destructor works
gfxptr = NULL;
}
static void scanline_callback_GFX8x2(void) {
((DVIGFX8x2 *)gfxptr)->_scanline_callback();
}
void __not_in_flash_func(DVIGFX8x2::_scanline_callback)(void) {
uint16_t *b16;
while (queue_try_remove_u32(&dvi0.q_colour_free, &b16))
; // Discard returned pointer(s)
b16 = row565[rowidx]; // Next row to send
queue_add_blocking_u32(&dvi0.q_colour_valid, &b16); // Send it
if (++scanline >= HEIGHT) { // Next scanline...end of screen reached?
if (swap_wait) { // Swap buffers?
back_index = 1 - back_index; // Yes plz
buffer = buffer_save + WIDTH * HEIGHT * back_index;
swap_wait = 0;
if ((copy_framebuffer)) {
uint32_t bufsize = WIDTH * HEIGHT;
memcpy(buffer_save + bufsize * back_index,
buffer_save + bufsize * (1 - back_index), bufsize);
}
scanline = 0;
}
// Refresh from front buffer
uint8_t *b8 = buffer_save + WIDTH * HEIGHT * (1 - back_index) +
WIDTH * scanline; // New src
rowidx = (rowidx + 1) & 1; // Swap row565[] bufs
b16 = row565[rowidx]; // New dest
for (int i = 0; i < WIDTH; i++)
b16[i] = palette[1 - back_index][b8[i]];
}
bool DVIGFX8x2::begin(void) {
uint8_t *bufptr = getBuffer();
if ((bufptr)) {
gfxptr = this;
row565[0] = (uint16_t *)&bufptr[WIDTH * HEIGHT * 2];
row565[1] = row565[0] + WIDTH;
memset(palette, 0, sizeof palette);
// mainloop = mainloop8;
mainloop = dvi_scanbuf_main_16bpp; // in libdvi
dvi0.scanline_callback = scanline_callback_GFX8x2;
PicoDVI::begin();
bufptr += WIDTH * HEIGHT; // Initial front buffer is index 1
// No need to initialize the row565 buffer contents as that memory is
// cleared on canvas alloc, and the initial palette state is also all 0.
uint16_t *b16 = row565[0];
queue_add_blocking_u32(&dvi0.q_colour_valid, &b16);
b16 = row565[1];
queue_add_blocking_u32(&dvi0.q_colour_valid, &b16);
wait_begin = false; // Set core 1 in motion
return true;
}
return false;
}
void DVIGFX8x2::swap(bool copy_framebuffer, bool copy_palette) {
// Request buffer swap at next frame end, wait for it to happen.
for (swap_wait = 1; swap_wait;)
;
if ((copy_framebuffer)) {
uint32_t bufsize = WIDTH * HEIGHT;
memcpy(buffer_save + bufsize * back_index,
buffer_save + bufsize * (1 - back_index), bufsize);
}
if ((copy_palette)) {
memcpy(palette[back_index], palette[1 - back_index], sizeof(palette[0]));
if ((copy_palette)) {
memcpy(palette[back_index], palette[1 - back_index], sizeof(palette[0]));
}
}
}
// 1-bit WIP --------
DVIGFX1::DVIGFX1(const DVIresolution r, const bool d,
const struct dvi_serialiser_cfg &c, vreg_voltage v)
: PicoDVI(dvispec[r].timing, c, v),

View file

@ -55,34 +55,11 @@ protected:
class DVIGFX8 : public PicoDVI, public GFXcanvas8 {
public:
DVIGFX8(const DVIresolution res = DVI_RES_400x240p60,
DVIGFX8(const DVIresolution res = DVI_RES_400x240p60, const bool dbuf = false,
const struct dvi_serialiser_cfg &c = pimoroni_demo_hdmi_cfg,
vreg_voltage v = VREG_VOLTAGE_1_20);
~DVIGFX8(void);
bool begin(void);
uint16_t *getPalette(void) { return palette; }
void setColor(uint8_t idx, uint16_t color) { palette[idx] = color; }
void setColor(uint8_t idx, uint8_t red, uint8_t green, uint8_t blue) {
palette[idx] = ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | (blue >> 3);
}
uint16_t getColor(uint8_t idx) { return palette[idx]; }
void _scanline_callback(void);
protected:
uint16_t palette[256];
uint16_t *row565[2]; // 2 scanlines of 16-bit RGB565 data
uint16_t scanline = 2; // First 2 scanlines are set up before DVI start
uint8_t rowidx = 1; // Alternate 0/1 for which row565[] is active
};
class DVIGFX8x2 : public PicoDVI, public GFXcanvas8 {
public:
DVIGFX8x2(const DVIresolution res = DVI_RES_400x240p60,
const struct dvi_serialiser_cfg &c = pimoroni_demo_hdmi_cfg,
vreg_voltage v = VREG_VOLTAGE_1_20);
~DVIGFX8x2(void);
bool begin(void);
uint16_t *getPalette(void) { return palette[back_index]; }
void setColor(uint8_t idx, uint16_t color) {
palette[back_index][idx] = color;
@ -97,13 +74,14 @@ public:
void _scanline_callback(void);
protected:
uint16_t palette[2][256]; // Double-buffered palette
uint16_t palette[2][256]; // [2] for double-buffering
uint16_t *row565[2]; // 2 scanlines of 16-bit RGB565 data
uint16_t scanline = 2; // First 2 scanlines are set up before DVI start
uint8_t rowidx = 1; // Alternate 0/1 for which row565[] is active
bool dbuf = false; // True if double-buffered
uint8_t *buffer_save; // Original canvas buffer pointer
uint8_t back_index = 0; // Which of 2 buffers receives draw ops
volatile bool swap_wait = 0; // For syncronizing front/back buffer swap
volatile bool swap_wait = 0; // For synchronizing fromt/back buffer swap
};
class DVIGFX1 : public PicoDVI, public GFXcanvas1 {