Skip to content

Commit 293ff17

Browse files
Separate a reusable Image class from Screen (#834)
Co-authored-by: ArthurSonzogni <[email protected]>
1 parent 1f6e110 commit 293ff17

File tree

12 files changed

+243
-107
lines changed

12 files changed

+243
-107
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
*
44
!*/
55

6+
# Ignore build directories generated by default MSVC CMake integration
7+
# (otherwise causes terribly slow indexing)
8+
out/
9+
610
# Allowed top-level files:
711
!.clang-format
812
!.clang-tidy

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ current (development)
3232
reflecting the current scroll position. Proposed by @ibrahimnasson in
3333
[issue 752](https://github.com/ArthurSonzogni/FTXUI/issues/752)
3434

35+
### Screen
36+
- Feature: Add `Box::IsEmpty()`.
37+
3538
### Build
3639
- Support for cmake's "unity/jumbo" builds. Fixed by @ClausKlein.
3740

CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@ add_library(screen
3333
include/ftxui/screen/box.hpp
3434
include/ftxui/screen/color.hpp
3535
include/ftxui/screen/color_info.hpp
36+
include/ftxui/screen/image.hpp
37+
include/ftxui/screen/pixel.hpp
3638
include/ftxui/screen/screen.hpp
3739
include/ftxui/screen/string.hpp
3840
src/ftxui/screen/box.cpp
3941
src/ftxui/screen/color.cpp
4042
src/ftxui/screen/color_info.cpp
43+
src/ftxui/screen/image.cpp
4144
src/ftxui/screen/screen.cpp
4245
src/ftxui/screen/string.cpp
4346
src/ftxui/screen/terminal.cpp

include/ftxui/dom/canvas.hpp

+14-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#include <unordered_map> // for unordered_map
1111

1212
#include "ftxui/screen/color.hpp" // for Color
13-
#include "ftxui/screen/screen.hpp" // for Pixel
13+
#include "ftxui/screen/image.hpp" // for Pixel, Image
1414

1515
#ifdef DrawText
1616
// Workaround for WinUsr.h (via Windows.h) defining macros that break things.
@@ -94,6 +94,12 @@ struct Canvas {
9494
void DrawText(int x, int y, const std::string& value);
9595
void DrawText(int x, int y, const std::string& value, const Color& color);
9696
void DrawText(int x, int y, const std::string& value, const Stylizer& style);
97+
98+
// Draw using directly pixels or images --------------------------------------
99+
// x is considered to be a multiple of 2.
100+
// y is considered to be a multiple of 4.
101+
void DrawPixel(int x, int y, const Pixel&);
102+
void DrawImage(int x, int y, const Image&);
97103

98104
// Decorator:
99105
// x is considered to be a multiple of 2.
@@ -104,15 +110,18 @@ struct Canvas {
104110
bool IsIn(int x, int y) const {
105111
return x >= 0 && x < width_ && y >= 0 && y < height_;
106112
}
113+
107114
enum CellType {
108-
kBraille,
109-
kBlock,
110-
kText,
115+
kCell, // Units of size 2x4
116+
kBlock, // Units of size 2x2
117+
kBraille, // Units of size 1x1
111118
};
119+
112120
struct Cell {
113-
CellType type = kText;
121+
CellType type = kCell;
114122
Pixel content;
115123
};
124+
116125
struct XY {
117126
int x;
118127
int y;

include/ftxui/screen/box.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ struct Box {
1515
static auto Intersection(Box a, Box b) -> Box;
1616
static auto Union(Box a, Box b) -> Box;
1717
bool Contain(int x, int y) const;
18+
bool IsEmpty();
1819
bool operator==(const Box& other) const;
1920
bool operator!=(const Box& other) const;
2021
};

include/ftxui/screen/image.hpp

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2024 Arthur Sonzogni. All rights reserved.
2+
// Use of this source code is governed by the MIT license that can be found in
3+
// the LICENSE file.
4+
#ifndef FTXUI_SCREEN_IMAGE_HPP
5+
#define FTXUI_SCREEN_IMAGE_HPP
6+
7+
#include <cstdint> // for uint8_t
8+
#include <memory>
9+
#include <string> // for string, basic_string, allocator
10+
#include <vector> // for vector
11+
12+
#include "ftxui/screen/box.hpp" // for Box
13+
#include "ftxui/screen/pixel.hpp" // for Pixel
14+
15+
namespace ftxui {
16+
17+
/// @brief A rectangular grid of Pixel.
18+
/// @ingroup screen
19+
class Image {
20+
public:
21+
// Constructors:
22+
Image() = delete;
23+
Image(int dimx, int dimy);
24+
25+
// Access a character in the grid at a given position.
26+
std::string& at(int x, int y);
27+
const std::string& at(int x, int y) const;
28+
29+
// Access a cell (Pixel) in the grid at a given position.
30+
Pixel& PixelAt(int x, int y);
31+
const Pixel& PixelAt(int x, int y) const;
32+
33+
// Get screen dimensions.
34+
int dimx() const { return dimx_; }
35+
int dimy() const { return dimy_; }
36+
37+
// Fill the image with space and default style
38+
void Clear();
39+
40+
Box stencil;
41+
42+
protected:
43+
int dimx_;
44+
int dimy_;
45+
std::vector<std::vector<Pixel>> pixels_;
46+
};
47+
48+
} // namespace ftxui
49+
50+
#endif // FTXUI_SCREEN_IMAGE_HPP

include/ftxui/screen/pixel.hpp

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2024 Arthur Sonzogni. All rights reserved.
2+
// Use of this source code is governed by the MIT license that can be found in
3+
// the LICENSE file.
4+
5+
#include <cstdint> // for uint8_t
6+
#include <string> // for string, basic_string, allocator
7+
#include "ftxui/screen/color.hpp" // for Color, Color::Default
8+
9+
namespace ftxui {
10+
11+
/// @brief A Unicode character and its associated style.
12+
/// @ingroup screen
13+
struct Pixel {
14+
Pixel()
15+
: blink(false),
16+
bold(false),
17+
dim(false),
18+
inverted(false),
19+
underlined(false),
20+
underlined_double(false),
21+
strikethrough(false),
22+
automerge(false) {}
23+
24+
// A bit field representing the style:
25+
bool blink : 1;
26+
bool bold : 1;
27+
bool dim : 1;
28+
bool inverted : 1;
29+
bool underlined : 1;
30+
bool underlined_double : 1;
31+
bool strikethrough : 1;
32+
bool automerge : 1;
33+
34+
// The hyperlink associated with the pixel.
35+
// 0 is the default value, meaning no hyperlink.
36+
// It's an index for accessing Screen meta data
37+
uint8_t hyperlink = 0;
38+
39+
// The graphemes stored into the pixel. To support combining characters,
40+
// like: a?, this can potentially contain multiple codepoints.
41+
std::string character = " ";
42+
43+
// Colors:
44+
Color background_color = Color::Default;
45+
Color foreground_color = Color::Default;
46+
};
47+
48+
} // namespace ftxui

include/ftxui/screen/screen.hpp

+5-57
Original file line numberDiff line numberDiff line change
@@ -9,48 +9,12 @@
99
#include <string> // for string, basic_string, allocator
1010
#include <vector> // for vector
1111

12-
#include "ftxui/screen/box.hpp" // for Box
1312
#include "ftxui/screen/color.hpp" // for Color, Color::Default
13+
#include "ftxui/screen/image.hpp" // for Pixel, Image
1414
#include "ftxui/screen/terminal.hpp" // for Dimensions
1515

1616
namespace ftxui {
1717

18-
/// @brief A unicode character and its associated style.
19-
/// @ingroup screen
20-
struct Pixel {
21-
Pixel()
22-
: blink(false),
23-
bold(false),
24-
dim(false),
25-
inverted(false),
26-
underlined(false),
27-
underlined_double(false),
28-
strikethrough(false),
29-
automerge(false) {}
30-
31-
// A bit field representing the style:
32-
bool blink : 1;
33-
bool bold : 1;
34-
bool dim : 1;
35-
bool inverted : 1;
36-
bool underlined : 1;
37-
bool underlined_double : 1;
38-
bool strikethrough : 1;
39-
bool automerge : 1;
40-
41-
// The hyperlink associated with the pixel.
42-
// 0 is the default value, meaning no hyperlink.
43-
uint8_t hyperlink = 0;
44-
45-
// The graphemes stored into the pixel. To support combining characters,
46-
// like: a⃦, this can potentially contain multiple codepoints.
47-
std::string character = " ";
48-
49-
// Colors:
50-
Color background_color = Color::Default;
51-
Color foreground_color = Color::Default;
52-
};
53-
5418
/// @brief Define how the Screen's dimensions should look like.
5519
/// @ingroup screen
5620
namespace Dimension {
@@ -60,36 +24,24 @@ Dimensions Full();
6024

6125
/// @brief A rectangular grid of Pixel.
6226
/// @ingroup screen
63-
class Screen {
27+
class Screen : public Image {
6428
public:
6529
// Constructors:
6630
Screen(int dimx, int dimy);
6731
static Screen Create(Dimensions dimension);
6832
static Screen Create(Dimensions width, Dimensions height);
6933

70-
// Access a character in the grid at a given position.
71-
std::string& at(int x, int y);
72-
const std::string& at(int x, int y) const;
73-
74-
// Access a cell (Pixel) in the grid at a given position.
75-
Pixel& PixelAt(int x, int y);
76-
const Pixel& PixelAt(int x, int y) const;
77-
7834
std::string ToString() const;
7935

8036
// Print the Screen on to the terminal.
8137
void Print() const;
8238

83-
// Get screen dimensions.
84-
int dimx() const { return dimx_; }
85-
int dimy() const { return dimy_; }
39+
// Fill the screen with space and reset any screen state, like hyperlinks, and cursor
40+
void Clear();
8641

8742
// Move the terminal cursor n-lines up with n = dimy().
8843
std::string ResetPosition(bool clear = false) const;
8944

90-
// Fill the screen with space.
91-
void Clear();
92-
9345
void ApplyShader();
9446

9547
struct Cursor {
@@ -107,6 +59,7 @@ class Screen {
10759
};
10860
Shape shape;
10961
};
62+
11063
Cursor cursor() const { return cursor_; }
11164
void SetCursor(Cursor cursor) { cursor_ = cursor; }
11265

@@ -115,12 +68,7 @@ class Screen {
11568
uint8_t RegisterHyperlink(const std::string& link);
11669
const std::string& Hyperlink(uint8_t id) const;
11770

118-
Box stencil;
119-
12071
protected:
121-
int dimx_;
122-
int dimy_;
123-
std::vector<std::vector<Pixel>> pixels_;
12472
Cursor cursor_;
12573
std::vector<std::string> hyperlinks_ = {""};
12674
};

src/ftxui/dom/canvas.cpp

+37-1
Original file line numberDiff line numberDiff line change
@@ -810,13 +810,49 @@ void Canvas::DrawText(int x,
810810
continue;
811811
}
812812
Cell& cell = storage_[XY{x / 2, y / 4}];
813-
cell.type = CellType::kText;
813+
cell.type = CellType::kCell;
814814
cell.content.character = it;
815815
style(cell.content);
816816
x += 2;
817817
}
818818
}
819819

820+
/// @brief Directly draw a predefined pixel at the given coordinate
821+
/// @param x the x coordinate of the pixel.
822+
/// @param y the y coordinate of the pixel.
823+
/// @param p the pixel to draw.
824+
void Canvas::DrawPixel(int x, int y, const Pixel& p) {
825+
Cell& cell = storage_[XY{x / 2, y / 4}];
826+
cell.type = CellType::kCell;
827+
cell.content = p;
828+
}
829+
830+
/// @brief Draw a predefined image, with top-left corner at the given coordinate
831+
/// You can supply negative coordinates to align the image however you like -
832+
/// only the 'visible' portion will be drawn
833+
/// @param x the x coordinate corresponding to the top-left corner of the image.
834+
/// @param y the y coordinate corresponding to the top-left corner of the image.
835+
/// @param image the image to draw.
836+
void Canvas::DrawImage(int x, int y, const Image& image) {
837+
x /= 2;
838+
y /= 4;
839+
const int dx_begin = std::max(0, -x);
840+
const int dy_begin = std::max(0, -y);
841+
const int dx_end = std::min(image.dimx(), width_ - x);
842+
const int dy_end = std::min(image.dimy(), height_ - y);
843+
844+
for (int dy = dy_begin; dy < dy_end; ++dy) {
845+
for (int dx = dx_begin; dx < dx_end; ++dx) {
846+
Cell& cell = storage_[XY{
847+
x + dx,
848+
y + dy,
849+
}];
850+
cell.type = CellType::kCell;
851+
cell.content = image.PixelAt(dx, dy);
852+
}
853+
}
854+
}
855+
820856
/// @brief Modify a pixel at a given location.
821857
/// @param style a function that modifies the pixel.
822858
void Canvas::Style(int x, int y, const Stylizer& style) {

src/ftxui/screen/box.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ bool Box::Contain(int x, int y) const {
3939
y_max >= y;
4040
}
4141

42+
/// @return whether the box is empty.
43+
/// @ingroup screen
44+
bool Box::IsEmpty() {
45+
return x_min > x_max || y_min > y_max;
46+
}
47+
4248
/// @return whether |other| is the same as |this|
4349
/// @ingroup screen
4450
bool Box::operator==(const Box& other) const {

0 commit comments

Comments
 (0)