Separate a reusable Image class from Screen (#834)

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
This commit is contained in:
Dimo Markov
2024-04-27 12:03:44 +03:00
committed by GitHub
parent 1f6e1101e8
commit 293ff179f6
12 changed files with 243 additions and 107 deletions

View File

@@ -810,13 +810,49 @@ void Canvas::DrawText(int x,
continue;
}
Cell& cell = storage_[XY{x / 2, y / 4}];
cell.type = CellType::kText;
cell.type = CellType::kCell;
cell.content.character = it;
style(cell.content);
x += 2;
}
}
/// @brief Directly draw a predefined pixel at the given coordinate
/// @param x the x coordinate of the pixel.
/// @param y the y coordinate of the pixel.
/// @param p the pixel to draw.
void Canvas::DrawPixel(int x, int y, const Pixel& p) {
Cell& cell = storage_[XY{x / 2, y / 4}];
cell.type = CellType::kCell;
cell.content = p;
}
/// @brief Draw a predefined image, with top-left corner at the given coordinate
/// You can supply negative coordinates to align the image however you like -
/// only the 'visible' portion will be drawn
/// @param x the x coordinate corresponding to the top-left corner of the image.
/// @param y the y coordinate corresponding to the top-left corner of the image.
/// @param image the image to draw.
void Canvas::DrawImage(int x, int y, const Image& image) {
x /= 2;
y /= 4;
const int dx_begin = std::max(0, -x);
const int dy_begin = std::max(0, -y);
const int dx_end = std::min(image.dimx(), width_ - x);
const int dy_end = std::min(image.dimy(), height_ - y);
for (int dy = dy_begin; dy < dy_end; ++dy) {
for (int dx = dx_begin; dx < dx_end; ++dx) {
Cell& cell = storage_[XY{
x + dx,
y + dy,
}];
cell.type = CellType::kCell;
cell.content = image.PixelAt(dx, dy);
}
}
}
/// @brief Modify a pixel at a given location.
/// @param style a function that modifies the pixel.
void Canvas::Style(int x, int y, const Stylizer& style) {

View File

@@ -39,6 +39,12 @@ bool Box::Contain(int x, int y) const {
y_max >= y;
}
/// @return whether the box is empty.
/// @ingroup screen
bool Box::IsEmpty() {
return x_min > x_max || y_min > y_max;
}
/// @return whether |other| is the same as |this|
/// @ingroup screen
bool Box::operator==(const Box& other) const {

View File

@@ -0,0 +1,68 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <cstdint> // for size_t
#include <iostream> // for operator<<, stringstream, basic_ostream, flush, cout, ostream
#include <limits>
#include <map> // for _Rb_tree_const_iterator, map, operator!=, operator==
#include <memory> // for allocator, allocator_traits<>::value_type
#include <sstream> // IWYU pragma: keep
#include <utility> // for pair
#include "ftxui/screen/image.hpp"
#include "ftxui/screen/string.hpp" // for string_width
namespace ftxui {
namespace
{
Pixel& dev_null_pixel() {
static Pixel pixel;
return pixel;
}
}
Image::Image(int dimx, int dimy)
: stencil{0, dimx - 1, 0, dimy - 1},
dimx_(dimx),
dimy_(dimy),
pixels_(dimy, std::vector<Pixel>(dimx)) {}
/// @brief Access a character in a cell at a given position.
/// @param x The cell position along the x-axis.
/// @param y The cell position along the y-axis.
std::string& Image::at(int x, int y) {
return PixelAt(x, y).character;
}
/// @brief Access a character in a cell at a given position.
/// @param x The cell position along the x-axis.
/// @param y The cell position along the y-axis.
const std::string& Image::at(int x, int y) const {
return PixelAt(x, y).character;
}
/// @brief Access a cell (Pixel) at a given position.
/// @param x The cell position along the x-axis.
/// @param y The cell position along the y-axis.
Pixel& Image::PixelAt(int x, int y) {
return stencil.Contain(x, y) ? pixels_[y][x] : dev_null_pixel();
}
/// @brief Access a cell (Pixel) at a given position.
/// @param x The cell position along the x-axis.
/// @param y The cell position along the y-axis.
const Pixel& Image::PixelAt(int x, int y) const {
return stencil.Contain(x, y) ? pixels_[y][x] : dev_null_pixel();
}
/// @brief Clear all the pixel from the screen.
void Image::Clear() {
for (auto& line : pixels_) {
for (auto& cell : line) {
cell = Pixel();
}
}
}
} // namespace ftxui

View File

@@ -42,11 +42,6 @@ namespace ftxui {
namespace {
Pixel& dev_null_pixel() {
static Pixel pixel;
return pixel;
}
#if defined(_WIN32)
void WindowsEmulateVT100Terminal() {
static bool done = false;
@@ -392,15 +387,11 @@ Screen Screen::Create(Dimensions dimension) {
return {dimension.dimx, dimension.dimy};
}
Screen::Screen(int dimx, int dimy)
: stencil{0, dimx - 1, 0, dimy - 1},
dimx_(dimx),
dimy_(dimy),
pixels_(dimy, std::vector<Pixel>(dimx)) {
Screen::Screen(int dimx, int dimy) : Image{dimx, dimy} {
#if defined(_WIN32)
// The placement of this call is a bit weird, however we can assume that
// anybody who instantiates a Screen object eventually wants to output
// something to the console.
// something to the console. If that is not the case, use an instance of Image instead.
// As we require UTF8 for all input/output operations we will just switch to
// UTF8 encoding here
SetConsoleOutputCP(CP_UTF8);
@@ -450,34 +441,6 @@ void Screen::Print() const {
std::cout << ToString() << '\0' << std::flush;
}
/// @brief Access a character in a cell at a given position.
/// @param x The cell position along the x-axis.
/// @param y The cell position along the y-axis.
std::string& Screen::at(int x, int y) {
return PixelAt(x, y).character;
}
/// @brief Access a character in a cell at a given position.
/// @param x The cell position along the x-axis.
/// @param y The cell position along the y-axis.
const std::string& Screen::at(int x, int y) const {
return PixelAt(x, y).character;
}
/// @brief Access a cell (Pixel) at a given position.
/// @param x The cell position along the x-axis.
/// @param y The cell position along the y-axis.
Pixel& Screen::PixelAt(int x, int y) {
return stencil.Contain(x, y) ? pixels_[y][x] : dev_null_pixel();
}
/// @brief Access a cell (Pixel) at a given position.
/// @param x The cell position along the x-axis.
/// @param y The cell position along the y-axis.
const Pixel& Screen::PixelAt(int x, int y) const {
return stencil.Contain(x, y) ? pixels_[y][x] : dev_null_pixel();
}
/// @brief Return a string to be printed in order to reset the cursor position
/// to the beginning of the screen.
///
@@ -517,11 +480,8 @@ std::string Screen::ResetPosition(bool clear) const {
/// @brief Clear all the pixel from the screen.
void Screen::Clear() {
for (auto& line : pixels_) {
for (auto& cell : line) {
cell = Pixel();
}
}
Image::Clear();
cursor_.x = dimx_ - 1;
cursor_.y = dimy_ - 1;