mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-22 10:40:00 +08:00
Performance improvement by refactoring pixel styles (#704)
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
This commit is contained in:
parent
00e63993ce
commit
e2a205ed0d
@ -38,6 +38,8 @@ current (development)
|
||||
### Screen
|
||||
- Breaking: `WordBreakProperty` becomes a uint8_t enum. This yields a 0.8%
|
||||
performance improvement.
|
||||
- Breaking: Remove user defined Pixel constructor and equality operator.
|
||||
- Performance: 19% faster on benchmarks.
|
||||
|
||||
|
||||
### Build
|
||||
|
@ -15,19 +15,15 @@ namespace ftxui {
|
||||
/// @brief A unicode character and its associated style.
|
||||
/// @ingroup screen
|
||||
struct Pixel {
|
||||
bool operator==(const Pixel& other) const;
|
||||
|
||||
// The graphemes stored into the pixel. To support combining characters,
|
||||
// like: a⃦, this can potentially contain multiple codepoints.
|
||||
std::string character = " ";
|
||||
|
||||
// The hyperlink associated with the pixel.
|
||||
// 0 is the default value, meaning no hyperlink.
|
||||
uint8_t hyperlink = 0;
|
||||
|
||||
// Colors:
|
||||
Color background_color = Color::Default;
|
||||
Color foreground_color = Color::Default;
|
||||
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;
|
||||
@ -39,15 +35,17 @@ struct Pixel {
|
||||
bool strikethrough : 1;
|
||||
bool automerge : 1;
|
||||
|
||||
Pixel()
|
||||
: blink(false),
|
||||
bold(false),
|
||||
dim(false),
|
||||
inverted(false),
|
||||
underlined(false),
|
||||
underlined_double(false),
|
||||
strikethrough(false),
|
||||
automerge(false) {}
|
||||
// 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.
|
||||
|
@ -91,7 +91,7 @@ Canvas::Canvas(int width, int height)
|
||||
/// @param y the y coordinate of the cell.
|
||||
Pixel Canvas::GetPixel(int x, int y) const {
|
||||
auto it = storage_.find(XY{x, y});
|
||||
return (it == storage_.end()) ? Pixel{} : it->second.content;
|
||||
return (it == storage_.end()) ? Pixel() : it->second.content;
|
||||
}
|
||||
|
||||
/// @brief Draw a braille dot.
|
||||
|
@ -18,6 +18,23 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
// Macro for hinting that an expression is likely to be false.
|
||||
#if !defined(FTXUI_UNLIKELY)
|
||||
#if defined(COMPILER_GCC) || defined(__clang__)
|
||||
#define FTXUI_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
#else
|
||||
#define FTXUI_UNLIKELY(x) (x)
|
||||
#endif // defined(COMPILER_GCC)
|
||||
#endif // !defined(FTXUI_UNLIKELY)
|
||||
|
||||
#if !defined(FTXUI_LIKELY)
|
||||
#if defined(COMPILER_GCC) || defined(__clang__)
|
||||
#define FTXUI_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
#else
|
||||
#define FTXUI_LIKELY(x) (x)
|
||||
#endif // defined(COMPILER_GCC)
|
||||
#endif // !defined(FTXUI_LIKELY)
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
namespace {
|
||||
@ -53,80 +70,53 @@ void WindowsEmulateVT100Terminal() {
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void UpdatePixelStyle(const Screen* screen,
|
||||
std::stringstream& ss,
|
||||
Pixel& previous,
|
||||
const Pixel& prev,
|
||||
const Pixel& next) {
|
||||
// See https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
|
||||
if (next.hyperlink != previous.hyperlink) {
|
||||
if (FTXUI_UNLIKELY(next.hyperlink != prev.hyperlink)) {
|
||||
ss << "\x1B]8;;" << screen->Hyperlink(next.hyperlink) << "\x1B\\";
|
||||
}
|
||||
|
||||
if ((!next.bold && previous.bold) || //
|
||||
(!next.dim && previous.dim)) {
|
||||
ss << "\x1B[22m"; // BOLD_RESET and DIM_RESET
|
||||
// We might have wrongfully reset dim or bold because they share the same
|
||||
// resetter. Take it into account so that the side effect will cause it to
|
||||
// be set again below.
|
||||
previous.bold = false;
|
||||
previous.dim = false;
|
||||
// Bold
|
||||
if (FTXUI_UNLIKELY(next.bold != prev.bold || next.dim != prev.dim)) {
|
||||
// BOLD_AND_DIM_RESET:
|
||||
ss << ((prev.bold && !next.bold) || (prev.dim && !next.dim) ? "\x1B[22m"
|
||||
: "");
|
||||
ss << (next.bold ? "\x1B[1m" : ""); // BOLD_SET
|
||||
ss << (next.dim ? "\x1B[2m" : ""); // DIM_SET
|
||||
}
|
||||
|
||||
if ((!next.underlined && previous.underlined) ||
|
||||
(!next.underlined_double && previous.underlined_double)) {
|
||||
// We might have wrongfully reset underlined or underlinedbold because they
|
||||
// share the same resetter. Take it into account so that the side effect
|
||||
// will cause it to be set again below.
|
||||
ss << "\x1B[24m"; // UNDERLINED_RESET
|
||||
previous.underlined = false;
|
||||
previous.underlined_double = false;
|
||||
// Underline
|
||||
if (FTXUI_UNLIKELY(next.underlined != prev.underlined ||
|
||||
next.underlined_double != prev.underlined_double)) {
|
||||
ss << (next.underlined ? "\x1B[4m" // UNDERLINE
|
||||
: next.underlined_double ? "\x1B[21m" // UNDERLINE_DOUBLE
|
||||
: "\x1B[24m"); // UNDERLINE_RESET
|
||||
}
|
||||
|
||||
if (next.bold && !previous.bold) {
|
||||
ss << "\x1B[1m"; // BOLD_SET
|
||||
// Blink
|
||||
if (FTXUI_UNLIKELY(next.blink != prev.blink)) {
|
||||
ss << (next.blink ? "\x1B[5m" // BLINK_SET
|
||||
: "\x1B[25m"); // BLINK_RESET
|
||||
}
|
||||
|
||||
if (next.dim && !previous.dim) {
|
||||
ss << "\x1B[2m"; // DIM_SET
|
||||
// Inverted
|
||||
if (FTXUI_UNLIKELY(next.inverted != prev.inverted)) {
|
||||
ss << (next.inverted ? "\x1B[7m" // INVERTED_SET
|
||||
: "\x1B[27m"); // INVERTED_RESET
|
||||
}
|
||||
|
||||
if (next.underlined && !previous.underlined) {
|
||||
ss << "\x1B[4m"; // UNDERLINED_SET
|
||||
// StrikeThrough
|
||||
if (FTXUI_UNLIKELY(next.strikethrough != prev.strikethrough)) {
|
||||
ss << (next.strikethrough ? "\x1B[9m" // CROSSED_OUT
|
||||
: "\x1B[29m"); // CROSSED_OUT_RESET
|
||||
}
|
||||
|
||||
if (next.blink && !previous.blink) {
|
||||
ss << "\x1B[5m"; // BLINK_SET
|
||||
}
|
||||
|
||||
if (!next.blink && previous.blink) {
|
||||
ss << "\x1B[25m"; // BLINK_RESET
|
||||
}
|
||||
|
||||
if (next.inverted && !previous.inverted) {
|
||||
ss << "\x1B[7m"; // INVERTED_SET
|
||||
}
|
||||
|
||||
if (!next.inverted && previous.inverted) {
|
||||
ss << "\x1B[27m"; // INVERTED_RESET
|
||||
}
|
||||
|
||||
if (next.strikethrough && !previous.strikethrough) {
|
||||
ss << "\x1B[9m"; // CROSSED_OUT
|
||||
}
|
||||
|
||||
if (!next.strikethrough && previous.strikethrough) {
|
||||
ss << "\x1B[29m"; // CROSSED_OUT_RESET
|
||||
}
|
||||
|
||||
if (next.underlined_double && !previous.underlined_double) {
|
||||
ss << "\x1B[21m"; // DOUBLE_UNDERLINED_SET
|
||||
}
|
||||
|
||||
if (next.foreground_color != previous.foreground_color ||
|
||||
next.background_color != previous.background_color) {
|
||||
if (FTXUI_UNLIKELY(next.foreground_color != prev.foreground_color ||
|
||||
next.background_color != prev.background_color)) {
|
||||
ss << "\x1B[" + next.foreground_color.Print(false) + "m";
|
||||
ss << "\x1B[" + next.background_color.Print(true) + "m";
|
||||
}
|
||||
|
||||
previous = next;
|
||||
}
|
||||
|
||||
struct TileEncoding {
|
||||
@ -373,18 +363,6 @@ bool ShouldAttemptAutoMerge(Pixel& pixel) {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Pixel::operator==(const Pixel& other) const {
|
||||
return character == other.character && //
|
||||
background_color == other.background_color && //
|
||||
foreground_color == other.foreground_color && //
|
||||
blink == other.blink && //
|
||||
bold == other.bold && //
|
||||
dim == other.dim && //
|
||||
inverted == other.inverted && //
|
||||
underlined == other.underlined && //
|
||||
automerge == other.automerge; //
|
||||
}
|
||||
|
||||
/// A fixed dimension.
|
||||
/// @see Fit
|
||||
/// @see Full
|
||||
@ -435,25 +413,31 @@ Screen::Screen(int dimx, int dimy)
|
||||
std::string Screen::ToString() const {
|
||||
std::stringstream ss;
|
||||
|
||||
Pixel previous_pixel;
|
||||
const Pixel final_pixel;
|
||||
const Pixel default_pixel;
|
||||
const Pixel* previous_pixel_ref = &default_pixel;
|
||||
|
||||
for (int y = 0; y < dimy_; ++y) {
|
||||
// New line in between two lines.
|
||||
if (y != 0) {
|
||||
UpdatePixelStyle(this, ss, previous_pixel, final_pixel);
|
||||
UpdatePixelStyle(this, ss, *previous_pixel_ref, default_pixel);
|
||||
previous_pixel_ref = &default_pixel;
|
||||
ss << "\r\n";
|
||||
}
|
||||
|
||||
// After printing a fullwith character, we need to skip the next cell.
|
||||
bool previous_fullwidth = false;
|
||||
for (const auto& pixel : pixels_[y]) {
|
||||
if (!previous_fullwidth) {
|
||||
UpdatePixelStyle(this, ss, previous_pixel, pixel);
|
||||
UpdatePixelStyle(this, ss, *previous_pixel_ref, pixel);
|
||||
previous_pixel_ref = &pixel;
|
||||
ss << pixel.character;
|
||||
}
|
||||
previous_fullwidth = (string_width(pixel.character) == 2);
|
||||
}
|
||||
}
|
||||
|
||||
UpdatePixelStyle(this, ss, previous_pixel, final_pixel);
|
||||
// Reset the style to default:
|
||||
UpdatePixelStyle(this, ss, *previous_pixel_ref, default_pixel);
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user