From 3d6c48e800347854646e312f7082f12160915d82 Mon Sep 17 00:00:00 2001 From: Epixu Date: Fri, 29 Mar 2024 17:08:09 +0200 Subject: [PATCH 1/5] Separated Image from Screen to avoid tinkering with Windows console; Added Canvas methods for drawing Pixels/Images directly --- .gitignore | 4 ++ CMakeLists.txt | 2 + include/ftxui/dom/canvas.hpp | 7 ++- include/ftxui/screen/box.hpp | 1 + include/ftxui/screen/image.hpp | 87 +++++++++++++++++++++++++++++++++ include/ftxui/screen/screen.hpp | 62 ++--------------------- src/ftxui/dom/canvas.cpp | 48 ++++++++++++++++++ src/ftxui/screen/box.cpp | 7 +++ src/ftxui/screen/image.cpp | 68 ++++++++++++++++++++++++++ src/ftxui/screen/screen.cpp | 48 ++---------------- 10 files changed, 232 insertions(+), 102 deletions(-) create mode 100644 include/ftxui/screen/image.hpp create mode 100644 src/ftxui/screen/image.cpp diff --git a/.gitignore b/.gitignore index 735ed65b7..4d12e7580 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,10 @@ * !*/ +# Ignore build directories generated by default MSVC CMake integration +# (otherwise causes terribly slow indexing) +out/ + # Allowed top-level files: !.clang-format !.clang-tidy diff --git a/CMakeLists.txt b/CMakeLists.txt index 583ed46a2..6a0b7a556 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,11 +33,13 @@ add_library(screen include/ftxui/screen/box.hpp include/ftxui/screen/color.hpp include/ftxui/screen/color_info.hpp + include/ftxui/screen/image.hpp include/ftxui/screen/screen.hpp include/ftxui/screen/string.hpp src/ftxui/screen/box.cpp src/ftxui/screen/color.cpp src/ftxui/screen/color_info.cpp + src/ftxui/screen/image.cpp src/ftxui/screen/screen.cpp src/ftxui/screen/string.cpp src/ftxui/screen/terminal.cpp diff --git a/include/ftxui/dom/canvas.hpp b/include/ftxui/dom/canvas.hpp index 928683807..5c59d0e57 100644 --- a/include/ftxui/dom/canvas.hpp +++ b/include/ftxui/dom/canvas.hpp @@ -10,7 +10,7 @@ #include // for unordered_map #include "ftxui/screen/color.hpp" // for Color -#include "ftxui/screen/screen.hpp" // for Pixel +#include "ftxui/screen/image.hpp" // for Pixel, Image #ifdef DrawText // Workaround for WinUsr.h (via Windows.h) defining macros that break things. @@ -94,6 +94,11 @@ struct Canvas { void DrawText(int x, int y, const std::string& value); void DrawText(int x, int y, const std::string& value, const Color& color); void DrawText(int x, int y, const std::string& value, const Stylizer& style); + + // Draw using directly pixels or images -------------------------------------- + // Pixel/image coordinates correspond 1:1 + void DrawPixel(int x, int y, const Pixel&); + void DrawImage(int x, int y, const Image&); // Decorator: // x is considered to be a multiple of 2. diff --git a/include/ftxui/screen/box.hpp b/include/ftxui/screen/box.hpp index 701b600c2..32e08fff7 100644 --- a/include/ftxui/screen/box.hpp +++ b/include/ftxui/screen/box.hpp @@ -15,6 +15,7 @@ struct Box { static auto Intersection(Box a, Box b) -> Box; static auto Union(Box a, Box b) -> Box; bool Contain(int x, int y) const; + int Area() const noexcept; bool operator==(const Box& other) const; bool operator!=(const Box& other) const; }; diff --git a/include/ftxui/screen/image.hpp b/include/ftxui/screen/image.hpp new file mode 100644 index 000000000..9bcb95b3e --- /dev/null +++ b/include/ftxui/screen/image.hpp @@ -0,0 +1,87 @@ +// 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. +#ifndef FTXUI_SCREEN_IMAGE_HPP +#define FTXUI_SCREEN_IMAGE_HPP + +#include // for uint8_t +#include +#include // for string, basic_string, allocator +#include // for vector + +#include "ftxui/screen/color.hpp" // for Color, Color::Default +#include "ftxui/screen/box.hpp" // for Box + +namespace ftxui { + +/// @brief A unicode character and its associated style. +/// @ingroup screen +struct Pixel { + Pixel() + : blink(false), + bold(false), + dim(false), + inverted(false), + underlined(false), + underlined_double(false), + strikethrough(false), + automerge(false) {} + + // A bit field representing the style: + bool blink : 1; + bool bold : 1; + bool dim : 1; + bool inverted : 1; + bool underlined : 1; + bool underlined_double : 1; + bool strikethrough : 1; + bool automerge : 1; + + // The hyperlink associated with the pixel. + // 0 is the default value, meaning no hyperlink. + // It's an index for accessing Screen meta data + uint8_t hyperlink = 0; + + // The graphemes stored into the pixel. To support combining characters, + // like: a?, this can potentially contain multiple codepoints. + std::string character = " "; + + // Colors: + Color background_color = Color::Default; + Color foreground_color = Color::Default; +}; + +/// @brief A rectangular grid of Pixel. +/// @ingroup screen +class Image { + public: + // Constructors: + Image() = delete; + Image(int dimx, int dimy); + + // Access a character in the grid at a given position. + std::string& at(int x, int y); + const std::string& at(int x, int y) const; + + // Access a cell (Pixel) in the grid at a given position. + Pixel& PixelAt(int x, int y); + const Pixel& PixelAt(int x, int y) const; + + // Get screen dimensions. + int dimx() const { return dimx_; } + int dimy() const { return dimy_; } + + // Fill the image with space and default style + void Clear(); + + Box stencil; + + protected: + int dimx_; + int dimy_; + std::vector> pixels_; +}; + +} // namespace ftxui + +#endif // FTXUI_SCREEN_IMAGE_HPP diff --git a/include/ftxui/screen/screen.hpp b/include/ftxui/screen/screen.hpp index 8d27900d1..6dd6eedfc 100644 --- a/include/ftxui/screen/screen.hpp +++ b/include/ftxui/screen/screen.hpp @@ -9,48 +9,12 @@ #include // for string, basic_string, allocator #include // for vector -#include "ftxui/screen/box.hpp" // for Box #include "ftxui/screen/color.hpp" // for Color, Color::Default +#include "ftxui/screen/image.hpp" // for Pixel, Image #include "ftxui/screen/terminal.hpp" // for Dimensions namespace ftxui { -/// @brief A unicode character and its associated style. -/// @ingroup screen -struct Pixel { - Pixel() - : blink(false), - bold(false), - dim(false), - inverted(false), - underlined(false), - underlined_double(false), - strikethrough(false), - automerge(false) {} - - // A bit field representing the style: - bool blink : 1; - bool bold : 1; - bool dim : 1; - bool inverted : 1; - bool underlined : 1; - bool underlined_double : 1; - bool strikethrough : 1; - bool automerge : 1; - - // The hyperlink associated with the pixel. - // 0 is the default value, meaning no hyperlink. - uint8_t hyperlink = 0; - - // The graphemes stored into the pixel. To support combining characters, - // like: a⃦, this can potentially contain multiple codepoints. - std::string character = " "; - - // Colors: - Color background_color = Color::Default; - Color foreground_color = Color::Default; -}; - /// @brief Define how the Screen's dimensions should look like. /// @ingroup screen namespace Dimension { @@ -60,36 +24,24 @@ Dimensions Full(); /// @brief A rectangular grid of Pixel. /// @ingroup screen -class Screen { +class Screen : public Image { public: // Constructors: Screen(int dimx, int dimy); static Screen Create(Dimensions dimension); static Screen Create(Dimensions width, Dimensions height); - // Access a character in the grid at a given position. - std::string& at(int x, int y); - const std::string& at(int x, int y) const; - - // Access a cell (Pixel) in the grid at a given position. - Pixel& PixelAt(int x, int y); - const Pixel& PixelAt(int x, int y) const; - std::string ToString() const; // Print the Screen on to the terminal. void Print() const; - // Get screen dimensions. - int dimx() const { return dimx_; } - int dimy() const { return dimy_; } + // Fill the screen with space and reset any screen state, like hyperlinks, and cursor + void Clear(); // Move the terminal cursor n-lines up with n = dimy(). std::string ResetPosition(bool clear = false) const; - // Fill the screen with space. - void Clear(); - void ApplyShader(); struct Cursor { @@ -107,6 +59,7 @@ class Screen { }; Shape shape; }; + Cursor cursor() const { return cursor_; } void SetCursor(Cursor cursor) { cursor_ = cursor; } @@ -115,12 +68,7 @@ class Screen { uint8_t RegisterHyperlink(const std::string& link); const std::string& Hyperlink(uint8_t id) const; - Box stencil; - protected: - int dimx_; - int dimy_; - std::vector> pixels_; Cursor cursor_; std::vector hyperlinks_ = {""}; }; diff --git a/src/ftxui/dom/canvas.cpp b/src/ftxui/dom/canvas.cpp index 58c24904d..c441e6da5 100644 --- a/src/ftxui/dom/canvas.cpp +++ b/src/ftxui/dom/canvas.cpp @@ -817,6 +817,54 @@ void Canvas::DrawText(int x, } } +/// @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, y}]; + cell.type = CellType::kText; // Epixu: should we add kCustom or something? + 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) { + Box crop = image.stencil; + + if (x < 0) { + crop.x_min -= x; + x = 0; + } + + if (y < 0) { + crop.y_min -= y; + y = 0; + } + + if (crop.Area() == 0) + return; + + const auto xend = x + crop.x_max - crop.x_min; + const auto yend = y + crop.y_max - crop.y_min; + + for (int py = y; py < yend; ++py) { + for (int px = x; px < xend; ++px) { + Cell& cell = storage_[XY{px, py}]; + cell.type = CellType::kText; // Epixu: should we add kCustom or something? + cell.content = image.PixelAt(crop.x_min + px - x, crop.y_min + py - y); + } + } +} + /// @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) { diff --git a/src/ftxui/screen/box.cpp b/src/ftxui/screen/box.cpp index 38fe484c1..e3eba85bd 100644 --- a/src/ftxui/screen/box.cpp +++ b/src/ftxui/screen/box.cpp @@ -39,6 +39,13 @@ bool Box::Contain(int x, int y) const { y_max >= y; } +/// @return the are of the box +/// @ingroup screen +int Box::Area() const noexcept { + const auto area = (x_max - x_min) * (y_max - y_min); + return area < 0 ? 0 : area; +} + /// @return whether |other| is the same as |this| /// @ingroup screen bool Box::operator==(const Box& other) const { diff --git a/src/ftxui/screen/image.cpp b/src/ftxui/screen/image.cpp new file mode 100644 index 000000000..adee91b25 --- /dev/null +++ b/src/ftxui/screen/image.cpp @@ -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 // for size_t +#include // for operator<<, stringstream, basic_ostream, flush, cout, ostream +#include +#include // for _Rb_tree_const_iterator, map, operator!=, operator== +#include // for allocator, allocator_traits<>::value_type +#include // IWYU pragma: keep +#include // 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(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 diff --git a/src/ftxui/screen/screen.cpp b/src/ftxui/screen/screen.cpp index 2a12219ea..981a3f62a 100644 --- a/src/ftxui/screen/screen.cpp +++ b/src/ftxui/screen/screen.cpp @@ -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(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; From 0279bed8c4a3718802aa8e56ebcff84a9173332e Mon Sep 17 00:00:00 2001 From: ArthurSonzogni Date: Thu, 11 Apr 2024 23:07:09 +0200 Subject: [PATCH 2/5] Add Box::IsEmpty() --- CHANGELOG.md | 3 +++ include/ftxui/screen/box.hpp | 2 +- src/ftxui/dom/canvas.cpp | 3 ++- src/ftxui/screen/box.cpp | 7 +++---- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a976af79..b441a05e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,9 @@ current (development) reflecting the current scroll position. Proposed by @ibrahimnasson in [issue 752](https://github.com/ArthurSonzogni/FTXUI/issues/752) +### Screen +- Feature: Add `Box::IsEmpty()`. + ### Build - Support for cmake's "unity/jumbo" builds. Fixed by @ClausKlein. diff --git a/include/ftxui/screen/box.hpp b/include/ftxui/screen/box.hpp index 32e08fff7..9fc279215 100644 --- a/include/ftxui/screen/box.hpp +++ b/include/ftxui/screen/box.hpp @@ -15,7 +15,7 @@ struct Box { static auto Intersection(Box a, Box b) -> Box; static auto Union(Box a, Box b) -> Box; bool Contain(int x, int y) const; - int Area() const noexcept; + bool IsEmpty(); bool operator==(const Box& other) const; bool operator!=(const Box& other) const; }; diff --git a/src/ftxui/dom/canvas.cpp b/src/ftxui/dom/canvas.cpp index c441e6da5..854c8c6cf 100644 --- a/src/ftxui/dom/canvas.cpp +++ b/src/ftxui/dom/canvas.cpp @@ -850,8 +850,9 @@ void Canvas::DrawImage(int x, y = 0; } - if (crop.Area() == 0) + if (crop.IsEmpty()) { return; + } const auto xend = x + crop.x_max - crop.x_min; const auto yend = y + crop.y_max - crop.y_min; diff --git a/src/ftxui/screen/box.cpp b/src/ftxui/screen/box.cpp index e3eba85bd..0210153b0 100644 --- a/src/ftxui/screen/box.cpp +++ b/src/ftxui/screen/box.cpp @@ -39,11 +39,10 @@ bool Box::Contain(int x, int y) const { y_max >= y; } -/// @return the are of the box +/// @return whether the box is empty. /// @ingroup screen -int Box::Area() const noexcept { - const auto area = (x_max - x_min) * (y_max - y_min); - return area < 0 ? 0 : area; +bool Box::IsEmpty() { + return x_min > x_max || y_min > y_max; } /// @return whether |other| is the same as |this| From 9eccde5c3c685ff3873935ba26625b9a573a48de Mon Sep 17 00:00:00 2001 From: ArthurSonzogni Date: Thu, 11 Apr 2024 23:24:14 +0200 Subject: [PATCH 3/5] Move pixel to its own file. --- include/ftxui/screen/image.hpp | 41 ++--------------------------- include/ftxui/screen/pixel.hpp | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 39 deletions(-) create mode 100644 include/ftxui/screen/pixel.hpp diff --git a/include/ftxui/screen/image.hpp b/include/ftxui/screen/image.hpp index 9bcb95b3e..d81c723a3 100644 --- a/include/ftxui/screen/image.hpp +++ b/include/ftxui/screen/image.hpp @@ -9,48 +9,11 @@ #include // for string, basic_string, allocator #include // for vector -#include "ftxui/screen/color.hpp" // for Color, Color::Default -#include "ftxui/screen/box.hpp" // for Box +#include "ftxui/screen/box.hpp" // for Box +#include "ftxui/screen/pixel.hpp" // for Pixel namespace ftxui { -/// @brief A unicode character and its associated style. -/// @ingroup screen -struct Pixel { - Pixel() - : blink(false), - bold(false), - dim(false), - inverted(false), - underlined(false), - underlined_double(false), - strikethrough(false), - automerge(false) {} - - // A bit field representing the style: - bool blink : 1; - bool bold : 1; - bool dim : 1; - bool inverted : 1; - bool underlined : 1; - bool underlined_double : 1; - bool strikethrough : 1; - bool automerge : 1; - - // The hyperlink associated with the pixel. - // 0 is the default value, meaning no hyperlink. - // It's an index for accessing Screen meta data - uint8_t hyperlink = 0; - - // The graphemes stored into the pixel. To support combining characters, - // like: a?, this can potentially contain multiple codepoints. - std::string character = " "; - - // Colors: - Color background_color = Color::Default; - Color foreground_color = Color::Default; -}; - /// @brief A rectangular grid of Pixel. /// @ingroup screen class Image { diff --git a/include/ftxui/screen/pixel.hpp b/include/ftxui/screen/pixel.hpp new file mode 100644 index 000000000..0a58a3898 --- /dev/null +++ b/include/ftxui/screen/pixel.hpp @@ -0,0 +1,48 @@ +// Copyright 2024 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 // for uint8_t +#include // for string, basic_string, allocator +#include "ftxui/screen/color.hpp" // for Color, Color::Default + +namespace ftxui { + +/// @brief A Unicode character and its associated style. +/// @ingroup screen +struct Pixel { + Pixel() + : blink(false), + bold(false), + dim(false), + inverted(false), + underlined(false), + underlined_double(false), + strikethrough(false), + automerge(false) {} + + // A bit field representing the style: + bool blink : 1; + bool bold : 1; + bool dim : 1; + bool inverted : 1; + bool underlined : 1; + bool underlined_double : 1; + bool strikethrough : 1; + bool automerge : 1; + + // The hyperlink associated with the pixel. + // 0 is the default value, meaning no hyperlink. + // It's an index for accessing Screen meta data + uint8_t hyperlink = 0; + + // The graphemes stored into the pixel. To support combining characters, + // like: a?, this can potentially contain multiple codepoints. + std::string character = " "; + + // Colors: + Color background_color = Color::Default; + Color foreground_color = Color::Default; +}; + +} // namespace ftxui From 94b0e97900875556b5c39f19f7665cdde4ced903 Mon Sep 17 00:00:00 2001 From: ArthurSonzogni Date: Thu, 11 Apr 2024 23:26:07 +0200 Subject: [PATCH 4/5] Update copyright date. --- CMakeLists.txt | 1 + include/ftxui/screen/image.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a0b7a556..65f41dbdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ add_library(screen include/ftxui/screen/color.hpp include/ftxui/screen/color_info.hpp include/ftxui/screen/image.hpp + include/ftxui/screen/pixel.hpp include/ftxui/screen/screen.hpp include/ftxui/screen/string.hpp src/ftxui/screen/box.cpp diff --git a/include/ftxui/screen/image.hpp b/include/ftxui/screen/image.hpp index d81c723a3..1790a9710 100644 --- a/include/ftxui/screen/image.hpp +++ b/include/ftxui/screen/image.hpp @@ -1,4 +1,4 @@ -// Copyright 2020 Arthur Sonzogni. All rights reserved. +// Copyright 2024 Arthur Sonzogni. All rights reserved. // Use of this source code is governed by the MIT license that can be found in // the LICENSE file. #ifndef FTXUI_SCREEN_IMAGE_HPP From 6e613520c63f44da46eaa543fee5fd405609c996 Mon Sep 17 00:00:00 2001 From: ArthurSonzogni Date: Thu, 11 Apr 2024 23:39:51 +0200 Subject: [PATCH 5/5] Rename Canvas::CellType::kText and simplify. --- include/ftxui/dom/canvas.hpp | 14 ++++++---- src/ftxui/dom/canvas.cpp | 53 ++++++++++++++---------------------- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/include/ftxui/dom/canvas.hpp b/include/ftxui/dom/canvas.hpp index 5c59d0e57..ffc487c7d 100644 --- a/include/ftxui/dom/canvas.hpp +++ b/include/ftxui/dom/canvas.hpp @@ -96,7 +96,8 @@ struct Canvas { void DrawText(int x, int y, const std::string& value, const Stylizer& style); // Draw using directly pixels or images -------------------------------------- - // Pixel/image coordinates correspond 1:1 + // x is considered to be a multiple of 2. + // y is considered to be a multiple of 4. void DrawPixel(int x, int y, const Pixel&); void DrawImage(int x, int y, const Image&); @@ -109,15 +110,18 @@ struct Canvas { bool IsIn(int x, int y) const { return x >= 0 && x < width_ && y >= 0 && y < height_; } + enum CellType { - kBraille, - kBlock, - kText, + kCell, // Units of size 2x4 + kBlock, // Units of size 2x2 + kBraille, // Units of size 1x1 }; + struct Cell { - CellType type = kText; + CellType type = kCell; Pixel content; }; + struct XY { int x; int y; diff --git a/src/ftxui/dom/canvas.cpp b/src/ftxui/dom/canvas.cpp index 854c8c6cf..81d415b03 100644 --- a/src/ftxui/dom/canvas.cpp +++ b/src/ftxui/dom/canvas.cpp @@ -810,7 +810,7 @@ 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; @@ -821,11 +821,9 @@ void Canvas::DrawText(int x, /// @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, y}]; - cell.type = CellType::kText; // Epixu: should we add kCustom or something? +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; } @@ -835,33 +833,22 @@ void Canvas::DrawPixel(int x, /// @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) { - Box crop = image.stencil; - - if (x < 0) { - crop.x_min -= x; - x = 0; - } - - if (y < 0) { - crop.y_min -= y; - y = 0; - } - - if (crop.IsEmpty()) { - return; - } - - const auto xend = x + crop.x_max - crop.x_min; - const auto yend = y + crop.y_max - crop.y_min; - - for (int py = y; py < yend; ++py) { - for (int px = x; px < xend; ++px) { - Cell& cell = storage_[XY{px, py}]; - cell.type = CellType::kText; // Epixu: should we add kCustom or something? - cell.content = image.PixelAt(crop.x_min + px - x, crop.y_min + py - y); +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); } } }