mch2022-template-app/components/renze-graphics/driver_framebuffer.cpp
2022-01-24 22:32:33 +01:00

195 lines
5.9 KiB
C++

/*
* BADGE.TEAM framebuffer driver
* Uses parts of the Adafruit GFX Arduino libray
* Renze Nicolai 2019
*/
#include "sdkconfig.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_system.h"
#include "include/driver_framebuffer_internal.h"
#define TAG "fb"
static uint8_t* framebuffer;
/* Color space conversions */
inline uint16_t convert24to16(uint32_t in) //RGB24 to 565
{
uint8_t b = (in>>16)&0xFF;
uint8_t r = in&0xFF;
uint8_t g = (in>>8)&0xFF;
return ((b & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (r >> 3);
}
inline uint8_t convert24to8C(uint32_t in) //RGB24 to 256-color
{
uint8_t b = ((in>>16)&0xFF) >> 5;
uint8_t r = ( in &0xFF) >> 6;
uint8_t g = ((in>> 8)&0xFF) >> 5;
return r | (g<<3) | (b<<6);
}
inline uint32_t convert8Cto24(uint8_t in) //256-color to RGB24
{
uint8_t r = in & 0x07;
uint8_t b = in >> 6;
uint8_t g = (in>>3) & 0x07;
return b | (g << 8) | (r << 16);
}
inline uint8_t convert24to8(uint32_t in) //RGB24 to 8-bit greyscale
{
uint8_t r = (in>>16)&0xFF;
uint8_t b = in&0xFF;
uint8_t g = (in>>8)&0xFF;
return ( r + g + b + 1 ) / 3;
}
inline bool convert8to1(uint8_t in) //8-bit greyscale to black&white
{
return in >= 128;
}
esp_err_t driver_framebuffer_init(uint8_t* buffer)
{
static bool driver_framebuffer_init_done = false;
if (driver_framebuffer_init_done) return ESP_OK;
ESP_LOGD(TAG, "init called");
framebuffer = buffer;
driver_framebuffer_fill(NULL, COLOR_FILL_DEFAULT);
driver_framebuffer_set_orientation_angle(NULL, 0); //Apply global orientation (needed for flip)
driver_framebuffer_init_done = true;
ESP_LOGD(TAG, "init done");
return ESP_OK;
}
bool _getFrameContext(Window* window, uint8_t** buffer, int16_t* width, int16_t* height)
{
if (window == NULL) {
//No window provided, use global context
*width = FB_WIDTH;
*height = FB_HEIGHT;
*buffer = framebuffer;
if (!framebuffer) {
ESP_LOGE(TAG, "Framebuffer not allocated!");
return false;
}
} else {
*width = window->width;
*height = window->height;
*buffer = window->buffer;
}
return true;
}
void driver_framebuffer_fill(Window* window, uint32_t value)
{
uint8_t* buffer;
int16_t width, height;
if (!_getFrameContext(window, &buffer, &width, &height)) return;
if (!window) driver_framebuffer_set_dirty_area(0,0,width-1,height-1, true);
value = convert24to16(value);
uint8_t c0 = (value>>8)&0xFF;
uint8_t c1 = value&0xFF;
for (uint32_t i = 0; i < width*height*2; i+=2) {
buffer[i + 0] = c0;
buffer[i + 1] = c1;
}
}
void driver_framebuffer_setPixel(Window* window, int16_t x, int16_t y, uint32_t value)
{
uint8_t* buffer; int16_t width, height;
if (!_getFrameContext(window, &buffer, &width, &height)) return;
if (!driver_framebuffer_orientation_apply(window, &x, &y)) return;
bool changed = false;
value = convert24to16(value);
uint8_t c0 = (value>>8)&0xFF;
uint8_t c1 = value&0xFF;
uint32_t position = (y * width * 2) + (x * 2);
if (buffer[position + 0] != c0 || buffer[position + 1] != c1) changed = true;
buffer[position + 0] = c0;
buffer[position + 1] = c1;
if ((!window) && changed) driver_framebuffer_set_dirty_area(x,y,x,y,false);
}
uint32_t driver_framebuffer_getPixel(Window* window, int16_t x, int16_t y)
{
uint8_t* buffer; int16_t width, height;
if (!_getFrameContext(window, &buffer, &width, &height)) return 0;
if (!driver_framebuffer_orientation_apply(window, &x, &y)) return 0;
uint32_t position = (y * width * 2) + (x * 2);
uint32_t color = (buffer[position] << 8) + (buffer[position + 1]);
uint8_t r = ((((color >> 11) & 0x1F) * 527) + 23) >> 6;
uint8_t g = ((((color >> 5 ) & 0x3F) * 259) + 33) >> 6;
uint8_t b = ((((color ) & 0x1F) * 527) + 23) >> 6;
return r << 16 | g << 8 | b;
}
void driver_framebuffer_blit(Window* source, Window* target)
{
if (source->vOffset >= source->height) return; //The vertical offset is larger than the height of the window
if (source->hOffset >= source->width) return; //The horizontal offset is larger than the width of the window
for (uint16_t wy = source->vOffset; wy < source->drawHeight; wy++) {
for (uint16_t wx = source->hOffset; wx < source->drawWidth; wx++) {
if (wy >= source->height) continue; //Out-of-bounds
if (wx >= source->width) continue; //Out-of-bounds
uint32_t color = driver_framebuffer_getPixel(source, wx, wy); //Read the pixel from the window framebuffer
if (source->enableTransparentColor && source->transparentColor == color) continue; //Transparent
driver_framebuffer_setPixel(target, source->x + wx, source->y + wy, color); //Write the pixel to the global framebuffer
}
}
}
void _render_windows()
{
//Step through the linked list of windows and blit each of the visible windows to the main framebuffer
Window* currentWindow = driver_framebuffer_window_first();
while (currentWindow != NULL) {
if (currentWindow->visible) {
driver_framebuffer_blit(currentWindow, NULL);
}
currentWindow = currentWindow->_nextWindow;
}
}
bool driver_framebuffer_flush(uint32_t flags)
{
if (!framebuffer) {
ESP_LOGE(TAG, "flush without alloc!");
return false;
}
_render_windows();
uint32_t eink_flags = 0;
if ((flags & FB_FLAG_FULL) || (flags & FB_FLAG_FORCE)) {
driver_framebuffer_set_dirty_area(0, 0, FB_WIDTH-1, FB_HEIGHT-1, true);
} else if (!driver_framebuffer_is_dirty()) {
return false; //No need to update, stop.
}
driver_framebuffer_set_dirty_area(FB_WIDTH-1, FB_HEIGHT-1, 0, 0, true); //Not dirty.
return true;
}
uint16_t driver_framebuffer_getWidth(Window* window)
{
int16_t width, height;
driver_framebuffer_get_orientation_size(window, &width, &height);
return width;
}
uint16_t driver_framebuffer_getHeight(Window* window)
{
int16_t width, height;
driver_framebuffer_get_orientation_size(window, &width, &height);
return height;
}