Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate a reusable Image class from Screen #834

Merged
merged 5 commits into from
Apr 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
*
!*/

# Ignore build directories generated by default MSVC CMake integration
# (otherwise causes terribly slow indexing)
out/

ArthurSonzogni marked this conversation as resolved.
Show resolved Hide resolved
# Allowed top-level files:
!.clang-format
!.clang-tidy
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@ 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/pixel.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
Expand Down
19 changes: 14 additions & 5 deletions include/ftxui/dom/canvas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#include <unordered_map> // 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.
Expand Down Expand Up @@ -94,6 +94,12 @@ 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 --------------------------------------
// 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&);

// Decorator:
// x is considered to be a multiple of 2.
Expand All @@ -104,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;
Expand Down
1 change: 1 addition & 0 deletions include/ftxui/screen/box.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
bool IsEmpty();
bool operator==(const Box& other) const;
bool operator!=(const Box& other) const;
};
Expand Down
50 changes: 50 additions & 0 deletions include/ftxui/screen/image.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// 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
#define FTXUI_SCREEN_IMAGE_HPP

#include <cstdint> // for uint8_t
#include <memory>
#include <string> // for string, basic_string, allocator
#include <vector> // for vector

#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/pixel.hpp" // for Pixel

namespace ftxui {

/// @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<std::vector<Pixel>> pixels_;
};

} // namespace ftxui

#endif // FTXUI_SCREEN_IMAGE_HPP
48 changes: 48 additions & 0 deletions include/ftxui/screen/pixel.hpp
Original file line number Diff line number Diff line change
@@ -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 <cstdint> // for uint8_t
#include <string> // 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
62 changes: 5 additions & 57 deletions include/ftxui/screen/screen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,12 @@
#include <string> // for string, basic_string, allocator
#include <vector> // 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 {
Expand All @@ -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 {
Expand All @@ -107,6 +59,7 @@ class Screen {
};
Shape shape;
};

Cursor cursor() const { return cursor_; }
void SetCursor(Cursor cursor) { cursor_ = cursor; }

Expand All @@ -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<std::vector<Pixel>> pixels_;
Cursor cursor_;
std::vector<std::string> hyperlinks_ = {""};
};
Expand Down
38 changes: 37 additions & 1 deletion src/ftxui/dom/canvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
6 changes: 6 additions & 0 deletions src/ftxui/screen/box.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading
Loading