diff --git a/meson.build b/meson.build index 91655e54..841b0607 100644 --- a/meson.build +++ b/meson.build @@ -62,7 +62,7 @@ linux_src = src + ['tests/platform/x64_uC.c', 'rtos/uC-CPU/Posix/cpu_c.c'] -linux_def = def + {'SCREEN_WIDTH': '160', 'SCREEN_HEIGHT': '128'} +linux_def = def + {'SCREEN_WIDTH': '160', 'SCREEN_HEIGHT': '128', 'PIX_FMT_RGB565': ''} linux_inc = inc + ['rtos/uC-OS3/Ports/POSIX', 'rtos/uC-CPU/Posix'] diff --git a/platform/drivers/display/display_libSDL.c b/platform/drivers/display/display_libSDL.c index b4ef0d3f..788528af 100644 --- a/platform/drivers/display/display_libSDL.c +++ b/platform/drivers/display/display_libSDL.c @@ -34,13 +34,85 @@ * Screen dimensions, adjust basing on the size of the screen you need to * emulate */ +#ifndef SCREEN_WIDTH #define SCREEN_WIDTH 160 -#define SCREEN_HEIGHT 128 +#endif + +#ifndef SCREEN_HEIGHT +#define SCREEN_HEIGHT 128 +#endif + +#if !defined(PIX_FMT_BW) && !defined(PIX_FMT_GRAYSC) && !defined(PIX_FMT_RGB565) +#warning No pixel format defined, defaulting to RGB565 +#define PIX_FMT_RGB565 +#endif + +SDL_Window *window; /* SDL window */ +SDL_Surface *renderSurface; /* SDL rendering surface */ +void *frameBuffer; /* Pointer to framebuffer */ +bool inProgress; /* Flag to signal when rendering is in progress */ + +/** + * @internal + * Internal helper function which fetches pixel at position (x, y) from framebuffer + * and returns it in SDL-compatible format, which is ARGB8888. + */ +uint32_t fetchPixelFromFb(unsigned int x, unsigned int y) +{ + uint32_t pixel = 0; + + #ifdef PIX_FMT_BW + /* + * Black and white 1bpp format: framebuffer is an array of uint8_t, where + * each cell contains the values of eight pixels, one per bit. + */ + uint8_t *fb = (uint8_t *)(frameBuffer); + unsigned int cell = (x + y*SCREEN_WIDTH) / 8; + unsigned int elem = (x + y*SCREEN_WIDTH) % 8; + if(fb[cell] & (1 << elem)) pixel = 0xFFFFFFFF; + #endif + + #ifdef PIX_FMT_GRAYSC + /* + * Convert from 8bpp grayscale to ARGB8888, we have to do nothing more that + * replicating the pixel value for the three components + */ + uint8_t *fb = (uint8_t *)(frameBuffer); + uint8_t px = fb[x + y*SCREEN_WIDTH]; + + pixel = 0xFF000000 | (px << 16) | (px << 8) | px; + #endif + + #ifdef PIX_FMT_RGB565 + /* + * SDL pixel format is ARGB8888, while ours is RGB565, thus we need + * to do some conversions when writing framebuffer content to the + * window. We also set alpha value to its maximum. + */ + uint16_t *fb = (uint16_t *)(frameBuffer); + + uint32_t r = (fb[x + y*SCREEN_WIDTH] & 0xF800) >> 11; + uint32_t g = (fb[x + y*SCREEN_WIDTH] & 0x07E0) >> 5; + uint32_t b = (fb[x + y*SCREEN_WIDTH] & 0x001F) & 0x1F; + + /* + * Here we do conversions by multiplying by some scaling factors, + * we use ints just because the precision of floats is not really + * needed. + * Conversion factors: + * - five bit to eight bit: 8.226 + * - six bit to eight bit: 4.0476 + */ + r = (r * 8) + (r * 226)/1000; + g = (g * 4) + (g * 476)/10000; + b = (b * 8) + (b * 226)/1000; + + pixel = 0xFF000000 | (r << 16) | (g << 8) | b; + #endif + + return pixel; +} -SDL_Window *window; -SDL_Surface *renderSurface; -uint16_t *frameBuffer; -bool inProgress; void display_init() { @@ -61,9 +133,35 @@ void display_init() renderSurface = SDL_GetWindowSurface(window); SDL_FillRect(renderSurface,NULL,0xFFFFFF); - unsigned int scrSize = SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint16_t); - frameBuffer = (uint16_t *)(malloc(scrSize)); - memset(frameBuffer, 0xFFFF, scrSize); + /* + * Black and white pixel format: framebuffer type is uint8_t where each + * bit represents a pixel. We have to allocate + * (SCREEN_HEIGHT * SCREEN_WIDTH)/8 elements + */ + #ifdef PIX_FMT_BW + unsigned int fbSize = (SCREEN_HEIGHT * SCREEN_WIDTH)/8; + if((fbSize * 8) < (SCREEN_HEIGHT * SCREEN_WIDTH)) fbSize += 1; /* Compensate for eventual truncation error in division */ + fbSize *= sizeof(uint8_t); + #endif + + /* + * Grayscale pixel format: framebuffer type is uint8_t where each element + * controls one pixel + */ + #ifdef PIX_FMT_GRAYSC + unsigned int fbSize = SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint8_t); + #endif + + /* + * RGB565 pixel format: framebuffer type is uint16_t where each element + * controls one pixel + */ + #ifdef PIX_FMT_RGB565 + unsigned int fbSize = SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint16_t); + #endif + + frameBuffer = malloc(fbSize); + memset(frameBuffer, 0xFFFF, fbSize); inProgress = false; } } @@ -97,32 +195,11 @@ void display_renderRows(uint8_t startRow, uint8_t endRow) Uint32 *pixels = (Uint32*)renderSurface->pixels; inProgress = true; - for(int x = 0; x < SCREEN_WIDTH; x++) + for(unsigned int x = 0; x < SCREEN_WIDTH; x++) { - for(int y = startRow; y < endRow; y++) + for(unsigned int y = startRow; y < endRow; y++) { - /* - * SDL pixel format is ARGB8888, while ours is RGB565, thus we need - * to do some conversions when writing framebuffer content to the - * window. We also set alpha value to its maximum. - */ - uint32_t r = (frameBuffer[x + y*SCREEN_WIDTH] & 0xF800) >> 11; - uint32_t g = (frameBuffer[x + y*SCREEN_WIDTH] & 0x07E0) >> 5; - uint32_t b = (frameBuffer[x + y*SCREEN_WIDTH] & 0x001F) & 0x1F; - - /* - * Here we do conversions by multiplying by some scaling factors, - * we use ints just because the precision of floats is not really - * needed. - * Conversion factors: - * - five bit to eight bit: 8.226 - * - six bit to eight bit: 4.0476 - */ - r = (r * 8) + (r * 226)/1000; - g = (g * 4) + (g * 476)/10000; - b = (b * 8) + (b * 226)/1000; - - pixels[x + y*SCREEN_WIDTH] = 0xFF000000 | (r << 16) | (g << 8) | b; + pixels[x + y*SCREEN_WIDTH] = fetchPixelFromFb(x, y); } }