Skip to content

Commit

Permalink
Separate a reusable Image class from Screen (#834)
Browse files Browse the repository at this point in the history
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
  • Loading branch information
Epixu and ArthurSonzogni authored Apr 27, 2024
1 parent 1f6e110 commit 293ff17
Show file tree
Hide file tree
Showing 12 changed files with 243 additions and 107 deletions.
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/

# 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 @@ -32,6 +32,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

0 comments on commit 293ff17

Please sign in to comment.