This commit is contained in:
+99
-98
@@ -759,27 +759,85 @@ void set_source(cairo_t *cr, const Color &color) {
|
||||
cairo_set_source_rgb(cr, color.red, color.green, color.blue);
|
||||
}
|
||||
|
||||
void rounded_rectangle(cairo_t *cr, double x, double y, double width,
|
||||
double height, double radius) {
|
||||
radius = min(radius, min(width, height) / 2.);
|
||||
struct FontMetrics {
|
||||
string family;
|
||||
double em_size;
|
||||
int cell_width;
|
||||
int cell_height;
|
||||
double baseline;
|
||||
};
|
||||
|
||||
cairo_new_sub_path(cr);
|
||||
cairo_move_to(cr, x + radius, y);
|
||||
cairo_line_to(cr, x + width - radius, y);
|
||||
cairo_arc(
|
||||
cr, x + width - radius, y + radius, radius, -numbers::pi / 2., 0.);
|
||||
cairo_line_to(cr, x + width, y + height - radius);
|
||||
cairo_arc(cr, x + width - radius, y + height - radius, radius, 0.,
|
||||
numbers::pi / 2.);
|
||||
cairo_line_to(cr, x + radius, y + height);
|
||||
cairo_arc(cr, x + radius, y + height - radius, radius, numbers::pi / 2.,
|
||||
numbers::pi);
|
||||
cairo_line_to(cr, x, y + radius);
|
||||
cairo_arc(
|
||||
cr, x + radius, y + radius, radius, numbers::pi, 3. * numbers::pi / 2.);
|
||||
cairo_close_path(cr);
|
||||
FontMetrics measure_font(const string &family, double point_size) {
|
||||
SurfacePtr surface(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1),
|
||||
cairo_surface_destroy);
|
||||
if (cairo_surface_status(surface.get()) != CAIRO_STATUS_SUCCESS)
|
||||
fail("cannot create Cairo font measurement surface");
|
||||
|
||||
CairoPtr cr(cairo_create(surface.get()), cairo_destroy);
|
||||
cairo_select_font_face(cr.get(), family.c_str(), CAIRO_FONT_SLANT_NORMAL,
|
||||
CAIRO_FONT_WEIGHT_NORMAL);
|
||||
|
||||
const double em_size = point_size * kRenderDpi / 72.;
|
||||
cairo_set_font_size(cr.get(), em_size);
|
||||
cairo_font_extents_t extents{};
|
||||
cairo_font_extents(cr.get(), &extents);
|
||||
cairo_text_extents_t em_glyph{};
|
||||
cairo_text_extents(cr.get(), "M", &em_glyph);
|
||||
if (cairo_status(cr.get()) != CAIRO_STATUS_SUCCESS ||
|
||||
!isfinite(extents.height) || !isfinite(em_glyph.x_advance))
|
||||
fail("cannot measure font " + family);
|
||||
|
||||
const int cell_width = max(1, int(lround(em_glyph.x_advance)));
|
||||
const int cell_height = max(1, int(lround(extents.height)));
|
||||
return {
|
||||
family,
|
||||
em_size,
|
||||
cell_width,
|
||||
cell_height,
|
||||
(cell_height - extents.ascent - extents.descent) / 2. + extents.ascent,
|
||||
};
|
||||
}
|
||||
|
||||
// --- Rendering ---------------------------------------------------------------
|
||||
|
||||
int canvas_dimension(int cells, int cell_size, int border) {
|
||||
const int64_t result = int64_t(cells) * cell_size + 2 * int64_t(border);
|
||||
if (result > WEBP_MAX_DIMENSION)
|
||||
fail("rendering options make the WebP canvas too large");
|
||||
|
||||
return int(result);
|
||||
}
|
||||
|
||||
class Renderer {
|
||||
FontMetrics font_;
|
||||
const Theme &theme_;
|
||||
BadgePosition badge_position_;
|
||||
int border_;
|
||||
int width_;
|
||||
int height_;
|
||||
|
||||
void render_cell(cairo_t *cr, const Terminal &terminal,
|
||||
const VTermPos &cursor, const VTermScreenCell &cell, int row, int col,
|
||||
int cell_columns) const;
|
||||
void render_key(cairo_t *cr, const string &key) const;
|
||||
|
||||
public:
|
||||
Renderer(const Cast &cast, const Options &options);
|
||||
|
||||
[[nodiscard]] SurfacePtr render(
|
||||
const Terminal &terminal, const string &key) const;
|
||||
[[nodiscard]] int width() const { return width_; }
|
||||
[[nodiscard]] int height() const { return height_; }
|
||||
};
|
||||
|
||||
Renderer::Renderer(const Cast &cast, const Options &options)
|
||||
: font_(measure_font(options.font_name, options.point_size)),
|
||||
theme_(cast.header.theme), badge_position_(options.badge_position),
|
||||
border_(options.border),
|
||||
width_(canvas_dimension(cast.max_cols, font_.cell_width, border_)),
|
||||
height_(canvas_dimension(cast.max_rows, font_.cell_height, border_)) {}
|
||||
|
||||
// These look better pixel-perfect and properly adjusted to cell boundaries.
|
||||
bool add_block_element(cairo_t *cr, const VTermScreenCell &cell, double x,
|
||||
double y, double width, double height) {
|
||||
if (cell.chars[1])
|
||||
@@ -862,84 +920,6 @@ bool add_block_element(cairo_t *cr, const VTermScreenCell &cell, double x,
|
||||
return true;
|
||||
}
|
||||
|
||||
struct FontMetrics {
|
||||
string family;
|
||||
double em_size;
|
||||
int cell_width;
|
||||
int cell_height;
|
||||
double baseline;
|
||||
};
|
||||
|
||||
FontMetrics measure_font(const string &family, double point_size) {
|
||||
SurfacePtr surface(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1),
|
||||
cairo_surface_destroy);
|
||||
if (cairo_surface_status(surface.get()) != CAIRO_STATUS_SUCCESS)
|
||||
fail("cannot create Cairo font measurement surface");
|
||||
|
||||
CairoPtr cr(cairo_create(surface.get()), cairo_destroy);
|
||||
cairo_select_font_face(cr.get(), family.c_str(), CAIRO_FONT_SLANT_NORMAL,
|
||||
CAIRO_FONT_WEIGHT_NORMAL);
|
||||
|
||||
const double em_size = point_size * kRenderDpi / 72.;
|
||||
cairo_set_font_size(cr.get(), em_size);
|
||||
cairo_font_extents_t extents{};
|
||||
cairo_font_extents(cr.get(), &extents);
|
||||
cairo_text_extents_t em_glyph{};
|
||||
cairo_text_extents(cr.get(), "M", &em_glyph);
|
||||
if (cairo_status(cr.get()) != CAIRO_STATUS_SUCCESS ||
|
||||
!isfinite(extents.height) || !isfinite(em_glyph.x_advance))
|
||||
fail("cannot measure font " + family);
|
||||
|
||||
const int cell_width = max(1, int(lround(em_glyph.x_advance)));
|
||||
const int cell_height = max(1, int(lround(extents.height)));
|
||||
return {
|
||||
family,
|
||||
em_size,
|
||||
cell_width,
|
||||
cell_height,
|
||||
(cell_height - extents.ascent - extents.descent) / 2. + extents.ascent,
|
||||
};
|
||||
}
|
||||
|
||||
// --- Rendering ---------------------------------------------------------------
|
||||
|
||||
int canvas_dimension(int cells, int cell_size, int border) {
|
||||
const int64_t result = int64_t(cells) * cell_size + 2 * int64_t(border);
|
||||
if (result > WEBP_MAX_DIMENSION)
|
||||
fail("rendering options make the WebP canvas too large");
|
||||
|
||||
return int(result);
|
||||
}
|
||||
|
||||
class Renderer {
|
||||
FontMetrics font_;
|
||||
const Theme &theme_;
|
||||
BadgePosition badge_position_;
|
||||
int border_;
|
||||
int width_;
|
||||
int height_;
|
||||
|
||||
void render_cell(cairo_t *cr, const Terminal &terminal,
|
||||
const VTermPos &cursor, const VTermScreenCell &cell, int row, int col,
|
||||
int cell_columns) const;
|
||||
void render_key(cairo_t *cr, const string &key) const;
|
||||
|
||||
public:
|
||||
Renderer(const Cast &cast, const Options &options);
|
||||
|
||||
[[nodiscard]] SurfacePtr render(
|
||||
const Terminal &terminal, const string &key) const;
|
||||
[[nodiscard]] int width() const { return width_; }
|
||||
[[nodiscard]] int height() const { return height_; }
|
||||
};
|
||||
|
||||
Renderer::Renderer(const Cast &cast, const Options &options)
|
||||
: font_(measure_font(options.font_name, options.point_size)),
|
||||
theme_(cast.header.theme), badge_position_(options.badge_position),
|
||||
border_(options.border),
|
||||
width_(canvas_dimension(cast.max_cols, font_.cell_width, border_)),
|
||||
height_(canvas_dimension(cast.max_rows, font_.cell_height, border_)) {}
|
||||
|
||||
void Renderer::render_cell(cairo_t *cr, const Terminal &terminal,
|
||||
const VTermPos &cursor, const VTermScreenCell &cell, int row, int col,
|
||||
int cell_columns) const {
|
||||
@@ -993,6 +973,27 @@ double badge_coordinate(double offset, double canvas_size, double badge_size,
|
||||
return border + offset * em_size;
|
||||
}
|
||||
|
||||
void add_rounded_rectangle(cairo_t *cr, double x, double y, double width,
|
||||
double height, double radius) {
|
||||
radius = min(radius, min(width, height) / 2.);
|
||||
|
||||
cairo_new_sub_path(cr);
|
||||
cairo_move_to(cr, x + radius, y);
|
||||
cairo_line_to(cr, x + width - radius, y);
|
||||
cairo_arc(
|
||||
cr, x + width - radius, y + radius, radius, -numbers::pi / 2., 0.);
|
||||
cairo_line_to(cr, x + width, y + height - radius);
|
||||
cairo_arc(cr, x + width - radius, y + height - radius, radius, 0.,
|
||||
numbers::pi / 2.);
|
||||
cairo_line_to(cr, x + radius, y + height);
|
||||
cairo_arc(cr, x + radius, y + height - radius, radius, numbers::pi / 2.,
|
||||
numbers::pi);
|
||||
cairo_line_to(cr, x, y + radius);
|
||||
cairo_arc(
|
||||
cr, x + radius, y + radius, radius, numbers::pi, 3. * numbers::pi / 2.);
|
||||
cairo_close_path(cr);
|
||||
}
|
||||
|
||||
void Renderer::render_key(cairo_t *cr, const string &key) const {
|
||||
if (key.empty())
|
||||
return;
|
||||
@@ -1015,8 +1016,8 @@ void Renderer::render_key(cairo_t *cr, const string &key) const {
|
||||
const double y = badge_coordinate(
|
||||
badge_position_.top, height_, badge_height, border_, font.em_size);
|
||||
|
||||
rounded_rectangle(cr, x + 0.5, y + 0.5, badge_width - 1., badge_height - 1.,
|
||||
font.em_size * 0.75);
|
||||
add_rounded_rectangle(cr, x + 0.5, y + 0.5, badge_width - 1.,
|
||||
badge_height - 1., font.em_size * 0.75);
|
||||
set_source(cr, theme_.background);
|
||||
cairo_fill_preserve(cr);
|
||||
set_source(cr, theme_.foreground);
|
||||
|
||||
Reference in New Issue
Block a user