54uint8_t g_map_braille[2][4][2] = {
56 {0b00000000, 0b00000001},
57 {0b00000000, 0b00000010},
58 {0b00000000, 0b00000100},
59 {0b00000001, 0b00000000},
62 {0b00000000, 0b00001000},
63 {0b00000000, 0b00010000},
64 {0b00000000, 0b00100000},
65 {0b00000010, 0b00000000},
70std::vector<std::string> g_map_block = {
71 " ",
"▘",
"▖",
"▌",
"▝",
"▀",
"▞",
"▛",
72 "▗",
"▚",
"▄",
"▙",
"▐",
"▜",
"▟",
"█",
76const std::map<std::string, uint8_t> g_map_block_inversed = {
77 {
" ", 0b0000}, {
"▘", 0b0001}, {
"▖", 0b0010}, {
"▌", 0b0011},
78 {
"▝", 0b0100}, {
"▀", 0b0101}, {
"▞", 0b0110}, {
"▛", 0b0111},
79 {
"▗", 0b1000}, {
"▚", 0b1001}, {
"▄", 0b1010}, {
"▙", 0b1011},
80 {
"▐", 0b1100}, {
"▜", 0b1101}, {
"▟", 0b1110}, {
"█", 0b1111},
83constexpr auto nostyle = [](Pixel& ) {};
93 storage_(width_ * height_ / 8 ) {}
99 auto it = storage_.find(XY{x, y});
100 return (it == storage_.end()) ?
Pixel() : it->second.content;
141 Cell& cell = storage_[XY{x / 2, y / 4}];
142 if (cell.type != CellType::kBraille) {
144 cell.type = CellType::kBraille;
147 cell.content.
character[1] |= g_map_braille[x % 2][y % 4][0];
148 cell.content.
character[2] |= g_map_braille[x % 2][y % 4][1];
158 Cell& cell = storage_[XY{x / 2, y / 4}];
159 if (cell.type != CellType::kBraille) {
161 cell.type = CellType::kBraille;
164 cell.content.
character[1] &= ~(g_map_braille[x % 2][y % 4][0]);
165 cell.content.
character[2] &= ~(g_map_braille[x % 2][y % 4][1]);
175 Cell& cell = storage_[XY{x / 2, y / 4}];
176 if (cell.type != CellType::kBraille) {
178 cell.type = CellType::kBraille;
181 cell.content.
character[1] ^= g_map_braille[x % 2][y % 4][0];
182 cell.content.
character[2] ^= g_map_braille[x % 2][y % 4][1];
216 const int dx = std::abs(x2 - x1);
217 const int dy = std::abs(y2 - y1);
218 const int sx = x1 < x2 ? 1 : -1;
219 const int sy = y1 < y2 ? 1 : -1;
220 const int length = std::max(dx, dy);
222 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
225 if (dx + dx > width_ * height_) {
230 for (
int i = 0; i < length; ++i) {
232 if (2 * error >= -dy) {
236 if (2 * error <= dx) {
287 const Color& color) {
323 const Color& color) {
342 int dx = (1 + 2 * x) * e2 * e2;
354 err += dx += 2 * r2 * r2;
358 err += dy += 2 * r1 * r1;
387 const Color& color) {
406 int dx = (1 + 2 * x) * e2 * e2;
411 for (
int xx = x1 + x; xx <= x1 - x; ++xx) {
418 err += dx += 2 * r2 * r2;
422 err += dy += 2 * r1 * r1;
427 for (
int yy = y1 - y; yy <= y1 + y; ++yy) {
472 Cell& cell = storage_[XY{x / 2, y / 2}];
473 if (cell.type != CellType::kBlock) {
475 cell.type = CellType::kBlock;
478 const uint8_t bit = (x % 2) * 2 + y % 2;
479 uint8_t value = g_map_block_inversed.at(cell.content.
character);
481 cell.content.
character = g_map_block[value];
491 Cell& cell = storage_[XY{x / 2, y / 4}];
492 if (cell.type != CellType::kBlock) {
494 cell.type = CellType::kBlock;
498 const uint8_t bit = (y % 2) * 2 + x % 2;
499 uint8_t value = g_map_block_inversed.at(cell.content.
character);
500 value &= ~(1U << bit);
501 cell.content.
character = g_map_block[value];
512 Cell& cell = storage_[XY{x / 2, y / 4}];
513 if (cell.type != CellType::kBlock) {
515 cell.type = CellType::kBlock;
519 const uint8_t bit = (y % 2) * 2 + x % 2;
520 uint8_t value = g_map_block_inversed.at(cell.content.
character);
522 cell.content.
character = g_map_block[value];
559 const int dx = std::abs(x2 - x1);
560 const int dy = std::abs(y2 - y1);
561 const int sx = x1 < x2 ? 1 : -1;
562 const int sy = y1 < y2 ? 1 : -1;
563 const int length = std::max(dx, dy);
565 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
568 if (dx + dx > width_ * height_) {
573 for (
int i = 0; i < length; ++i) {
575 if (2 * error >= -dy) {
579 if (2 * error <= dx) {
630 const Color& color) {
666 const Color& color) {
687 int dx = (1 + 2 * x) * e2 * e2;
692 DrawBlock(x1 - x, 2 * (y1 + y),
true, s);
693 DrawBlock(x1 + x, 2 * (y1 + y),
true, s);
694 DrawBlock(x1 + x, 2 * (y1 - y),
true, s);
695 DrawBlock(x1 - x, 2 * (y1 - y),
true, s);
699 err += dx += 2 * r2 * r2;
703 err += dy += 2 * r1 * r1;
732 const Color& color) {
753 int dx = (1 + 2 * x) * e2 * e2;
758 for (
int xx = x1 + x; xx <= x1 - x; ++xx) {
765 err += dx += 2 * r2 * r2;
769 err += dy += 2 * r1 * r1;
774 for (
int yy = y1 + y; yy <= y1 - y; ++yy) {
795 const std::string& value,
796 const Color& color) {
807 const std::string& value,
814 Cell& cell = storage_[XY{x / 2, y / 4}];
815 cell.type = CellType::kCell;
827 Cell& cell = storage_[XY{x / 2, y / 4}];
828 cell.type = CellType::kCell;
841 const int dx_begin = std::max(0, -x);
842 const int dy_begin = std::max(0, -y);
843 const int dx_end = std::min(image.
dimx(), width_ - x);
844 const int dy_end = std::min(image.
dimy(), height_ - y);
846 for (
int dy = dy_begin; dy < dy_end; ++dy) {
847 for (
int dx = dx_begin; dx < dx_end; ++dx) {
848 Cell& cell = storage_[XY{
852 cell.type = CellType::kCell;
853 cell.content = image.
PixelAt(dx, dy);
862 style(storage_[XY{x / 2, y / 4}].content);
868class CanvasNodeBase :
public Node {
870 CanvasNodeBase() =
default;
874 const int y_max = std::min(c.
height() / 4, box_.y_max - box_.y_min + 1);
875 const int x_max = std::min(c.
width() / 2, box_.x_max - box_.x_min + 1);
876 for (
int y = 0; y < y_max; ++y) {
877 for (
int x = 0; x < x_max; ++x) {
883 virtual const Canvas&
canvas() = 0;
891 class Impl :
public CanvasNodeBase {
894 requirement_.min_x = (canvas_->width() + 1) / 2;
895 requirement_.min_y = (canvas_->height() + 3) / 4;
897 const Canvas& canvas()
final {
return *canvas_; }
900 return std::make_shared<Impl>(canvas);
908 class Impl :
public CanvasNodeBase {
910 Impl(
int width,
int height, std::function<
void(
Canvas&)> fn)
911 : width_(width), height_(height), fn_(std::move(fn)) {}
913 void ComputeRequirement()
final {
914 requirement_.min_x = (width_ + 1) / 2;
915 requirement_.min_y = (height_ + 3) / 4;
918 void Render(
Screen& screen)
final {
919 const int width = (box_.x_max - box_.x_min + 1) * 2;
920 const int height = (box_.y_max - box_.y_min + 1) * 4;
921 canvas_ =
Canvas(width, height);
923 CanvasNodeBase::Render(screen);
926 const Canvas& canvas()
final {
return canvas_; }
930 std::function<void(
Canvas&)> fn_;
932 return std::make_shared<Impl>(width, height, std::move(fn));
938 const int default_dim = 12;
939 return canvas(default_dim, default_dim, std::move(fn));
void DrawImage(int x, int y, const Image &)
在给定坐标处绘制预定义图像,左上角位于该坐标 您可以提供负坐标来随意对齐图像 - 只会绘制“可见”部分
void DrawBlockLine(int x1, int y1, int x2, int y2)
绘制由块字符组成的线条。
void DrawPointEllipseFilled(int x, int y, int r1, int r2)
绘制由盲文点组成的实心椭圆。
void DrawPointLine(int x1, int y1, int x2, int y2)
绘制由盲文点组成的线条。
void DrawText(int x, int y, const std::string &value)
绘制一段文本。
std::function< void(Pixel &)> Stylizer
void DrawBlockOn(int x, int y)
绘制一个块。
void DrawPointCircleFilled(int x, int y, int radius)
绘制由盲文点组成的实心圆。
void DrawPointOn(int x, int y)
绘制一个盲文点。
void DrawPointOff(int x, int y)
擦除一个盲文点。
Pixel GetPixel(int x, int y) const
获取单元格的内容。
void DrawBlockEllipseFilled(int x1, int y1, int r1, int r2)
绘制由块字符组成的实心椭圆。
void DrawPointEllipse(int x, int y, int r1, int r2)
绘制由盲文点组成的椭圆。
void DrawPoint(int x, int y, bool value)
绘制一个盲文点。
void DrawBlockEllipse(int x1, int y1, int r1, int r2)
绘制由块字符组成的椭圆。
void DrawBlockToggle(int x, int y)
切换一个块。如果已填充,则擦除。如果为空, 将被填充。
void DrawBlockCircle(int x1, int y1, int radius)
绘制由块字符组成的圆。
void DrawBlockCircleFilled(int x1, int y1, int radius)
绘制由块字符组成的实心圆。
void DrawPointCircle(int x, int y, int radius)
绘制由盲文点组成的圆。
void DrawBlockOff(int x, int y)
擦除一个块。
void DrawBlock(int x, int y, bool value)
绘制一个块。
void Style(int x, int y, const Stylizer &style)
修改给定位置的像素。
void DrawPointToggle(int x, int y)
切换盲文点。已填充的将被擦除,另一个将被绘制。
void DrawPixel(int x, int y, const Pixel &)
在给定坐标处直接绘制预定义像素
void Render(Screen &screen, const Element &element)
在 ftxui::Screen 上显示元素。
Decorator color(Color)
使用前景色进行装饰。
Canvas 是一个与绘图操作相关的可绘制缓冲区。
Pixel & PixelAt(int x, int y)
访问给定位置的单元格 (Pixel)。
#include "ftxui/component/component_base.hpp" // 用于 ComponentBase
std::shared_ptr< Node > Element
std::vector< std::string > Utf8ToGlyphs(const std::string &input)
Element canvas(ConstRef< Canvas >)
从 Canvas 或 Canvas 引用生成元素。