Skip to content

Commit

Permalink
More work on box char porting
Browse files Browse the repository at this point in the history
  • Loading branch information
kovidgoyal committed Dec 20, 2024
1 parent 1741b02 commit e844826
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 30 deletions.
163 changes: 134 additions & 29 deletions kitty/decorations.c
Original file line number Diff line number Diff line change
Expand Up @@ -443,10 +443,16 @@ triangle(Canvas *self, bool left, bool inverted) {
}

typedef enum Corner {
TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT
TOP_LEFT = LEFT_EDGE | TOP_EDGE, TOP_RIGHT = TOP_EDGE | RIGHT_EDGE,
BOTTOM_LEFT = BOTTOM_EDGE | LEFT_EDGE, BOTTOM_RIGHT = BOTTOM_EDGE | RIGHT_EDGE,
} Corner;

typedef struct Point { int x, y; } Point;
typedef union Point {
struct {
int32_t x: 32, y: 32;
};
int64_t val;
} Point;

static void
thick_line(Canvas *self, uint thickness_in_pixels, Point p1, Point p2) {
Expand All @@ -462,6 +468,44 @@ thick_line(Canvas *self, uint thickness_in_pixels, Point p1, Point p2) {
}
}

static void
frame(Canvas *self, uint level, Edge edges) {
uint h = thickness(self, level, true), v = thickness(self, level, false);
#define line(x1, x2, y1, y2) { \
for (uint y=y1; y < min(y2, self->height); y++) memset(self->mask + y * self->width + x1, 255, minus(min(x2, self->width), x1)); }
#define hline(y1, y2) line(0, self->width, y1, y2)
#define vline(x1, x2) line(x1, x2, 0, self->height)
if (edges & TOP_EDGE) hline(0, h + 1);
if (edges & BOTTOM_EDGE) hline(self->height - h - 1, self->height);
if (edges & LEFT_EDGE) vline(0, v + 1);
if (edges & RIGHT_EDGE) vline(self->width - v - 1, self->width);
#undef hline
#undef vline
#undef line
}

typedef enum Segment { LEFT, MIDDLE, RIGHT } Segment;

static void
progress_bar(Canvas *self, Segment which, bool filled) {
const Edge edges = TOP_EDGE | BOTTOM_EDGE;
switch(which) {
case LEFT: frame(self, 1, LEFT_EDGE | edges); break;
case MIDDLE: frame(self, 1, edges); break;
case RIGHT: frame(self, 1, RIGHT_EDGE | edges); break;
}
if (!filled) return;
uint h = thickness(self, 1, true), v = thickness(self, 1, false);
static const uint gap_factor = 3;
uint y1 = gap_factor * h, y2 = minus(self->height, gap_factor*h), x1 = 0, x2 = 0;
switch(which) {
case LEFT: x1 = gap_factor * v; x2 = self->width; break;
case MIDDLE: x2 = self->width; break;
case RIGHT: x2 = minus(self->width, gap_factor * v); break;
}
for (uint y = y1; y < y2; y++) memset(self->mask + y * self->width + x1, 255, minus(min(x2, self->width), x1));
}

static void
half_cross_line(Canvas *self, uint level, Corner corner) {
uint my = minus(self->height, 1) / 2; Point p1 = {0}, p2 = {0};
Expand All @@ -474,6 +518,14 @@ half_cross_line(Canvas *self, uint level, Corner corner) {
thick_line(self, thickness(self, level, true), p1, p2);
}

static void
cross_line(Canvas *self, uint level, bool left) {
uint w = minus(self->width, 1), h = minus(self->height, 1);
Point p1 = {0}, p2 = {0};
if (left) p2 = (Point){.x=w, .y=h}; else { p1.x = w; p2.y = h; }
thick_line(self, thickness(self, level, true), p1, p2);
}

typedef struct CubicBezier {
Point start, c1, c2, end;
} CubicBezier;
Expand All @@ -493,7 +545,7 @@ bezier_y(CubicBezier cb, double t) { bezier_eq(y); }
static int
find_bezier_for_D(int width, int height) {
int cx = width - 1, last_cx = cx;
CubicBezier cb = {.end={0, height - 1}, .c2={0, height - 1}};
CubicBezier cb = {.end={.x=0, .y=height - 1}, .c2={.x=0, .y=height - 1}};
while (true) {
cb.c1.x = cx; cb.c2.x = cx;
if (bezier_x(cb, 0.5) > width - 1) return last_cx;
Expand Down Expand Up @@ -537,54 +589,83 @@ get_bezier_limits(Canvas *self, CubicBezier cb) {
}
}

#define mirror_horizontally(expr) { \
RAII_ALLOC(uint8_t, mbuf, calloc(self->width, self->height)); \
if (!mbuf) fatal("Out of memory"); \
uint8_t *buf = self->mask; \
self->mask = mbuf; \
expr; \
self->mask = buf; \
for (uint y = 0; y < self->height; y++) { \
uint offset = y * self->width; \
for (uint src_x = 0; src_x < self->width; src_x++) { \
uint dest_x = self->width - 1 - src_x; \
buf[offset + dest_x] = mbuf[offset + src_x]; \
} \
} \
}

static void
filled_D(Canvas *self, bool left) {
int c1x = find_bezier_for_D(self->width, self->height);
CubicBezier cb = {.end={0, self->height-1}, .c1 = {c1x, 0}, .c2 = {c1x, self->height - 1}};
CubicBezier cb = {.end={.y=self->height-1}, .c1 = {.x=c1x}, .c2 = {.x=c1x, .y=self->height - 1}};
get_bezier_limits(self, cb);
if (left) fill_region(self, false);
else {
RAII_ALLOC(uint8_t, mbuf, calloc(self->width, self->height));
if (!mbuf) fatal("Out of memory");
uint8_t *buf = self->mask;
self->mask = mbuf;
fill_region(self, false);
self->mask = buf;
for (uint y = 0; y < self->height; y++) {
uint offset = y * self->width;
for (uint src_x = 0; src_x < self->width; src_x++) {
uint dest_x = self->width - 1 - src_x;
buf[offset + dest_x] = mbuf[offset + src_x];
}
}

}
else mirror_horizontally(fill_region(self, false));
}

#define NAME position_set
#define KEY_TY int64_t
#define KEY_TY Point
#define HASH_FN hash_point
#define CMPR_FN cmpr_point
static uint64_t hash_point(Point p);
static bool cmpr_point(Point, Point);
#include "kitty-verstable.h"
static uint64_t hash_point(Point p) { return vt_hash_integer(p.val); }
static bool cmpr_point(Point a, Point b) { return a.val == b.val; }

#define draw_parametrized_curve(self, level, xfunc, yfunc) { \
div_t d = div(thickness(self, level, true)); \
div_t d = div(thickness(self, level, true), 2u); \
int delta = d.quot, extra = d.rem; \
uint num_samples = self->height * 8; \
position_set seen; vt_init(&seen); \
for (uint i = 0; i < num_samples; i++) { \
double t = i / (double)num_samples; \
int32_t x_p = xfunc, y_p = yfunc; \
int64_t key = (x_p << 32) | y_p; \
position_set_itr q = vt_get(&seen, key); \
Point p = {.x=(int32_t)xfunc, .y=(int32_t)yfunc}; \
position_set_itr q = vt_get(&seen, p); \
if (!vt_is_end(q)) continue; \
if (vt_is_end(vt_insert(&seen, key))) fatal("Out of memory"); \
for (int y = MAX(0, y_p - delta); y < MIN(y_p + delta + extra, (int)self->height); y++) { \
uint offset = y * self->width, start = MAX(0, x_p - delta); \
memset(self->mask + offset + start, 255, minus((uint)MIN(x_p + delta + extra, self->width), start)); \
if (vt_is_end(vt_insert(&seen, p))) fatal("Out of memory"); \
for (int y = MAX(0, p.y - delta); y < MIN(p.y + delta + extra, (int)self->height); y++) { \
uint offset = y * self->width, start = MAX(0, p.x - delta); \
memset(self->mask + offset + start, 255, minus((uint)MIN(p.x + delta + extra, (int)self->width), start)); \
} \
} \
vt_cleanup(&seen); \
}

static void
rounded_separator(Canvas *self, uint level, bool left) {
uint gap = thickness(self, level, true);
int c1x = find_bezier_for_D(minus(self->width, gap), self->height);
CubicBezier cb = {.end={.y=self->height - 1}, .c1={.x=c1x}, .c2={.x=c1x, .y=self->height - 1}};
if (left) { draw_parametrized_curve(self, level, bezier_x(cb, t), bezier_y(cb, t)); }
else { mirror_horizontally(draw_parametrized_curve(self, level, bezier_x(cb, t), bezier_y(cb, t))); }
}

static void
corner_triangle(Canvas *self, const Corner corner) {
StraightLine diag;
const uint w = minus(self->width, 1), h = minus(self->height, 1);
bool top = corner == TOP_RIGHT || corner == TOP_LEFT;
if (corner == TOP_RIGHT || corner == BOTTOM_LEFT) diag = line_from_points(0, 0, w, h);
else diag = line_from_points(w, 0, 0, h);
for (uint x = 0; x < self->width; x++) {
if (top) append_limit(self, line_y(diag, x), 0);
else append_limit(self, h, line_y(diag, x));
}
fill_region(self, false);
}

void
render_box_char(char_type ch, uint8_t *buf, unsigned width, unsigned height, double dpi_x, double dpi_y) {
Canvas canvas = {.mask=buf, .width = width, .height = height, .dpi={.x=dpi_x, .y=dpi_y}, .supersample_factor=1u}, ss = canvas;
Expand Down Expand Up @@ -644,6 +725,30 @@ render_box_char(char_type ch, uint8_t *buf, unsigned width, unsigned height, dou
S(L'◗', filled_D, true);
S(L'', filled_D, false);
S(L'◖', filled_D, false);
S(L'', rounded_separator, 1, true);
S(L'', rounded_separator, 1, false);

S(L'', cross_line, 1, true);
S(L'', cross_line, 1, true);
S(L'', cross_line, 1, false);
S(L'', cross_line, 1, false);

S(L'', corner_triangle, BOTTOM_LEFT);
S(L'◣', corner_triangle, BOTTOM_LEFT);
S(L'', corner_triangle, BOTTOM_RIGHT);
S(L'◢', corner_triangle, BOTTOM_RIGHT);
S(L'', corner_triangle, TOP_LEFT);
S(L'◤', corner_triangle, TOP_LEFT);
S(L'', corner_triangle, TOP_RIGHT);
S(L'◥', corner_triangle, TOP_RIGHT);

C(L'', progress_bar, LEFT, false);
C(L'', progress_bar, MIDDLE, false);
C(L'', progress_bar, RIGHT, false);
C(L'', progress_bar, LEFT, true);
C(L'', progress_bar, MIDDLE, true);
C(L'', progress_bar, RIGHT, true);

}
#undef CC
#undef SS
Expand Down
2 changes: 1 addition & 1 deletion kitty/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#define debug_input(...) if (OPT(debug_keyboard)) { timed_debug_print(__VA_ARGS__); }
#define debug_fonts(...) if (global_state.debug_font_fallback) { timed_debug_print(__VA_ARGS__); }

typedef enum { LEFT_EDGE, TOP_EDGE, RIGHT_EDGE, BOTTOM_EDGE } Edge;
typedef enum { LEFT_EDGE = 1, TOP_EDGE = 2, RIGHT_EDGE = 4, BOTTOM_EDGE = 8 } Edge;
typedef enum { REPEAT_MIRROR, REPEAT_CLAMP, REPEAT_DEFAULT } RepeatStrategy;
typedef enum { WINDOW_NORMAL, WINDOW_FULLSCREEN, WINDOW_MAXIMIZED, WINDOW_MINIMIZED } WindowState;

Expand Down

0 comments on commit e844826

Please sign in to comment.