generated from Projetos/RaspberryPi_app
215 lines
7.0 KiB
C++
215 lines
7.0 KiB
C++
// linux_fb_display.cpp — Linux framebuffer HAL implementation.
|
|
|
|
#include "linux_fb_display.h"
|
|
|
|
#include <cerrno>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <unistd.h>
|
|
|
|
namespace gfx {
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Construction / destruction
|
|
// ---------------------------------------------------------------------------
|
|
LinuxFBDisplay::LinuxFBDisplay(int16_t width, int16_t height)
|
|
: GfxCanvas(width, height) {}
|
|
|
|
LinuxFBDisplay::~LinuxFBDisplay() { close(); }
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Open / close
|
|
// ---------------------------------------------------------------------------
|
|
bool LinuxFBDisplay::open(const char* device) {
|
|
fd_ = ::open(device, O_RDWR);
|
|
if (fd_ < 0) {
|
|
std::perror("LinuxFBDisplay::open");
|
|
return false;
|
|
}
|
|
|
|
if (ioctl(fd_, FBIOGET_VSCREENINFO, &vinfo_) < 0) {
|
|
std::perror("FBIOGET_VSCREENINFO");
|
|
close();
|
|
return false;
|
|
}
|
|
if (ioctl(fd_, FBIOGET_FSCREENINFO, &finfo_) < 0) {
|
|
std::perror("FBIOGET_FSCREENINFO");
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
bpp_ = vinfo_.bits_per_pixel;
|
|
line_len_ = finfo_.line_length;
|
|
map_size_ = finfo_.smem_len;
|
|
|
|
buf_ = static_cast<uint8_t*>(
|
|
mmap(nullptr, map_size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0));
|
|
if (buf_ == MAP_FAILED) {
|
|
std::perror("mmap");
|
|
buf_ = nullptr;
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void LinuxFBDisplay::close() {
|
|
if (buf_) {
|
|
munmap(buf_, map_size_);
|
|
buf_ = nullptr;
|
|
}
|
|
if (fd_ >= 0) {
|
|
::close(fd_);
|
|
fd_ = -1;
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Pixel format conversion
|
|
// ---------------------------------------------------------------------------
|
|
void LinuxFBDisplay::writePixel(uint8_t* dst, Color color) const {
|
|
if (bpp_ == 32) {
|
|
// XRGB8888 — write directly.
|
|
std::memcpy(dst, &color, 4);
|
|
} else if (bpp_ == 16) {
|
|
// RGB565
|
|
uint16_t r = (color >> 19) & 0x1F;
|
|
uint16_t g = (color >> 10) & 0x3F;
|
|
uint16_t b = (color >> 3) & 0x1F;
|
|
uint16_t pixel = (r << 11) | (g << 5) | b;
|
|
std::memcpy(dst, &pixel, 2);
|
|
} else if (bpp_ == 8) {
|
|
// Grayscale — simple luminance approximation.
|
|
uint8_t r = (color >> 16) & 0xFF;
|
|
uint8_t g = (color >> 8) & 0xFF;
|
|
uint8_t b = color & 0xFF;
|
|
dst[0] = static_cast<uint8_t>((r * 77 + g * 150 + b * 29) >> 8);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// drawPixelImpl
|
|
// ---------------------------------------------------------------------------
|
|
void LinuxFBDisplay::drawPixelImpl(int16_t x, int16_t y, Color color) {
|
|
if (!buf_) return;
|
|
uint32_t offset = static_cast<uint32_t>(y) * line_len_
|
|
+ static_cast<uint32_t>(x) * (bpp_ / 8);
|
|
writePixel(buf_ + offset, color);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Accelerated horizontal line — memset for monochrome, loop for color
|
|
// ---------------------------------------------------------------------------
|
|
void LinuxFBDisplay::drawHLineImpl(int16_t x, int16_t y,
|
|
int16_t w, Color color) {
|
|
if (!buf_ || w <= 0) return;
|
|
|
|
uint32_t bytes_pp = bpp_ / 8;
|
|
uint8_t* row = buf_ + static_cast<uint32_t>(y) * line_len_
|
|
+ static_cast<uint32_t>(x) * bytes_pp;
|
|
|
|
if (bpp_ == 32) {
|
|
// For XRGB8888, use 32-bit fill.
|
|
auto* p = reinterpret_cast<uint32_t*>(row);
|
|
for (int16_t i = 0; i < w; ++i)
|
|
p[i] = color;
|
|
} else if (bpp_ == 16) {
|
|
uint16_t r = (color >> 19) & 0x1F;
|
|
uint16_t g = (color >> 10) & 0x3F;
|
|
uint16_t b = (color >> 3) & 0x1F;
|
|
uint16_t pixel = (r << 11) | (g << 5) | b;
|
|
auto* p = reinterpret_cast<uint16_t*>(row);
|
|
for (int16_t i = 0; i < w; ++i)
|
|
p[i] = pixel;
|
|
} else if (bpp_ == 8) {
|
|
uint8_t r_ch = (color >> 16) & 0xFF;
|
|
uint8_t g_ch = (color >> 8) & 0xFF;
|
|
uint8_t b_ch = color & 0xFF;
|
|
uint8_t gray = static_cast<uint8_t>(
|
|
(r_ch * 77 + g_ch * 150 + b_ch * 29) >> 8);
|
|
std::memset(row, gray, static_cast<size_t>(w));
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Accelerated vertical line
|
|
// ---------------------------------------------------------------------------
|
|
void LinuxFBDisplay::drawVLineImpl(int16_t x, int16_t y,
|
|
int16_t h, Color color) {
|
|
if (!buf_ || h <= 0) return;
|
|
|
|
uint32_t bytes_pp = bpp_ / 8;
|
|
uint8_t* p = buf_ + static_cast<uint32_t>(y) * line_len_
|
|
+ static_cast<uint32_t>(x) * bytes_pp;
|
|
|
|
// Pre-encode the pixel once.
|
|
uint8_t pixel_buf[4];
|
|
writePixel(pixel_buf, color);
|
|
|
|
for (int16_t i = 0; i < h; ++i) {
|
|
std::memcpy(p, pixel_buf, bytes_pp);
|
|
p += line_len_;
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Accelerated filled rectangle
|
|
// ---------------------------------------------------------------------------
|
|
void LinuxFBDisplay::fillRectImpl(int16_t x, int16_t y,
|
|
int16_t w, int16_t h, Color color) {
|
|
if (!buf_ || w <= 0 || h <= 0) return;
|
|
|
|
// For 32-bpp with color == black or white, we can use memset for the
|
|
// entire rectangle. Otherwise, fill the first row with drawHLineImpl
|
|
// and memcpy it to subsequent rows.
|
|
|
|
uint32_t bytes_pp = bpp_ / 8;
|
|
uint32_t row_bytes = static_cast<uint32_t>(w) * bytes_pp;
|
|
uint8_t* first_row = buf_ + static_cast<uint32_t>(y) * line_len_
|
|
+ static_cast<uint32_t>(x) * bytes_pp;
|
|
|
|
// Optimized path: if all bytes of the pixel are the same value,
|
|
// the entire rect can be filled with memset.
|
|
bool can_memset = false;
|
|
uint8_t fill_byte = 0;
|
|
|
|
if (bpp_ == 32) {
|
|
// 0x00000000 → all bytes 0x00. 0x00FFFFFF → not uniform.
|
|
// Only pure black (all 0x00) qualifies.
|
|
if (color == 0x00000000) { can_memset = true; fill_byte = 0x00; }
|
|
} else if (bpp_ == 8) {
|
|
uint8_t r_ch = (color >> 16) & 0xFF;
|
|
uint8_t g_ch = (color >> 8) & 0xFF;
|
|
uint8_t b_ch = color & 0xFF;
|
|
fill_byte = static_cast<uint8_t>(
|
|
(r_ch * 77 + g_ch * 150 + b_ch * 29) >> 8);
|
|
can_memset = true;
|
|
}
|
|
|
|
if (can_memset) {
|
|
uint8_t* row = first_row;
|
|
for (int16_t r = 0; r < h; ++r) {
|
|
std::memset(row, fill_byte, row_bytes);
|
|
row += line_len_;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// General path: fill first row with pixel values, then memcpy.
|
|
drawHLineImpl(x, y, w, color);
|
|
|
|
uint8_t* src = first_row;
|
|
uint8_t* dst = first_row + line_len_;
|
|
for (int16_t r = 1; r < h; ++r) {
|
|
std::memcpy(dst, src, row_bytes);
|
|
dst += line_len_;
|
|
}
|
|
}
|
|
|
|
} // namespace gfx
|