First commit

This commit is contained in:
2025-12-23 21:24:06 +00:00
parent 2a30c0d77b
commit f5dc0ccbc9
52 changed files with 1663 additions and 208 deletions
+36
View File
@@ -0,0 +1,36 @@
#include "Display/UI/Text/BitmapFont.h"
namespace Display::UI::Text
{
// 5x7 bitmap digits (09)
static const uint8_t digits[10][7] = {
{0x1E, 0x29, 0x25, 0x23, 0x21, 0x29, 0x1E}, // 0
{0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E}, // 1
{0x1E, 0x21, 0x01, 0x0E, 0x10, 0x20, 0x3F}, // 2
{0x1E, 0x21, 0x02, 0x06, 0x01, 0x21, 0x1E}, // 3
{0x02, 0x06, 0x0A, 0x12, 0x3F, 0x02, 0x02}, // 4
{0x3F, 0x20, 0x3E, 0x01, 0x01, 0x21, 0x1E}, // 5
{0x0E, 0x10, 0x20, 0x3E, 0x21, 0x21, 0x1E}, // 6
{0x3F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08}, // 7
{0x1E, 0x21, 0x21, 0x1E, 0x21, 0x21, 0x1E}, // 8
{0x1E, 0x21, 0x21, 0x1F, 0x01, 0x02, 0x1C} // 9
};
void BitmapFont::drawDigit(Display::Graphics::Framebuffer &fb, int x, int y, int d, uint16_t color)
{
if (d < 0 || d > 9)
return;
for (int row = 0; row < 7; ++row)
{
for (int col = 0; col < 5; ++col)
{
if (digits[d][row] & (1 << (4 - col)))
{
// Рисуем пиксель как 2x2 квадрат для читаемости
fb.fillRect(x + col * 2, y + row * 2, 2, 2, color);
}
}
}
}
} // namespace Display::UI::Text
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include <cstdint>
#include "Display/Graphics/Framebuffer.h"
#include "Display/Graphics/Color.h"
namespace Display::UI::Text
{
class BitmapFont
{
public:
void drawDigit(Display::Graphics::Framebuffer &fb, int x, int y, int d, uint16_t color);
};
} // namespace Display::UI::Text
+21
View File
@@ -0,0 +1,21 @@
message(STATUS " Configuring Text")
add_library(Text
BitmapFont.cpp
FontFace.cpp
GlyphCache.cpp
Renderer.cpp
)
find_package(Freetype REQUIRED)
target_link_libraries(Text
PRIVATE Graphics ${FREETYPE_LIBRARIES}
)
# include-root общий
target_include_directories(Text
PUBLIC
${INCLUDE_BASE_DIR}
${FREETYPE_INCLUDE_DIRS}
)
+38
View File
@@ -0,0 +1,38 @@
#include "Display/UI/Text/FontFace.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include <stdexcept>
namespace Display::UI::Text
{
struct FontFace::FreeType
{
FT_Library library = nullptr;
FT_Face face = nullptr;
};
FontFace::FontFace(const std::string &path, int pixelSize)
: freetype(std::make_unique<FreeType>())
{
if (FT_Init_FreeType(&freetype->library))
throw std::runtime_error("FT_Init_FreeType failed");
if (FT_New_Face(freetype->library, path.c_str(), 0, &freetype->face))
throw std::runtime_error("FT_New_Face failed");
FT_Set_Pixel_Sizes(freetype->face, 0, pixelSize);
}
FontFace::~FontFace()
{
if (freetype->face)
FT_Done_Face(freetype->face);
if (freetype->library)
FT_Done_FreeType(freetype->library);
}
void *FontFace::getFace() const
{
return freetype->face;
}
} // namespace Display::UI::Text
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <memory>
#include <string>
namespace Display::UI::Text
{
class FontFace
{
public:
FontFace(const std::string &path, int pixelSize);
~FontFace();
FontFace(const FontFace &) = delete;
FontFace &operator=(const FontFace &) = delete;
FontFace(FontFace &&) = delete;
FontFace &operator=(FontFace &&) = delete;
void *getFace() const;
private:
struct FreeType;
std::unique_ptr<FreeType> freetype;
};
} // namespace Display::UI::Text
+15
View File
@@ -0,0 +1,15 @@
#include <cstdint>
#include <vector>
namespace Display::UI::Text
{
struct Glyph
{
int width;
int height;
int bearingX;
int bearingY;
int advance;
std::vector<uint8_t> buffer; // grayscale bitmap
};
} // namespace Display::UI::Text
+35
View File
@@ -0,0 +1,35 @@
#include "Display/UI/Text/GlyphCache.h"
#include <stdexcept>
#include <iostream>
#include "Display/UI/Text/FontFace.h"
#include <ft2build.h>
#include FT_FREETYPE_H
namespace Display::UI::Text
{
GlyphCache::GlyphCache(const FontFace &fontFace) : fontFace(fontFace) {}
const Glyph &GlyphCache::getGlyph(char c)
{
FT_Face face = static_cast<FT_Face>(fontFace.getFace());
auto it = cache.find(c);
if (it != cache.end())
return it->second;
if (!face)
throw std::runtime_error("FontFace not initialized!");
if (FT_Load_Char(face, c, FT_LOAD_RENDER))
throw std::runtime_error("FT_Load_Char failed");
FT_GlyphSlot g = face->glyph;
Glyph glyph;
glyph.width = g->bitmap.width;
glyph.height = g->bitmap.rows;
glyph.bearingX = g->bitmap_left;
glyph.bearingY = g->bitmap_top;
glyph.advance = g->advance.x >> 6;
glyph.buffer.assign(g->bitmap.buffer, g->bitmap.buffer + g->bitmap.width * g->bitmap.rows);
return cache.emplace(c, std::move(glyph)).first->second;
}
} // namespace Display::UI::Text
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include "Display/UI/Text/Glyph.h"
#include <unordered_map>
#include <cstdint>
namespace Display::UI::Text
{
class FontFace;
class GlyphCache
{
public:
GlyphCache(const FontFace &fontFace);
GlyphCache(const GlyphCache &) = delete;
GlyphCache &operator=(const GlyphCache &) = delete;
GlyphCache(GlyphCache &&) = delete;
GlyphCache &operator=(GlyphCache &&) = delete;
const Glyph &getGlyph(char c);
private:
const FontFace &fontFace;
std::unordered_map<char, Glyph> cache;
};
} // namespace Display::UI::Text
+47
View File
@@ -0,0 +1,47 @@
#include "Display/UI/Text/Renderer.h"
#include "Display/UI/Text/UTFDecoder.h"
#include <iostream>
namespace Display::UI::Text
{
Renderer::Renderer() : fontFace("/usr/share/fonts/truetype/noto/NotoMono-Regular.ttf", 12), glyphCache(fontFace) {}
void Renderer::drawText(Display::Graphics::Framebuffer &fb, int x, int y,
const std::string &text, const Display::Graphics::Color &color)
{
int penX = x;
int baseline = y;
for (char ch : text)
{
const Glyph &g = glyphCache.getGlyph(ch);
for (int row = 0; row < g.height; ++row)
{
int py = baseline - g.bearingY + row;
for (int col = 0; col < g.width; ++col)
{
int px = penX + col + g.bearingX;
uint8_t alpha = g.buffer[row * g.width + col];
if (!alpha)
continue;
Display::Graphics::Color bg = fb.getPixel(px, py);
Display::Graphics::Color out;
out.r = (alpha * color.r + (255 - alpha) * bg.r) / 255;
out.g = (alpha * color.g + (255 - alpha) * bg.g) / 255;
out.b = (alpha * color.b + (255 - alpha) * bg.b) / 255;
fb.setPixel(px, py, out);
}
}
penX += g.advance;
}
}
} // namespace Display::UI::Text
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include "Display/Graphics/Framebuffer.h"
#include "Display/UI/Text/FontFace.h"
#include "Display/UI/Text/GlyphCache.h"
#include "Display/Graphics/Color.h"
#include <string>
namespace Display::UI::Text
{
class Renderer
{
public:
Renderer();
Renderer(const Renderer &) = delete;
Renderer &operator=(const Renderer &) = delete;
void drawText(Display::Graphics::Framebuffer &fb, int x, int y, const std::string &text, const Display::Graphics::Color &color);
private:
FontFace fontFace;
GlyphCache glyphCache;
};
} // namespace Display::UI::Text
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include <string_view>
#include <cstdint>
namespace Display::UI::Text
{
inline bool utf8Next(std::string_view &s, uint32_t &codepoint)
{
if (s.empty())
return false;
uint8_t c = s[0];
if ((c & 0x80) == 0)
{
codepoint = c;
s.remove_prefix(1);
}
else if ((c & 0xE0) == 0xC0)
{
codepoint = ((c & 0x1F) << 6) | (s[1] & 0x3F);
s.remove_prefix(2);
}
else if ((c & 0xF0) == 0xE0)
{
codepoint = ((c & 0x0F) << 12) |
((s[1] & 0x3F) << 6) |
(s[2] & 0x3F);
s.remove_prefix(3);
}
else
{
return false;
}
return true;
}
} // namespace Display::UI::Text