221 lines
5.8 KiB
C++
221 lines
5.8 KiB
C++
#include "FBDisplay.h"
|
||
#include "FrameBuffer.h"
|
||
#include <fcntl.h>
|
||
#include <sys/ioctl.h>
|
||
#include <sys/mman.h>
|
||
#include <sys/select.h>
|
||
#include <unistd.h>
|
||
#include <cstdio>
|
||
#include <cstring>
|
||
#include <iostream>
|
||
#include <algorithm>
|
||
#include <ctime>
|
||
|
||
namespace Platform
|
||
{
|
||
namespace
|
||
{
|
||
inline uint16_t rgba_to_rgb565(uint32_t rgba)
|
||
{
|
||
const uint32_t r = (rgba >> 24) & 0xFFu;
|
||
const uint32_t g = (rgba >> 16) & 0xFFu;
|
||
const uint32_t b = (rgba >> 8) & 0xFFu;
|
||
return static_cast<uint16_t>(((r & 0xF8u) << 8) | ((g & 0xFCu) << 3) | (b >> 3));
|
||
}
|
||
|
||
inline uint32_t rgba_to_argb8888(uint32_t rgba)
|
||
{
|
||
return ((rgba >> 8) & 0x00FFFFFFu) | ((rgba & 0xFFu) << 24);
|
||
}
|
||
}
|
||
|
||
bool FBDisplay::init(int w, int h)
|
||
{
|
||
width = w;
|
||
height = h;
|
||
|
||
fb_fd = open("/dev/fb0", O_RDWR);
|
||
if (fb_fd < 0)
|
||
{
|
||
perror("open /dev/fb0");
|
||
return false;
|
||
}
|
||
|
||
if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ||
|
||
ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo) < 0)
|
||
{
|
||
perror("ioctl FBIOGET_FSCREENINFO / FBIOGET_VSCREENINFO");
|
||
close(fb_fd);
|
||
fb_fd = -1;
|
||
return false;
|
||
}
|
||
|
||
printf("FB: %dx%d (virtual %dx%d), %dbpp, line_length=%d\n",
|
||
vinfo.xres, vinfo.yres, vinfo.xres_virtual, vinfo.yres_virtual,
|
||
vinfo.bits_per_pixel, finfo.line_length);
|
||
printf("RGB offsets: R=%d/%d G=%d/%d B=%d/%d\n",
|
||
vinfo.red.offset, vinfo.red.length,
|
||
vinfo.green.offset, vinfo.green.length,
|
||
vinfo.blue.offset, vinfo.blue.length);
|
||
|
||
fb_size = finfo.line_length * vinfo.yres;
|
||
fb_mem = (uint8_t*)mmap(nullptr, fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
|
||
if (fb_mem == MAP_FAILED)
|
||
{
|
||
perror("mmap");
|
||
close(fb_fd);
|
||
fb_fd = -1;
|
||
return false;
|
||
}
|
||
|
||
memset(fb_mem, 0, fb_size);
|
||
return true;
|
||
}
|
||
|
||
uint32_t FBDisplay::convert_pixel(uint32_t rgba) const
|
||
{
|
||
uint8_t r = (rgba >> 24) & 0xFF;
|
||
uint8_t g = (rgba >> 16) & 0xFF;
|
||
uint8_t b = (rgba >> 8) & 0xFF;
|
||
uint8_t a = rgba & 0xFF;
|
||
|
||
uint32_t color = 0;
|
||
if (vinfo.red.length > 0)
|
||
color |= (uint32_t)(r >> (8 - vinfo.red.length)) << vinfo.red.offset;
|
||
if (vinfo.green.length > 0)
|
||
color |= (uint32_t)(g >> (8 - vinfo.green.length)) << vinfo.green.offset;
|
||
if (vinfo.blue.length > 0)
|
||
color |= (uint32_t)(b >> (8 - vinfo.blue.length)) << vinfo.blue.offset;
|
||
if (vinfo.transp.length > 0)
|
||
color |= (uint32_t)(a >> (8 - vinfo.transp.length)) << vinfo.transp.offset;
|
||
return color;
|
||
}
|
||
|
||
void FBDisplay::present(const Core::FrameBuffer* framebuffer)
|
||
{
|
||
if (!fb_mem || !framebuffer)
|
||
return;
|
||
|
||
const uint32_t* src = static_cast<const uint32_t*>(framebuffer->get_buffer());
|
||
const int src_width = framebuffer->get_width();
|
||
const int dst_width = std::min(src_width, static_cast<int>(vinfo.xres));
|
||
const int dst_height = std::min(framebuffer->get_height(), static_cast<int>(vinfo.yres));
|
||
const int bytes_per_pixel = vinfo.bits_per_pixel / 8;
|
||
const bool is_rgb565 =
|
||
vinfo.bits_per_pixel == 16 &&
|
||
vinfo.red.offset == 11 && vinfo.red.length == 5 &&
|
||
vinfo.green.offset == 5 && vinfo.green.length == 6 &&
|
||
vinfo.blue.offset == 0 && vinfo.blue.length == 5;
|
||
const bool is_argb8888 =
|
||
vinfo.bits_per_pixel == 32 &&
|
||
vinfo.red.offset == 16 && vinfo.red.length == 8 &&
|
||
vinfo.green.offset == 8 && vinfo.green.length == 8 &&
|
||
vinfo.blue.offset == 0 && vinfo.blue.length == 8;
|
||
const bool is_rgba8888 =
|
||
vinfo.bits_per_pixel == 32 &&
|
||
vinfo.red.offset == 24 && vinfo.red.length == 8 &&
|
||
vinfo.green.offset == 16 && vinfo.green.length == 8 &&
|
||
vinfo.blue.offset == 8 && vinfo.blue.length == 8;
|
||
|
||
if (is_rgb565)
|
||
{
|
||
#ifdef USE_RGB565_BACKBUFFER
|
||
// backbuffer 内部已是 RGB565,直接行拷贝提交
|
||
const uint8_t* src_bytes = static_cast<const uint8_t*>(framebuffer->get_buffer());
|
||
if (static_cast<int>(finfo.line_length) == dst_width * 2)
|
||
{
|
||
std::memcpy(fb_mem, src_bytes, static_cast<size_t>(dst_height) * dst_width * 2);
|
||
}
|
||
else
|
||
{
|
||
for (int y = 0; y < dst_height; ++y)
|
||
{
|
||
std::memcpy(fb_mem + y * finfo.line_length,
|
||
src_bytes + y * src_width * 2,
|
||
dst_width * 2);
|
||
}
|
||
}
|
||
#else
|
||
for (int y = 0; y < dst_height; ++y)
|
||
{
|
||
const uint32_t* src_row = src + y * src_width;
|
||
uint16_t* dst_row = reinterpret_cast<uint16_t*>(fb_mem + y * finfo.line_length);
|
||
for (int x = 0; x < dst_width; ++x)
|
||
{
|
||
dst_row[x] = rgba_to_rgb565(src_row[x]);
|
||
}
|
||
}
|
||
#endif
|
||
return;
|
||
}
|
||
|
||
if (is_argb8888)
|
||
{
|
||
for (int y = 0; y < dst_height; ++y)
|
||
{
|
||
const uint32_t* src_row = src + y * src_width;
|
||
uint32_t* dst_row = reinterpret_cast<uint32_t*>(fb_mem + y * finfo.line_length);
|
||
for (int x = 0; x < dst_width; ++x)
|
||
{
|
||
dst_row[x] = rgba_to_argb8888(src_row[x]);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (is_rgba8888 && dst_width == src_width && static_cast<int>(finfo.line_length) == dst_width * 4)
|
||
{
|
||
std::memcpy(fb_mem, src, static_cast<size_t>(dst_height) * static_cast<size_t>(dst_width) * sizeof(uint32_t));
|
||
return;
|
||
}
|
||
|
||
for (int y = 0; y < dst_height; ++y)
|
||
{
|
||
for (int x = 0; x < dst_width; ++x)
|
||
{
|
||
uint32_t pixel = convert_pixel(src[y * src_width + x]);
|
||
uint8_t* dst = fb_mem + y * finfo.line_length + x * bytes_per_pixel;
|
||
if (vinfo.bits_per_pixel == 32)
|
||
{
|
||
*(uint32_t*)dst = pixel;
|
||
}
|
||
else if (vinfo.bits_per_pixel == 16)
|
||
{
|
||
*(uint16_t*)dst = static_cast<uint16_t>(pixel);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void FBDisplay::poll_events(bool& should_quit)
|
||
{
|
||
fd_set fds;
|
||
FD_ZERO(&fds);
|
||
FD_SET(STDIN_FILENO, &fds);
|
||
struct timeval tv = { 0, 0 };
|
||
if (select(STDIN_FILENO + 1, &fds, nullptr, nullptr, &tv) > 0)
|
||
{
|
||
char c;
|
||
if (read(STDIN_FILENO, &c, 1) == 1 && (c == 'q' || c == 'Q'))
|
||
{
|
||
should_quit = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
void FBDisplay::shutdown()
|
||
{
|
||
if (fb_mem != nullptr)
|
||
{
|
||
memset(fb_mem, 0, fb_size);
|
||
munmap(fb_mem, fb_size);
|
||
fb_mem = nullptr;
|
||
}
|
||
if (fb_fd >= 0)
|
||
{
|
||
close(fb_fd);
|
||
fb_fd = -1;
|
||
}
|
||
}
|
||
}
|