/*
 * decorations.c
 * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>
 *
 * Distributed under terms of the GPL3 license.
 */

#include "decorations.h"
#include "state.h"

typedef uint32_t uint;

static uint max(uint a, uint b) { return a > b ? a : b; }
static uint min(uint a, uint b) { return a < b ? a : b; }

// Decorations {{{
#define STRAIGHT_UNDERLINE_LOOP \
    unsigned half = fcm.underline_thickness / 2; \
    DecorationGeometry ans = {.top = half > fcm.underline_position ? 0 : fcm.underline_position - half}; \
    for (unsigned y = ans.top; fcm.underline_thickness > 0 && y < fcm.cell_height; fcm.underline_thickness--, y++, ans.height++)

DecorationGeometry
add_straight_underline(uint8_t *buf, FontCellMetrics fcm) {
    STRAIGHT_UNDERLINE_LOOP {
        memset(buf + fcm.cell_width * y, 0xff, fcm.cell_width * sizeof(buf[0]));
    }
    return ans;
}

DecorationGeometry
add_strikethrough(uint8_t *buf, FontCellMetrics fcm) {
    unsigned half = fcm.strikethrough_thickness / 2;
    DecorationGeometry ans = {.top = half > fcm.strikethrough_position ? 0 : fcm.strikethrough_position - half};
    for (unsigned y = ans.top; fcm.strikethrough_thickness > 0 && y < fcm.cell_height; fcm.strikethrough_thickness--, y++, ans.height++) {
        memset(buf + fcm.cell_width * y, 0xff, fcm.cell_width * sizeof(buf[0]));
    }
    return ans;
}


DecorationGeometry
add_missing_glyph(uint8_t *buf, FontCellMetrics fcm) {
    DecorationGeometry ans = {.height=fcm.cell_height};
    unsigned thickness = min(fcm.underline_thickness, fcm.strikethrough_thickness);
    thickness = min(thickness, fcm.cell_width);
    for (unsigned y = 0; y < ans.height; y++) {
        uint8_t *line = buf + fcm.cell_width * y;
        if (y < thickness || y >= ans.height - thickness) memset(line, 0xff, fcm.cell_width);
        else {
            memset(line, 0xff, thickness);
            memset(line + fcm.cell_width - thickness, 0xff, thickness);
        }
    }
    return ans;
}

DecorationGeometry
add_double_underline(uint8_t *buf, FontCellMetrics fcm) {
    unsigned a = fcm.underline_position > fcm.underline_thickness ? fcm.underline_position - fcm.underline_thickness : 0;
    a = min(a, fcm.cell_height - 1);
    unsigned b = min(fcm.underline_position, fcm.cell_height - 1);
    unsigned top = min(a, b), bottom = max(a, b);
    int deficit = 2 - (bottom - top);
    if (deficit > 0) {
        if (bottom + deficit < fcm.cell_height) bottom += deficit;
        else if (bottom < fcm.cell_height - 1) {
            bottom += 1;
            if (deficit > 1) top -= deficit - 1;
        } else top -= deficit;
    }
    top = max(0u, min(top, fcm.cell_height - 1u));
    bottom = max(0u, min(bottom, fcm.cell_height - 1u));
    memset(buf + fcm.cell_width * top, 0xff, fcm.cell_width);
    memset(buf + fcm.cell_width * bottom, 0xff, fcm.cell_width);
    DecorationGeometry ans = {.top=top, .height = bottom + 1 - top};
    return ans;
}

static unsigned
distribute_dots(unsigned available_space, unsigned num_of_dots, unsigned *summed_gaps, unsigned *gaps) {
    unsigned dot_size = max(1u, available_space / (2u * num_of_dots));
    unsigned extra = 2 * num_of_dots * dot_size;
    extra = available_space > extra ? available_space - extra : 0;
    for (unsigned i = 0; i < num_of_dots; i++) gaps[i] = dot_size;
    if (extra > 0) {
        unsigned idx = 0;
        while (extra > 0) {
            gaps[idx] += 1;
            idx = (idx + 1) % num_of_dots;
            extra--;
        }
    }
    gaps[0] /= 2;
    for (unsigned i = 0; i < num_of_dots; i++) {
        summed_gaps[i] = 0;
        for (unsigned g = 0; g <= i; g++) summed_gaps[i] += gaps[g];
    }
    return dot_size;
}

DecorationGeometry
add_dotted_underline(uint8_t *buf, FontCellMetrics fcm) {
    unsigned num_of_dots = MAX(1u, fcm.cell_width / (2 * MAX(1u, fcm.underline_thickness)));
    RAII_ALLOC(unsigned, spacing, malloc(num_of_dots * 2 * sizeof(unsigned)));
    if (!spacing) fatal("Out of memory");
    unsigned size = distribute_dots(fcm.cell_width, num_of_dots, spacing, spacing + num_of_dots);
    STRAIGHT_UNDERLINE_LOOP {
        uint8_t *offset = buf + fcm.cell_width * y;
        for (unsigned j = 0; j < num_of_dots; j++) {
            unsigned s = spacing[j];
            memset(offset + j * size + s, 0xff, size);
        }
    }
    return ans;
}

DecorationGeometry
add_dashed_underline(uint8_t *buf, FontCellMetrics fcm) {
    unsigned quarter_width = fcm.cell_width / 4;
    unsigned dash_width = fcm.cell_width - 3 * quarter_width;
    unsigned second_dash_start = 3 * quarter_width;
    STRAIGHT_UNDERLINE_LOOP {
        uint8_t *offset = buf + fcm.cell_width * y;
        memset(offset, 0xff, dash_width);
        memset(offset + second_dash_start, 0xff, dash_width);
    }
    return ans;
}

static unsigned
add_intensity(uint8_t *buf, unsigned x, int y, uint8_t val, unsigned max_y, unsigned position, unsigned cell_width) {
    y += position;
    y = min(MAX(0, y), max_y);
    unsigned idx = cell_width * y + x;
    buf[idx] = min(255, buf[idx] + val);
    return y;
}

static uint
minus(uint a, uint b) {  // saturating subtraction (a > b ? a - b : 0)
    uint res = a - b;
    res &= -(res <= a);
    return res;
}

DecorationGeometry
add_curl_underline(uint8_t *buf, FontCellMetrics fcm) {
    unsigned max_x = fcm.cell_width - 1, max_y = fcm.cell_height - 1;
    double xfactor = ((OPT(undercurl_style) & 1) ? 4.0 : 2.0) * M_PI / max_x;
    div_t d = div(fcm.underline_thickness, 2);
    /*printf("cell_width: %u cell_height: %u underline_position: %u underline_thickness: %u\n",*/
    /*        fcm.cell_width, fcm.cell_height, fcm.underline_position, fcm.underline_thickness);*/
    unsigned position = min(fcm.underline_position, minus(fcm.cell_height, d.quot + d.rem));
    unsigned thickness = max(1u, min(fcm.underline_thickness, minus(fcm.cell_height, position + 1)));
    unsigned max_height = fcm.cell_height - minus(position, thickness / 2);  // descender from the font
    unsigned half_height = max(1u, max_height / 4u);  // 4 so as to be not too large
    if (OPT(undercurl_style) & 2) thickness = max(half_height, thickness);
    else thickness = max(1u, thickness) - (thickness < 3u ? 1u : 2u);

    position += half_height * 2;
    if (position + half_height > max_y) position = max_y - half_height;
    /*printf("position: %u half_height: %u thickness: %u\n", position, half_height, thickness);*/

    unsigned miny = fcm.cell_height, maxy = 0;
    // Use the Wu antialias algorithm to draw the curve
    // cosine waves always have slope <= 1 so are never steep
    for (unsigned x = 0; x < fcm.cell_width; x++) {
        double y = half_height * cos(x * xfactor);
        int y1 = (int)(floor(y - thickness)), y2 = (int)(ceil(y));
        unsigned intensity = (unsigned)((255. * fabs(y - floor(y))));
        unsigned i1 = 255 - intensity, i2 = intensity;
        unsigned yc = add_intensity(buf, x, y1, i1, max_y, position, fcm.cell_width);  // upper bound
        if (i1) { if (yc < miny) miny = yc; if (yc > maxy) maxy = yc; }
        yc = add_intensity(buf, x, y2, i2, max_y, position, fcm.cell_width);  // lower bound
        if (i2) { if (yc < miny) miny = yc; if (yc > maxy) maxy = yc; }
        // fill between upper and lower bound
        for (unsigned t = 1; t <= thickness; t++) add_intensity(buf, x, y1 + t, 255, max_y, position, fcm.cell_width);
    }
    DecorationGeometry ans = {.top=miny, .height=maxy-miny + 1};
    return ans;
}

static void
vert(uint8_t *ans, bool is_left_edge, double width_pt, double dpi_x, FontCellMetrics fcm) {
    unsigned width = max(1u, min((unsigned)(round(width_pt * dpi_x / 72.0)), fcm.cell_width));
    const unsigned left = is_left_edge ? 0 : (fcm.cell_width > width ? fcm.cell_width - width : 0);
    for (unsigned y = 0; y < fcm.cell_height; y++) {
        const unsigned offset = y * fcm.cell_width + left;
        memset(ans + offset, 0xff, width);
    }
}

static unsigned
horz(uint8_t *ans, bool is_top_edge, double height_pt, double dpi_y, FontCellMetrics fcm) {
    unsigned height = max(1u, min((unsigned)(round(height_pt * dpi_y / 72.0)), fcm.cell_height));
    const unsigned top = is_top_edge ? 0 : (fcm.cell_height > height ? fcm.cell_height - height : 0);
    for (unsigned y = top; y < top + height; y++) {
        const unsigned offset = y * fcm.cell_width;
        memset(ans + offset, 0xff, fcm.cell_width);
    }
    return top;
}


DecorationGeometry
add_beam_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_x) {
    vert(buf, true, OPT(cursor_beam_thickness), dpi_x, fcm);
    DecorationGeometry ans = {.height=fcm.cell_height};
    return ans;
}

DecorationGeometry
add_underline_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_y) {
    DecorationGeometry ans = {0};
    ans.top = horz(buf, false, OPT(cursor_underline_thickness), dpi_y, fcm);
    ans.height = fcm.cell_height - ans.top;
    return ans;
}

DecorationGeometry
add_hollow_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_x, double dpi_y) {
    vert(buf, true, 1.0, dpi_x, fcm); vert(buf, false, 1.0, dpi_x, fcm);
    horz(buf, true, 1.0, dpi_y, fcm); horz(buf, false, 1.0, dpi_y, fcm);
    DecorationGeometry ans = {.height=fcm.cell_height};
    return ans;
}

// }}}

typedef struct Range {
    uint start, end;
} Range;

typedef struct Limit { double upper, lower; } Limit;
typedef struct FloatPoint { double x, y; } FloatPoint;

typedef struct Canvas {
    uint8_t *mask;
    uint width, height, supersample_factor;
    struct { double x, y; } dpi;
    double scale;  // used to scale line thickness with font size for multicell rendering
    Range *holes; uint holes_count, holes_capacity;
    Limit *y_limits; uint y_limits_count, y_limits_capacity;
} Canvas;

static void
fill_canvas(Canvas *self, int byte) { memset(self->mask, byte, sizeof(self->mask[0]) * self->width * self->height); }

static void
append_hole(Canvas *self, Range hole) {
    ensure_space_for(self, holes, self->holes[0], self->holes_count + 1, holes_capacity, self->width, false);
    self->holes[self->holes_count++] = hole;
}

static void
append_limit(Canvas *self, double upper, double lower) {
    ensure_space_for(self, y_limits, self->y_limits[0], self->y_limits_count + 1, y_limits_capacity, self->width, false);
    self->y_limits[self->y_limits_count].upper = upper;
    self->y_limits[self->y_limits_count++].lower = lower;
}

static double
thickness_as_float(Canvas *self, uint level, bool horizontal) {
    level = min(level, arraysz(OPT(box_drawing_scale)));
    double pts = OPT(box_drawing_scale)[level];
    double dpi = horizontal ? self->dpi.x : self->dpi.y;
    return self->supersample_factor * self->scale * pts * dpi / 72.0;
}

static uint
thickness(Canvas *self, uint level, bool horizontal) {
    return (uint)ceil(thickness_as_float(self, level, horizontal));
}

static const uint hole_factor = 8;

static void
get_holes(Canvas *self, uint sz, uint hole_sz, uint num) {
    uint all_holes_use = (num + 1) * hole_sz;
    uint individual_block_size = max(1u, minus(sz, all_holes_use) / (num + 1));
    uint half_hole_sz = hole_sz / 2;
    int pos = - half_hole_sz;
    while (pos < (int)sz) {
        uint left = pos > 0 ? pos : 0;
        uint right = min(sz, pos + hole_sz);
        if (right > left) append_hole(self, (Range){left, right});
        pos = right + individual_block_size;
    }
}

static void
add_hholes(Canvas *self, uint level, uint num) {
    uint line_sz = thickness(self, level, true);
    uint hole_sz = self->width / hole_factor;
    uint start = minus(self->height / 2, line_sz / 2);
    get_holes(self, self->width, hole_sz, num);
    for (uint y = 0; y < start + line_sz; y++) {
        uint offset = y * self->width;
        for (uint i = 0; i < self->holes_count; i++) memset(self->mask + offset + self->holes[i].start, 0, self->holes[i].end - self->holes[i].start);
    }
}

static void
add_vholes(Canvas *self, uint level, uint num) {
    uint line_sz = thickness(self, level, false);
    uint hole_sz = self->height / hole_factor;
    uint start = minus(self->width / 2, line_sz / 2);
    get_holes(self, self->height, hole_sz, num);
    for (uint i = 0; i < self->holes_count; i++) {
        for (uint y = self->holes[i].start; y < self->holes[i].end; y++) {
            uint offset = y * self->width;
            memset(self->mask + offset + start, 0, line_sz);
        }
    }
}

static Range
hline_limits(Canvas *self, uint y, uint level) {
    uint sz = thickness(self, level, false);
    Range r = {.start=minus(y, sz / 2)};
    r.end = min(r.start + sz, self->height);
    return r;
}

static void
draw_hline(Canvas *self, uint x1, uint x2, uint y, uint level) {
    // Draw a horizontal line between [x1, x2) centered at y with the thickness given by level and self->supersample_factor
    Range r = hline_limits(self, y, level);
    for (uint y = r.start; y < r.end; y++) {
        uint8_t *py = self->mask + y * self->width;
        memset(py + x1, 255, minus(min(x2, self->width), x1));
    }
}

static Range
vline_limits(Canvas *self, uint x, uint level) {
    uint sz = thickness(self, level, true);
    Range r = {.start = minus(x, sz / 2)};
    r.end = min(r.start + sz, self->width);
    return r;
}

static void
draw_vline(Canvas *self, uint y1, uint y2, uint x, uint level) {
    // Draw a vertical line between [y1, y2) centered at x with the thickness given by level and self->supersample_factor
    Range r = vline_limits(self, x, level);
    uint xsz = minus(r.end, r.start);
    for (uint y = y1; y < min(y2, self->height); y++) {
        uint8_t *py = self->mask + y * self->width;
        memset(py + r.start, 255, xsz);
    }
}

static uint
half_width(Canvas *self) {  // align with non-supersampled co-ords
    return self->supersample_factor * (self->width / 2 / self->supersample_factor);
}

static uint
half_height(Canvas *self) { // align with non-supersampled co-ords
    return self->supersample_factor * (self->height / 2 / self->supersample_factor);
}

static double
unit_double(double x) {
    return x < 0.0 ? 0.0 : (x > 1.0 ? 1.0 : x);
}

static double
smoothstep(double edge0, double edge1, double x) {
    if (edge0 == edge1) return x < edge0 ? 0.0 : 1.0;
    double t = unit_double((x - edge0) / (edge1 - edge0));
    return t * t * (3.0 - 2.0 * t);
}

static void
half_hline(Canvas *self, uint level, bool right_half, uint extend_by) {
    uint x1, x2;
    if (right_half) {
        x1 = minus(half_width(self), extend_by); x2 = self->width;
    } else {
        x1 = 0; x2 = half_width(self) + extend_by;
    }
    draw_hline(self, x1, x2, half_height(self), level);
}

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


static Point
half_dhline(Canvas *self, uint level, bool right_half, Edge which) {
    uint x1 = 0, x2 = 0;
    if (right_half) { x1 = self->width / 2; x2 = self->width; } else x2 = self->width / 2;
    uint gap = thickness(self, level + 1, false);
    Point ans = {.x=self->height / 2 - gap, .y=self->height / 2 + gap};
    if (which & TOP_EDGE) draw_hline(self, x1, x2, ans.x, level);
    if (which & BOTTOM_EDGE) draw_hline(self, x1, x2, ans.y, level);
    return ans;
}

static Point
half_dvline(Canvas *self, uint level, bool bottom_half, Edge which) {
    uint y1 = 0, y2 = 0;
    if (bottom_half) { y1 = self->height / 2; y2 = self->height; } else y2 = self->height / 2;
    uint gap = thickness(self, level + 1, true);
    Point ans = {.x=self->width / 2 - gap, .y=self->width / 2 + gap};
    if (which & LEFT_EDGE) draw_vline(self, y1, y2, ans.x, level);
    if (which & RIGHT_EDGE) draw_vline(self, y1, y2, ans.y, level);
    return ans;
}

static Point
dhline(Canvas *self, uint level, Edge which) {
    half_dhline(self, level, false, which);
    return half_dhline(self, level, true, which);
}

static Point
dvline(Canvas *self, uint level, Edge which) {
    half_dvline(self, level, false, which);
    return half_dvline(self, level, true, which);
}


static void
half_vline(Canvas *self, uint level, bool bottom_half, uint extend_by) {
    uint y1, y2;
    if (bottom_half) {
        y1 = minus(half_height(self), extend_by); y2 = self->height;
    } else {
        y1 = 0; y2 = half_height(self) + extend_by;
    }
    draw_vline(self, y1, y2, half_width(self), level);
}

static void
hline(Canvas *self, uint level) {
    half_hline(self, level, false, 0);
    half_hline(self, level, true, 0);
}

static void
vline(Canvas *self, uint level) {
    half_vline(self, level, false, 0);
    half_vline(self, level, true, 0);
}

static void
hholes(Canvas *self, uint level, uint num) {
    hline(self, level);
    add_hholes(self, level, num);
}

static void
vholes(Canvas *self, uint level, uint num) {
    vline(self, level);
    add_vholes(self, level, num);
}

static uint8_t
plus(uint8_t a, uint8_t b) {
    uint8_t res = a + b;
    res |= -(res < a);
    return res;
}

static uint8_t
average_intensity(const Canvas *src, uint dest_x, uint dest_y) {
    uint src_x = dest_x * src->supersample_factor, src_y = dest_y * src->supersample_factor;
    uint total = 0;
    for (uint y = src_y; y < src_y + src->supersample_factor; y++) {
        uint offset = src->width * y;
        for (uint x = src_x; x < src_x + src->supersample_factor; x++) total += src->mask[offset + x];
    }
    return (total / (src->supersample_factor * src->supersample_factor)) & 0xff;
}

static void
downsample(const Canvas *src, Canvas *dest) {
    for (uint y = 0; y < dest->height; y++) {
        uint offset = dest->width * y;
        for (uint x = 0; x < dest->width; x++) {
            dest->mask[offset + x] = plus(dest->mask[offset + x], average_intensity(src, x, y));
        }
    }
}

typedef struct StraightLine {
    double m, c;
} StraightLine;


static StraightLine
line_from_points(double x1, double y1, double x2, double y2) {
    StraightLine ans = {.m = (y2 - y1) / (x2 - x1)};
    ans.c = y1 - ans.m * x1;
    return ans;
}

static double
line_y(StraightLine l, int x) {
    return l.m * x + l.c;
}

#define calc_limits(self, lower_y, upper_y) { \
    if (!self->y_limits) { \
        self->y_limits_count = self->width; self->y_limits = malloc(sizeof(self->y_limits[0]) * self->y_limits_count); \
        if (!self->y_limits) fatal("Out of memory"); \
    } \
    for (uint x = 0; x < self->width; x++) { self->y_limits[x].lower = lower_y; self->y_limits[x].upper = upper_y; } \
}

static void
fill_region(Canvas *self, bool inverted) {
    uint8_t full = 0, empty = 0; if (inverted) empty = 255; else full = 255;
    for (uint y = 0; y < self->height; y++) {
        uint offset = y * self->width;
        for (uint x = 0; x < self->width && x < self->y_limits_count; x++) {
            self->mask[offset + x] = self->y_limits[x].lower <= y && y <= self->y_limits[x].upper ? full : empty;
        }
    }
}

static void
triangle(Canvas *self, bool left, bool inverted) {
    int ay1 = 0, by1 = self->height - 1, y2 = self->height / 2, x1 = 0, x2 = 0;
    if (left) x2 = self->width - 1; else x1 = self->width - 1;
    StraightLine uppery = line_from_points(x1, ay1, x2, y2);
    StraightLine lowery = line_from_points(x1, by1, x2, y2);
    calc_limits(self, line_y(uppery, x), line_y(lowery, x));
    fill_region(self, inverted);
}

typedef enum Corner {
    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;

static void
thick_line(Canvas *self, uint thickness_in_pixels, Point p1, Point p2) {
    if (p1.x > p2.x) SWAP(p1, p2);
    StraightLine l = line_from_points(p1.x, p1.y, p2.x, p2.y);
    div_t d = div(thickness_in_pixels, 2);
    int delta = d.quot, extra = d.rem;
    for (int x = p1.x > 0 ? p1.x : 0; x < (int)self->width && x < p2.x + 1; x++) {
        int y_p = (int)line_y(l, x);
        for (int y = MAX(0, y_p - delta); y < MIN(y_p + delta + extra, (int)self->height); y++) {
            self->mask[x + y * self->width] = 255;
        }
    }
}

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};
    switch (corner) {
        case TOP_LEFT: p2.x = minus(self->width, 1); p2.y = my; break;
        case BOTTOM_LEFT: p1.x = minus(self->width, 1); p1.y = my; p2.y = self->height -1; break;
        case TOP_RIGHT: p1.x = minus(self->width, 1); p2.y = my; break;
        case BOTTOM_RIGHT: p2.x = minus(self->width, 1), p2.y = minus(self->height, 1); p1.y = my; break;
    }
    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;

#define bezier_eq(which) { \
    const CubicBezier *cb = v; \
    const double u = 1. - t; \
    const double u_3 = u * u * u; \
    const double t_3 = t * t * t; \
    return u_3 * cb->start.which + 3 * t * u * (u * cb->c1.which + t * cb->c2.which) + t_3 * cb->end.which; \
}

#define bezier_prime_eq(which) { \
    const CubicBezier *cb = v; \
    const double u = 1. - t; \
    const double u_2 = u * u; \
    const double t_2 = t * t; \
    return 3 * u_2 * (cb->c1.which - cb->start.which) + 6 * t * u * (cb->c2.which - cb->c1.which) + 3 * t_2 * (cb->end.which - cb->c2.which); \
}

static double bezier_x(const void *v, double t) { bezier_eq(x); }
static double bezier_y(const void *v, double t) { bezier_eq(y); }
static double bezier_prime_x(const void *v, double t) { bezier_prime_eq(x); }
static double bezier_prime_y(const void *v, double t) { bezier_prime_eq(y); }
#undef bezier_eq
#undef bezier_prime_eq

static int
find_bezier_for_D(int width, int height) {
    int cx = width - 1, last_cx = cx;
    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;
        last_cx = cx++;
    }
}

static double
find_t_for_x(const CubicBezier *cb, int x, double start_t) {
    if (fabs(bezier_x(cb, start_t) - x) < 0.1) return start_t;
    static const double t_limit = 0.5;
    double increment = t_limit - start_t;
    if (increment <= 0) return start_t;
    while (true) {
        double q = bezier_x(cb, start_t + increment);
        if (fabs(q - x) < 0.1) return start_t + increment;
        if (q > x) {
            increment /= 2.0;
            if (increment < 1e-6) {
                log_error("Failed to find cubic bezier t for x=%d\n", x);
                return start_t;
            }
        } else {
            start_t += increment;
            increment = t_limit - start_t;
            if (increment <= 0) return start_t;
        }
    }
}


static void
get_bezier_limits(Canvas *self, const CubicBezier *cb) {
    int start_x = (int)bezier_x(cb, 0), max_x = (int)bezier_x(cb, 0.5);
    double last_t = 0.;
    for (int x = start_x; x < max_x + 1; x++) {
        if (x > start_x) last_t = find_t_for_x(cb, x, last_t);
        double upper = bezier_y(cb, last_t), lower = bezier_y(cb, 1.0 - last_t);
        if (fabs(upper - lower) <= 2.0) break;  // avoid pip on end of D
        append_limit(self, lower, upper);
    }
}

#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={.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 mirror_horizontally(fill_region(self, false));
}

typedef double(*curve_func)(const void *, double t);

#define NAME position_set
#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; }

typedef struct ClipRect { uint left, top, x_end, y_end; } ClipRect;

static void
draw_parametrized_curve_with_derivative_and_antialiasing(
    Canvas *self, void *curve_data, double line_width, curve_func xfunc, curve_func yfunc,
    curve_func x_prime, curve_func y_prime, double x_offset, double y_offset, const ClipRect *clip_to
) {
    line_width = fmax(1., line_width);
    double half_thickness = line_width / 2.0;
    uint i=0, larger_dim = MAX(self->height, self->width);
    double t = 0;
    double step = 1.0 / larger_dim;
    uint cap = 2 * larger_dim;
    const double min_step = step / 1000., max_step = step;
    RAII_ALLOC(FloatPoint, samples, malloc(sizeof(FloatPoint) * cap));
    if (!samples) fatal("Out of memory");
    ClipRect cr = clip_to ? *clip_to : (ClipRect){.x_end=self->width, .y_end=self->height};
    while (true) {
        samples[i] = (FloatPoint){xfunc(curve_data, t) + x_offset, yfunc(curve_data, t) + y_offset};
        if (t >= 1.0) break;
        // Dynamically adjust step size based on curve's derivative
        double dx = x_prime(curve_data, t), dy = y_prime(curve_data, t);
        double d = sqrt(dx * dx + dy * dy);
        step = 1.0 / fmax(1e-6, d);
        step = fmax(min_step, fmin(step, max_step));
        t = fmin(t + step, 1.0);
        i++;
        if (i >= cap) {
            cap *= 2;
            samples = realloc(samples, sizeof(samples[0]) * cap);
            if (!samples) fatal("Out of memory");
        }
    }
    const uint num_samples = i;
    for (uint py = cr.top; py < cr.y_end; py++) {
        uint ypos = self->width * py;
        for (uint px = cr.left; px < cr.x_end; px++) {
            // Center of the current pixel
            double pixel_center_x = (double)px + 0.5;
            double pixel_center_y = (double)py + 0.5;

            double min_dist_sq = -1.0;

            // Find the closest point on the curve to the pixel center by sampling the curve.
            for (uint i = 0; i < num_samples; ++i) {
                double dx = samples[i].x - pixel_center_x;
                double dy = samples[i].y - pixel_center_y;
                double dist_sq = dx * dx + dy * dy;
                if (min_dist_sq < 0 || dist_sq < min_dist_sq) min_dist_sq = dist_sq;
            }

            double distance = sqrt(min_dist_sq);

            // Calculate alpha based on the distance from the curve.
            // This creates the anti-aliased edge. The distance from the center
            // of the pixel to the edge of the stroke is used.
            // We assume a pixel has a width of 1.0 for this calculation.
            double alpha_unclamped = half_thickness - distance + 0.5;

            uint offset = ypos + px;
            uint8_t old_alpha = self->mask[offset];
            double alpha = MAX(0.0, MIN(alpha_unclamped, 1.0));
            self->mask[offset] = (uint8_t)(alpha * 255 + (1 - alpha) * old_alpha);  // alpha blend
        }
    }
}

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), minus(self->height, gap));
    uint half_gap = gap / 2;
    CubicBezier cb = {.end={.y=minus(self->height,  1 + half_gap)}, .c1={.x=c1x},
        .c2={.x=c1x, .y=minus(self->height, 1 + half_gap)}};
    double line_width = thickness_as_float(self, level, true);
#define d draw_parametrized_curve_with_derivative_and_antialiasing(\
        self, &cb, line_width, bezier_x, bezier_y, bezier_prime_x, bezier_prime_y, 0, half_gap, NULL)
    if (left) { d; } else { mirror_horizontally(d); }
#undef d
}

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);
}

typedef struct Circle {
    double x, y, radius;
    double start, end, amt;
} Circle;

static Circle
circle(double x, double y, double radius, double start_at, double end_at) {
    double conv = M_PI / 180.;
    Circle ans = {.x=x, .y=y, .radius=radius, .start=start_at*conv, .end=end_at*conv};
    ans.amt = ans.end - ans.start;
    return ans;
}

static double circle_x(const void *v, double t) { const Circle *c=v; return c->x + c->radius * cos(c->start + c->amt * t); }
static double circle_y(const void *v, double t) { const Circle *c=v; return c->y + c->radius * sin(c->start + c->amt * t); }
static double circle_prime_x(const void *v, double t) { const Circle *c=v; return -c->radius * sin(c->start + c->amt * t); }
static double circle_prime_y(const void *v, double t) { const Circle *c=v; return c->radius * cos(c->start + c->amt * t); }

static void
spinner(Canvas *self, uint level, double start_degrees, double end_degrees) {
    double x = self->width / 2.0, y = self->height / 2.0;
    double line_width = thickness_as_float(self, level, true);
    double half_real_line_width = fmax(0.5, line_width / 2.0);
    double radius = fmax(0, fmin(x, y) - half_real_line_width);
    Circle c = circle(x, y, radius, start_degrees, end_degrees);
    uint leftover = minus(self->height, 2*(uint)(ceil(radius) + half_real_line_width) + 1) / 2;
    ClipRect cr = {.top=leftover, .y_end=self->height - leftover, .x_end=self->width};
    draw_parametrized_curve_with_derivative_and_antialiasing(
        self, &c, line_width, circle_x, circle_y, circle_prime_x, circle_prime_y, 0, 0, &cr);
}

static void
fill_circle_of_radius(Canvas *self, double origin_x, double origin_y, double radius, uint8_t alpha) {
    const double limit = radius * radius;
    for (uint y = 0; y < self->height; y++) {
        for (uint x = 0; x < self->width; x++) {
            double xw = (double)x - origin_x, yh = (double)y - origin_y;
            if (xw * xw + yh * yh <= limit) self->mask[y * self->width + x] = alpha;
        }
    }
}

static void
fill_circle(Canvas *self, double scale, double gap, bool invert) {
    const uint w = self->width / 2, h = self->height / 2;
    const double radius = (int)(scale * min(w, h) - gap / 2);
    const uint8_t fill = invert ? 0 : 255;
    fill_circle_of_radius(self, w, h, radius, fill);
}

static void
draw_fish_eye(Canvas *self, uint level UNUSED) {
    double x = self->width / 2., y = self->height / 2.;
    double radius = fmin(x, y);
    uint leftover = minus(self->height, 2*(uint)ceil(radius) + 1) / 2;
    double central_radius = (2./3.) * radius;
    fill_circle_of_radius(self, x, y, central_radius, 255);
    double line_width = fmax(1. * self->supersample_factor, (radius - central_radius) / 2.5);
    radius = fmax(0, fmin(x, y) - line_width / 2.);
    Circle c = circle(x, y, radius, 0, 360);
    ClipRect cr = {.top=leftover, .y_end=self->height - leftover, .x_end=self->width};
    draw_parametrized_curve_with_derivative_and_antialiasing(
            self, &c, line_width, circle_x, circle_y, circle_prime_x, circle_prime_y, 0, 0, &cr);
}

static void
inner_corner(Canvas *self, uint level, Corner corner) {
    uint hgap = thickness(self, level + 1, true), vgap = thickness(self, level + 1, false);
    uint vthick = thickness(self, level, true) / 2;
    uint x1 = 0, x2 = self->width, y1 = 0, y2 = self->height; int xd = 1, yd = 1;
    if (corner & LEFT_EDGE) {
        xd = -1;
        Range vlinelimit = vline_limits(self, self->width / 2 + (xd * hgap), level);
        x2 = vlinelimit.end;
    } else x1 = minus(self->width / 2 + hgap, vthick);
    if (corner & TOP_EDGE) {
        y2 = minus(self->height / 2, vgap); yd = -1;
    } else y1 = self->height / 2 + vgap;
    draw_hline(self, x1, x2, self->height / 2 + (yd * vgap), level);
    draw_vline(self, y1, y2, self->width / 2 + (xd * hgap), level);
}

static Range
fourth_range(uint size, uint which) {
    uint thickness = max(1, size / 4);
    uint block = thickness * 4;
    if (block == size) return (Range){.start=thickness * which, .end=thickness * (which + 1)};
    if (block > size) {
        uint start = min(which * thickness, minus(size, thickness));
        return (Range){.start=start, .end=start + thickness};
    }
    uint extra = minus(size, block);
    uint thicknesses[4] = {thickness, thickness, thickness, thickness};
    uint pos = 0;
    if (extra) {
#define d(i) thicknesses[i]++; if (!--extra) goto done;
        // ensures the thickness of first and last are least likely to be changed
        d(1); d(2); d(3); d(0);
#undef d
    }
done:
    for (uint i = 0; i < which; i++) pos += thicknesses[i];
    return (Range){.start=pos, .end=pos + thicknesses[which]};
}


static Range
eight_range(uint size, uint which) {
    uint thickness = max(1, size / 8);
    uint block = thickness * 8;
    if (block == size) return (Range){.start=thickness * which, .end=thickness * (which + 1)};
    if (block > size) {
        uint start = min(which * thickness, minus(size, thickness));
        return (Range){.start=start, .end=start + thickness};
    }
    uint extra = minus(size, block);
    uint thicknesses[8] = {thickness, thickness, thickness, thickness, thickness, thickness, thickness, thickness};
    uint pos = 0;
    if (extra) {
#define d(i) thicknesses[i]++; if (!--extra) goto done;
        // ensures the thickness of first and last are least likely to be changed
        d(3); d(4); d(2); d(5); d(6); d(1); d(7); d(0);
#undef d
    }
done:
    for (uint i = 0; i < which; i++) pos += thicknesses[i];
    return (Range){.start=pos, .end=pos + thicknesses[which]};
}

static void
eight_bar(Canvas *self, uint which, bool horizontal) {
    Range x_range, y_range;
    if (horizontal) {
        x_range = (Range){0, self->width};
        y_range = eight_range(self->height, which);
    } else {
        y_range = (Range){0, self->height};
        x_range = eight_range(self->width, which);
    }
    for (uint y = y_range.start; y < y_range.end; y++) {
        uint offset = y * self->width;
        memset(self->mask + offset + x_range.start, 255, minus(x_range.end, x_range.start));
    }
}


static void
octant_segment(Canvas *self, uint8_t which, bool left) {
    Range x_range = left ? (Range){0, self->width / 2} : (Range){self->width/2, self->width};
    Range y_range = fourth_range(self->height, which);
    for (uint y = y_range.start; y < y_range.end; y++) {
        uint offset = y * self->width;
        memset(self->mask + offset + x_range.start, 255, minus(x_range.end, x_range.start));
    }
}

static void
octant(Canvas *self, uint8_t which) {
    enum flags { a = 1, b = 2, c = 4, d = 8, m = 16, n = 32, o = 64, p = 128 };
    static const enum flags mapping[232] = {
        // 00 - 0f
        b,     b|m,   a|b|m, n,       a|n,   a|m|n,   b|n,     a|b|n,     b|m|n, c,   a|c, c|m,   a|c|m, a|b|c, b|c|m, a|b|c|m,
        // 10 - 1f
        c|n,   a|c|n, c|m|n, a|c|m|n, b|c|n, a|b|c|n, b|c|m|n, a|b|c|m|n, o,     a|o, m|o, a|m|o, b|o,   a|b|o, b|m|o, a|b|m|o,
        // 20 - 2f
        a|n|o, m|n|o, a|m|n|o, b|n|o, a|b|n|o, b|m|n|o, a|b|m|n|o, c|o, a|c|o, c|m|o, a|c|m|o, b|c|o, a|b|c|o, b|c|m|o, a|b|c|m|o, c|n|o,
        // 30 - 3f
        a|c|n|o, c|m|n|o, a|c|m|n|o, b|c|n|o, a|b|c|n|o, b|c|m|n|o, a|d, d|m, a|d|m, b|d, a|b|d, b|d|m, a|b|d|m, d|n, a|d|n, d|m|n,
        // 40 - 4f
        a|d|m|n, b|d|n, a|b|d|n, b|d|m|n, a|b|d|m|n, a|c|d, c|d|m, a|c|d|m, b|c|d, b|c|d|m, a|b|c|d|m, c|d|n, a|c|d|n, a|c|d|m|n, b|c|d|n, a|b|c|d|n,
        // 50 - 5f
        b|c|d|m|n, d|o, a|d|o, d|m|o, a|d|m|o, b|d|o, a|b|d|o, b|d|m|o, a|b|d|m|o, d|n|o, a|d|n|o, d|m|n|o, a|d|m|n|o, b|d|n|o, a|b|d|n|o, b|d|m|n|o,
        // 60 - 6f
        ~(c|p), c|d|o, a|c|d|o, c|d|m|o, a|c|d|m|o, b|c|d|o, ~(m|n|p), b|c|d|m|o, ~(n|p), c|d|n|o, a|c|d|n|o, c|d|m|n|o, ~(b|p), b|c|d|n|o, ~(m|p), ~(a|p),
        // 70 - 7f
        ~p, a|p, m|p, a|m|p, b|p, a|b|p, b|m|p, a|b|m|p, n|p, a|n|p, m|n|p, a|m|n|p, b|n|p, a|b|n|p, b|m|n|p, ~(c|d|o),
        // 80 - 8f
        c|p, a|c|p, c|m|p, a|c|m|p, b|c|p, a|b|c|p, b|c|m|p, ~(d|n|o), c|n|p, a|c|n|p, c|m|n|p, ~(b|d|o), b|c|n|p, ~(d|m|o), ~(a|d|o), ~(d|o),
        // 90 - 9f
        a|o|p, m|o|p, a|m|o|p, b|o|p, b|m|o|p, a|b|m|o|p, n|o|p, a|n|o|p, a|m|n|o|p, b|n|o|p, a|b|n|o|p, b|m|n|o|p, c|o|p, a|c|o|p, c|m|o|p, a|c|m|o|p,
        // a0 - af
        b|c|o|p, a|b|c|o|p, b|c|m|o|p, ~(n|d), c|n|o|p, a|c|n|o|p, c|m|n|o|p, ~(b|d), b|c|n|o|p, ~(d|m), ~(a|d), ~d, a|d|p, d|m|p, a|d|m|p, b|d|p,
        // b0 - bf
        a|b|d|p, b|d|m|p, a|b|d|m|p, d|n|p, a|d|n|p, d|m|n|p, a|d|m|n|p, b|d|n|p, a|b|d|n|p, b|d|m|n|p, ~(c|o), c|d|p, a|c|d|p, c|d|m|p, a|c|d|m|p, b|c|d|p,

        // c0 -cf
        a|b|c|d|p, b|c|d|m|p, ~(n|o), c|d|n|p, a|c|d|n|p, c|d|m|n|p, ~(b|o), b|c|d|n|p, ~(m|o), ~(a|o), ~o, d|o|p, a|d|o|p, d|m|o|p, a|d|m|o|p, b|d|o|p,

        // d0 - df
        a|b|d|o|p, b|d|m|o|p, ~(c|n), d|n|o|p, a|d|n|o|p, d|m|n|o|p, ~(b|c), b|d|n|o|p, ~(c|m), ~(a|c), ~c, a|c|d|o|p, c|d|m|o|p, ~(b|n), b|c|d|o|p, ~(a|n),
        // e0 - e7
        ~n, c|d|n|o|p, ~(b|m), ~b, ~m, ~a, b|c, n|o,

    };
    which = mapping[which];
    if (which & a) octant_segment(self, 0, true);
    if (which & b) octant_segment(self, 1, true);
    if (which & c) octant_segment(self, 2, true);
    if (which & d) octant_segment(self, 3, true);
    if (which & m) octant_segment(self, 0, false);
    if (which & n) octant_segment(self, 1, false);
    if (which & o) octant_segment(self, 2, false);
    if (which & p) octant_segment(self, 3, false);

}

static void
eight_block(Canvas *self, int horizontal, ...) {
    va_list args; va_start(args, horizontal);
    int which;
    while ((which = va_arg(args, int)) >= 0) eight_bar(self, which, horizontal);
    va_end(args);
}

typedef struct Shade {
    bool light, invert, fill_blank;
    Edge which_half;
    uint xnum, ynum;
} Shade;

#define is_odd(x) ((x) & 1u)

static void
shade(Canvas *self, Shade s) {
    const uint square_width = max(1, self->width / s.xnum);
    const uint square_height = max(1, s.ynum ? (self->height / s.ynum) : square_width);
    uint number_of_rows = self->height / square_height;
    uint number_of_cols = self->width / square_width;

    // Make sure the parity is correct
    // (except when that would cause division by zero)
    if (number_of_cols > 1 && is_odd(number_of_cols) != is_odd(s.xnum)) number_of_cols--;
    if (number_of_rows > 1 && is_odd(number_of_rows) != is_odd(s.ynum)) number_of_rows--;

    // Calculate how much space remains unused, and how frequently
    // to insert an extra column/row to fill all of it
    uint excess_cols = minus(self->width, square_width * number_of_cols);
    double square_width_extension = (double)excess_cols / number_of_cols;

    uint excess_rows = minus(self->height, square_height * number_of_rows);
    double square_height_extension = (double)excess_rows / number_of_rows;

    Range rows = {.end=number_of_rows}, cols = {.end=number_of_cols};
    switch(s.which_half) {
        // this is to remove gaps between half-filled characters
        case TOP_EDGE: rows.end /= 2; square_height_extension *= 2; break;
        case BOTTOM_EDGE: rows.start = number_of_rows / 2; square_height_extension *= 2; break;
        case LEFT_EDGE: cols.end /= 2; square_width_extension *= 2; break;
        case RIGHT_EDGE: cols.start = number_of_cols / 2; square_width_extension *= 2; break;
    }

    bool extra_row = false;
    uint ey = 0, old_ey = 0, drawn_rows = 0;

    for (uint r = rows.start; r < rows.end; r++) {
        // Keep track of how much extra height has accumulated, and add an extra row at every passed integer, including 0
        old_ey = ey;
        ey = (uint)ceil(drawn_rows * square_height_extension);
        extra_row = ey != old_ey;
        drawn_rows += 1;
        bool extra_col = false;
        uint ex = 0, old_ex = 0, drawn_cols = 0;
        for (uint c = cols.start; c < cols.end; c++) {
            old_ex = ex;
            ex = (uint)ceil(drawn_cols * square_width_extension);
            extra_col = ex != old_ex;
            drawn_cols += 1;

            // Fill extra rows with semi-transparent pixels that match the pattern
            if (extra_row) {
                uint y = r * square_height + old_ey;
                uint offset = self->width * y;
                for (uint xc = 0; xc < square_width; xc++) {
                    uint x = c * square_width + xc + ex;
                    if (s.light) {
                        if (s.invert) self->mask[offset + x] = is_odd(c) ? 255 : 70;
                        else self->mask[offset + x] = is_odd(c) ? 0 : 70;
                    } else self->mask[offset + x] = is_odd(c) == s.invert ? 120 : 30;
                }
            }
            // Do the same for the extra columns
            if (extra_col) {
                uint x = c * square_width + old_ex;
                for (uint yr = 0; yr < square_height; yr++) {
                    uint y = r * square_height + yr + ey;
                    uint offset = self->width * y;
                    if (s.light) {
                        if (s.invert) self->mask[offset + x] = is_odd(r) ? 255 : 70;
                        else self->mask[offset + x] = is_odd(r) ? 0 : 70;
                    } else self->mask[offset + x] = is_odd(r) == s.invert ? 120 : 30;
                }
            }
            // And in case they intersect, set the corner pixel too
            if (extra_row && extra_col) {
                uint x = c * square_width + old_ex;
                uint y = r * square_height + old_ey;
                uint offset = self->width * y;
                self->mask[offset + x] = 50;
            }

            const bool is_blank = s.invert ^ (is_odd(r) != is_odd(c) || (s.light && is_odd(r)));
            if (!is_blank) {
                // Fill the square
                for (uint yr = 0; yr < square_height; yr++) {
                    uint y = r * square_height + yr + ey;
                    uint offset = self->width * y;
                    for (uint xc = 0; xc < square_width; xc++) {
                        uint x = c * square_width + xc + ex;
                        self->mask[offset + x] = 255;
                    }
                }
            }
        }
    }
    if (!s.fill_blank) return;
    cols = (Range){.end=self->width}; rows = (Range){.end=self->height};
    switch(s.which_half) {
        case BOTTOM_EDGE: rows.end = self->height / 2; break;
        case TOP_EDGE: rows.start = minus(self->height / 2, 1); break;
        case RIGHT_EDGE: cols.end = self->width / 2; break;
        case LEFT_EDGE: cols.start = minus(self->width / 2, 1); break;
    }
    for (uint r = rows.start; r < rows.end; r++) memset(self->mask + r * self->width + cols.start, 255, cols.end - cols.start);
}

static void
apply_mask(Canvas *self, uint8_t *mask) {
    for (uint y = 0; y < self->height; y++) {
        uint offset = y * self->width;
        for (uint x = 0; x < self->width; x++) {
            uint p = offset + x;
            self->mask[p] = (uint8_t)round((mask[p] / 255.0) * self->mask[p]);
        }
    }
}

static void
cross_shade(Canvas *self, bool rotate) {
    static const uint num_of_lines = 7;
    uint line_thickness = max(self->supersample_factor, self->width / num_of_lines);
    uint delta = 2 * line_thickness;
    uint y1 = 0, y2 = self->height;
    if (rotate) SWAP(y1, y2);
    for (uint x = 0; x < self->width; x += delta) {
        thick_line(self, line_thickness, (Point){.x=0 + x, .y=y1}, (Point){.x=self->width + x, .y=y2});
        thick_line(self, line_thickness, (Point){.x=0 - x, .y=y1}, (Point){.x=self->width - x, .y=y2});
    }
}

static void
quad(Canvas *self, Corner which) {
    uint x = which & LEFT_EDGE ? 0 : 1, y = which & TOP_EDGE ? 0 : 1;
    uint num_cols = self->width / 2;
    uint left = x * num_cols;
    uint right = x ? self->width : num_cols;
    uint num_rows = self->height / 2;
    uint top = y * num_rows;
    uint bottom = y ? self->height : num_rows;
    for (uint r = top; r < bottom; r++) {
        uint off = r * self->width;
        memset(self->mask + off + left, 255, right - left);
    }
}

static void
quads(Canvas *self, ...) {
    va_list args; va_start(args, self);
    int which;
    while ((which = va_arg(args, int))) quad(self, which);
    va_end(args);
}

static void
smooth_mosaic(Canvas *self, bool lower, double ax, double ay, double bx, double by) {
    StraightLine l = line_from_points(
        ax * minus(self->width, 1), ay * minus(self->height, 1), bx * minus(self->width, 1), by * minus(self->height, 1));
    for (uint y = 0; y < self->height; y++) {
        uint offset = y * self->width;
        for (uint x = 0; x < self->width; x++) {
            double edge = line_y(l, x);
            if ((lower && y >= edge) || (!lower && y <= edge)) self->mask[offset + x] = 255;
        }
    }
}

static void
half_triangle(Canvas *self, Edge which, bool inverted) {
    uint mid_x = self->width / 2, mid_y = self->height / 2;
    StraightLine u, l;
    append_limit(self, 0, 0); // ensure space for limits
#define set_limits(startx, endx, a, b) for (uint x = startx; x < endx; x++) self->y_limits[x] = (Limit){.upper=b, .lower=a};
    switch (which) {
        case LEFT_EDGE:
            u = line_from_points(0, 0, mid_x, mid_y);
            l = line_from_points(0, minus(self->height, 1), mid_x, mid_y);
            set_limits(0, self->width, line_y(u, x), line_y(l, x));
            break;
        case TOP_EDGE:
            l = line_from_points(0, 0, mid_x, mid_y);
            set_limits(0, mid_x, 0, line_y(l, x));
            l = line_from_points(mid_x, mid_y, minus(self->width, 1), 0);
            set_limits(mid_x, self->width, 0, line_y(l, x));
            break;
        case RIGHT_EDGE:
            u = line_from_points(mid_x, mid_y, minus(self->width, 1), 0);
            l = line_from_points(mid_x, mid_y, minus(self->width, 1), minus(self->height, 1));
            set_limits(0, self->width, line_y(u, x), line_y(l, x));
            break;
        case BOTTOM_EDGE:
            l = line_from_points(0, minus(self->height, 1), mid_x, mid_y);
            set_limits(0, mid_x, line_y(l, x), minus(self->height, 1));
            l = line_from_points(mid_x, mid_y, minus(self->width, 1), minus(self->height, 1));
            set_limits(mid_x, self->width, line_y(l, x), minus(self->height, 1));
            break;
    }
    self->y_limits_count = self->width;
    fill_region(self, inverted);
#undef set_limits
}

static void
mid_lines(Canvas *self, uint level, ...) {
    uint mid_x = self->width / 2, mid_y = self->height / 2;
    const uint th = thickness(self, level, true);
    const Point l = {.x=0, .y=mid_y}, t={.x=mid_x, .y=0}, r={.x=minus(self->width, 1), .y=mid_y}, b={.x=mid_x, .y=minus(self->height, 1)};
    va_list args; va_start(args, level);
    Corner which;
    while ((which = va_arg(args, int)) > 0) {
        Point p1, p2;
        switch(which) {
            case TOP_LEFT: p1 = l; p2 = t; break;
            case TOP_RIGHT: p1 = r; p2 = t; break;
            case BOTTOM_LEFT: p1 = l; p2 = b; break;
            case BOTTOM_RIGHT: p1 = r; p2 = b; break;
        }
        thick_line(self, th, p1, p2);
    }
    va_end(args);
}

static Point*
get_fading_lines(uint total_length, uint num, Edge fade) {
    uint step = total_length / num, d1 = 0; int dir = 1;
    if (fade == LEFT_EDGE || fade == TOP_EDGE) { dir = -1; d1 = total_length; }
    Point *ans = malloc(num * sizeof(Point));
    if (!ans) fatal("Out of memory");
    for (uint i = 0; i < num; i++) {
        uint sz = step * (num - i) / (num + 1);
        if (step > 2 && sz >= step - 1) sz = step - 2;
        int d2 = d1 + dir * sz; if (d2 < 0) d2 = 0;
        if (d1 <= (uint)d2) { ans[i].x = d1; ans[i].y = d2; }
        else { ans[i].x = d2; ans[i].y = d1; }
        d1 += step * dir;
    }
    return ans;
}

static void
fading_hline(Canvas *self, uint level, uint num, Edge fade) {
    uint y = self->height / 2;
    RAII_ALLOC(Point, pts, get_fading_lines(self->width, num, fade));
    for (uint i = 0; i < num; i++) {
        uint x1 = pts[i].x, x2 = pts[i].y;
        draw_hline(self, x1, x2, y, level);
    }
}

static void
fading_vline(Canvas *self, uint level, uint num, Edge fade) {
    uint x = self->width / 2;
    RAII_ALLOC(Point, pts, get_fading_lines(self->height, num, fade));
    for (uint i = 0; i < num; i++) {
        uint y1 = pts[i].x, y2 = pts[i].y;
        draw_vline(self, y1, y2, x, level);
    }
}

static void
rounded_corner(Canvas *self, uint level, Corner which) {
    // Render a rounded box corner.
    const Range hori_line_range = hline_limits(self, half_height(self), level);
    const Range vert_line_range = vline_limits(self, half_width(self), level);
    const uint hori_line_height = hori_line_range.end - hori_line_range.start;
    const uint vert_line_width = vert_line_range.end - vert_line_range.start;
    const double adjusted_Hx = (double)vert_line_range.start + (double)vert_line_width / 2.0;
    const double adjusted_Hy = (double)hori_line_range.start + (double)hori_line_height / 2.0;
    const double stroke = (double)max(hori_line_height, vert_line_width);
    const double corner_radius = fmin(adjusted_Hx, adjusted_Hy);
    const double bx = adjusted_Hx - corner_radius;
    const double by = adjusted_Hy - corner_radius;

    // Anti-aliasing on corner
    const double aa_corner = (double)self->supersample_factor * 0.5;
    const double half_stroke = 0.5 * stroke;

    const double x_shift = (which & RIGHT_EDGE) ? adjusted_Hx : -adjusted_Hx;
    const double y_shift = (which & TOP_EDGE) ? -adjusted_Hy : adjusted_Hy;

    for (uint y = 0; y < self->height; y++) {
        const double sample_y = (double)y + y_shift + 0.5;
        const double pos_y = sample_y - adjusted_Hy;
        const uint row_off = y * self->width;

        for (uint x = 0; x < self->width; x++) {
            const double sample_x = (double)x + x_shift + 0.5;
            const double pos_x = sample_x - adjusted_Hx;

            const double qx = fabs(pos_x) - bx;
            const double qy = fabs(pos_y) - by;
            const double dx = qx > 0.0 ? qx : 0.0;
            const double dy = qy > 0.0 ? qy : 0.0;
            const double dist = hypot(dx, dy) + fmin(fmax(qx, qy), 0.0) - corner_radius;

            const double aa = (qx > 1e-7 && qy > 1e-7) ? aa_corner : 0.0;
            const double outer = half_stroke - dist;
            const double inner = -half_stroke - dist;
            const double alpha = smoothstep(-aa, aa, outer) - smoothstep(-aa, aa, inner);

            if (alpha <= 0.0) continue;
            const uint8_t value = (uint8_t)lrint(unit_double(alpha) * 255.0);
            uint8_t *p = &self->mask[row_off + x];
            if (value > *p) *p = value;
        }
    }
}

static void
commit(Canvas *self, Edge lines, bool solid) {
    static const uint level = 1; static const double scale = 0.9;
    uint hw = half_width(self), hh = half_height(self);
    if (lines & RIGHT_EDGE) draw_hline(self, hw, self->width, hh, level);
    if (lines & LEFT_EDGE) draw_hline(self, 0, hw, hh, level);
    if (lines & TOP_EDGE) draw_vline(self, 0, hh, hw, level);
    if (lines & BOTTOM_EDGE) draw_vline(self, hh, self->height, hw, level);
    fill_circle(self, scale, 0, false);
    if (!solid) fill_circle(self, scale, thickness(self, level, true), true);
}

// thin and fat line levels
#define t 1u
#define f 3u

static void
corner(Canvas *self, uint hlevel, uint vlevel, Corner which) {
    const uint v_thickness = thickness(self, vlevel, true);

    uint v_half_tickness;
    if (which & LEFT_EDGE && v_thickness % 2 != 0) {
        v_half_tickness = v_thickness / 2 + 1;
    } else {
        v_half_tickness = v_thickness / 2;
    }

    half_hline(self, hlevel, which & RIGHT_EDGE, v_half_tickness);
    half_vline(self, vlevel, which & BOTTOM_EDGE, 0);
}

static void
cross(Canvas *self, uint which) {
    static const uint level_map[16][4] = {
        {t, t, t, t}, {f, t, t, t}, {t, f, t, t}, {f, f, t, t}, {t, t, f, t}, {t, t, t, f}, {t, t, f, f},
        {f, t, f, t}, {t, f, f, t}, {f, t, t, f}, {t, f, t, f}, {f, f, f, t}, {f, f, t, f}, {f, t, f, f},
        {t, f, f, f}, {f, f, f, f}
    };
    const uint *m = level_map[which];
    half_hline(self, m[0], false, 0); half_hline(self, m[1], true, 0);
    half_vline(self, m[2], false, 0); half_vline(self, m[3], true, 0);
}

static void
vert_t(Canvas *self, uint base_char, uint variation) {
    static const uint level_map[8][3] = {
        {t, t, t}, {t, f, t}, {f, t, t}, {t, t, f}, {f, t, f}, {f, f, t}, {t, f, f}, {f, f, f}
    };
    const uint *m = level_map[variation];
    half_vline(self, m[0], false, 0);
    half_hline(self, m[1], base_char != L'┤', 0);
    half_vline(self, m[2], true, 0);
}

static void
horz_t(Canvas *self, uint base_char, uint variation) {
    static const uint level_map[8][3] = {
        {t, t, t}, {f, t, t}, {t, f, t}, {f, f, t}, {t, t, f}, {f, t, f}, {t, f, f}, {f, f, f}
    };
    const uint *m = level_map[variation];
    half_hline(self, m[0], false, 0);
    half_hline(self, m[1], true, 0);
    half_vline(self, m[2], base_char != L'┴', 0);
}

static void
dvcorner(Canvas *self, uint level, Corner which) {
    Point dline_position = half_dhline(self, level, which & LEFT_EDGE, TOP_EDGE | BOTTOM_EDGE);

    if (which & BOTTOM_EDGE) {
        Range bottom_limit = hline_limits(self, dline_position.y, level);
        draw_vline(self, 0, bottom_limit.end, half_width(self), level);
    } else {
        Range top_limit = hline_limits(self, dline_position.x, level);
        draw_vline(self, top_limit.start, self->height, half_width(self), level);
    }
}

static void
dhcorner(Canvas *self, uint level, Corner which) {
    Point dline_position = half_dvline(self, level, which & TOP_EDGE, LEFT_EDGE | RIGHT_EDGE);

    if (which & RIGHT_EDGE) {
        Range right_limit = vline_limits(self, dline_position.y, level);
        draw_hline(self, 0, right_limit.end, half_height(self), level);
    } else {
        Range left_limit = vline_limits(self, dline_position.x, level);
        draw_hline(self, left_limit.start, self->width, half_height(self), level);
    }
}

static void
dcorner(Canvas *self, uint level, Corner which) {
    uint hgap = thickness(self, level + 1, false);
    uint vgap = thickness(self, level + 1, true);
    uint x1 = self->width / 2, x2 = self->width / 2;
    if (which & RIGHT_EDGE) x1 = 0; else x2 = self->width;
    uint ypos = self->height / 2;
    int ydelta = which & BOTTOM_EDGE ? hgap : -hgap;
    if (which & RIGHT_EDGE) x2 += vgap; else x1 = minus(x1, vgap);
    draw_hline(self, x1, x2, ypos + ydelta, level);
    if (which & RIGHT_EDGE) x2 = minus(x2, 2 * vgap); else x1 += 2 * vgap;
    draw_hline(self, x1, x2, ypos - ydelta, level);

    uint xpos = self->width / 2;
    int xdelta = (which & LEFT_EDGE) ? vgap : -vgap;
    Range top_hline_limit = hline_limits(self, ypos + ydelta, level);
    Range bottom_hline_limit = hline_limits(self, ypos - ydelta, level);
    if (which & TOP_EDGE) {
        draw_vline(self, top_hline_limit.start, self->height, xpos - xdelta, level);
        draw_vline(self, bottom_hline_limit.start, self->height, xpos + xdelta, level);
    } else {
        draw_vline(self, 0, bottom_hline_limit.end, xpos + xdelta, level);
        draw_vline(self, 0, top_hline_limit.end, xpos - xdelta, level);
    }
}


static void
dpip(Canvas *self, uint level, Edge which) {
    uint x1, x2, y1, y2;
    if (which & (LEFT_EDGE | RIGHT_EDGE)) {
        Point p = dvline(self, level, LEFT_EDGE | RIGHT_EDGE);
        if (which & LEFT_EDGE) { x1 = 0; x2 = p.x; } else { x1 = p.y; x2 =self->width; }
        draw_hline(self, x1, x2, self->height / 2, level);
    } else {
        Point p = dhline(self, level, TOP_EDGE | BOTTOM_EDGE);
        if (which & TOP_EDGE) { y1 = 0; y2 = p.x; } else { y1 = p.y; y2 = self->height; }
        draw_vline(self, y1, y2, self->width / 2, level);
    }
}

static void
braille_dot(Canvas *self, uint col, uint row) {
    static const uint num_x_dots = 2, num_y_dots = 4;
    unsigned x_gaps[num_x_dots * 2], y_gaps[num_y_dots * 2];
    unsigned dot_width = distribute_dots(self->width, num_x_dots, x_gaps, x_gaps + num_x_dots);
    unsigned dot_height = distribute_dots(self->height, num_y_dots, y_gaps, y_gaps + num_y_dots);
    uint x_start = x_gaps[col] + col * dot_width;
    uint y_start = y_gaps[row] + row * dot_height;
    if (y_start < self->height && x_start < self->width) {
        for (uint y = y_start; y < min(self->height, y_start + dot_height); y++) {
            uint offset = y * self->width;
            memset(self->mask + offset + x_start, 255, minus(min(self->width, x_start + dot_width), x_start));
        }
    }
}


static void
braille(Canvas *self, uint8_t which) {
    if (!which) return;
    for (uint8_t i = 0, mask = 1; i < 8; i++, mask <<= 1) {
        if (which & mask) {
            uint q = i + 1, col, row;
            switch(q) { case 1: case 2: case 3: case 7: col = 0; break; default: col = 1; break; }
            switch(q) { case 1: case 4: row = 0; break; case 2: case 5: row = 1; break; case 3: case 6: row = 2; break; default: row = 3; }
            braille_dot(self, col, row);
        }
    }
}

static void
draw_sextant(Canvas *self, uint row, uint col) {
    Point start = {0}, end = {.x=self->width, .y = self->height};
    switch(row) {
        case 0: end.y = self->height / 3; break;
        case 1: start.y = self->height / 3; end.y = 2 * self->height / 3; break;
        case 2: start.y = 2 * self->height / 3; break;
    }
    switch(col) {
        case 0: end.x = self->width / 2; break;
        default: start.x = self->width / 2; break;
    }
    for (int r = start.y; r < end.y; r++) {
        uint off = r * self->width;
        memset(self->mask + off + start.x, 255, end.x - start.x);
    }
}

static void
sextant(Canvas *self, uint which) {
#define add_row(q, r) if (q & 1) { draw_sextant(self, r, 0); } if (q & 2) { draw_sextant(self, r, 1); }
    add_row(which % 4, 0)
    add_row(which / 4, 1)
    add_row(which / 16, 2)
#undef add_row
}

void
render_box_char(char_type ch, uint8_t *buf, unsigned width, unsigned height, double dpi_x, double dpi_y, double scale) {
    Canvas canvas = {.mask=buf, .width = width, .height = height, .dpi={.x=dpi_x, .y=dpi_y}, .supersample_factor=1u, .scale=scale}, ss = canvas;
    ss.mask = buf + width*height; ss.supersample_factor = SUPERSAMPLE_FACTOR;
    ss.width *= SUPERSAMPLE_FACTOR; ss.height *= SUPERSAMPLE_FACTOR;
    fill_canvas(&canvas, 0);
    Canvas *c = &canvas;

#define SB(ch, ...) case ch: fill_canvas(&ss, 0); c = &ss, __VA_ARGS__; downsample(&ss, &canvas);
#define CC(ch, ...) case ch: __VA_ARGS__; break
#define SS(ch, ...) SB(ch, __VA_ARGS__); break
#define C(ch, func, ...) CC(ch, func(c, __VA_ARGS__))
#define S(ch, func, ...) SS(ch, func(c, __VA_ARGS__))
START_ALLOW_CASE_RANGE

    switch(ch) {
        default: log_error("Unknown box drawing character: U+%x rendered as blank", ch); break;
        case L'█': fill_canvas(c, 255); break;

        C(L'─', hline, 1);
        C(L'━', hline, 3);
        C(L'│', vline, 1);
        C(L'┃', vline, 3);

        C(L'╌', hholes, 1, 1);
        C(L'╍', hholes, 3, 1);
        C(L'┄', hholes, 1, 2);
        C(L'┅', hholes, 3, 2);
        C(L'┈', hholes, 1, 3);
        C(L'┉', hholes, 3, 3);

        C(L'╎', vholes, 1, 1);
        C(L'╏', vholes, 3, 1);
        C(L'┆', vholes, 1, 2);
        C(L'┇', vholes, 3, 2);
        C(L'┊', vholes, 1, 3);
        C(L'┋', vholes, 3, 3);

        C(L'╴', half_hline, 1, false, 0);
        C(L'╵', half_vline, 1, false, 0);
        C(L'╶', half_hline, 1, true, 0);
        C(L'╷', half_vline, 1, true, 0);
        C(L'╸', half_hline, 3, false, 0);
        C(L'╹', half_vline, 3, false, 0);
        C(L'╺', half_hline, 3, true, 0);
        C(L'╻', half_vline, 3, true, 0);
        CC(L'╾', half_hline(c, 3, false, 0); half_hline(c, 1, true, 0));
        CC(L'╼', half_hline(c, 1, false, 0); half_hline(c, 3, true, 0));
        CC(L'╿', half_vline(c, 3, false, 0); half_vline(c, 1, true, 0));
        CC(L'╽', half_vline(c, 1, false, 0); half_vline(c, 3, true, 0));

        S(L'', triangle, true, false);
        S(L'', triangle, true, true);
        SS(L'', half_cross_line(c, 1, TOP_LEFT); half_cross_line(c, 1, BOTTOM_LEFT));
        S(L'', triangle, false, false);
        S(L'', triangle, false, true);
        SS(L'', half_cross_line(c, 1, TOP_RIGHT); half_cross_line(c, 1, BOTTOM_RIGHT));

        S(L'', filled_D, true);
        S(L'◗', filled_D, true);
        S(L'', filled_D, false);
        S(L'◖', filled_D, false);
        C(L'', rounded_separator, 1, true);
        C(L'', rounded_separator, 1, false);

        S(L'', cross_line, 1, true);
        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'╱', cross_line, 1, false);
        SS(L'╳', cross_line(c, 1, false); cross_line(c, 1, true));

        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);

        C(L'', spinner, 1, 235, 305);
        C(L'', spinner, 1, 270, 390);
        C(L'', spinner, 1, 315, 470);
        C(L'', spinner, 1, 360, 540);
        C(L'', spinner, 1, 80, 220);
        C(L'', spinner, 1, 170, 270);
        C(L'○', spinner, 0, 0, 360);
        C(L'◜', spinner, 1, 180, 270);
        C(L'◝', spinner, 1, 270, 360);
        C(L'◞', spinner, 1, 360, 450);
        C(L'◟', spinner, 1, 450, 540);
        C(L'◠', spinner, 1, 180, 360);
        C(L'◡', spinner, 1, 0, 180);
        S(L'●', fill_circle, 1.0, 0, false);
        S(L'◉', draw_fish_eye, 0);

        C(L'═', dhline, 1, TOP_EDGE | BOTTOM_EDGE);
        C(L'║', dvline, 1, LEFT_EDGE | RIGHT_EDGE);
        CC(L'╞', vline(c, 1); half_dhline(c, 1, true, TOP_EDGE | BOTTOM_EDGE));
        CC(L'╡', vline(c, 1); half_dhline(c, 1, false, TOP_EDGE | BOTTOM_EDGE));
        CC(L'╥', hline(c, 1); half_dvline(c, 1, true, LEFT_EDGE | RIGHT_EDGE));
        CC(L'╨', hline(c, 1); half_dvline(c, 1, false, LEFT_EDGE | RIGHT_EDGE));
        CC(L'╪', vline(c, 1); dhline(c, 1, TOP_EDGE | BOTTOM_EDGE));
        CC(L'╫', hline(c, 1), dvline(c, 1, LEFT_EDGE | RIGHT_EDGE));
        CC(L'╬', inner_corner(c, 1, TOP_LEFT); inner_corner(c, 1, TOP_RIGHT); inner_corner(c, 1, BOTTOM_LEFT); inner_corner(c, 1, BOTTOM_RIGHT));
        CC(L'╠', inner_corner(c, 1, TOP_RIGHT); inner_corner(c, 1, BOTTOM_RIGHT); dvline(c, 1, LEFT_EDGE));
        CC(L'╣', inner_corner(c, 1, TOP_LEFT); inner_corner(c, 1, BOTTOM_LEFT); dvline(c, 1, RIGHT_EDGE));
        CC(L'╦', inner_corner(c, 1, BOTTOM_LEFT); inner_corner(c, 1, BOTTOM_RIGHT); dhline(c, 1, TOP_EDGE));
        CC(L'╩', inner_corner(c, 1, TOP_LEFT); inner_corner(c, 1, TOP_RIGHT); dhline(c, 1, BOTTOM_EDGE));

#define EH(ch, ...) C(ch, eight_block, true, __VA_ARGS__, -1);
        EH(L'▔', 0);
        EH(L'▀', 0, 1, 2, 3);
        EH(L'▁', 7);
        EH(L'▂', 6, 7);
        EH(L'▃', 5, 6, 7);
        EH(L'▄', 4, 5, 6, 7);
        EH(L'▅', 3, 4, 5, 6, 7);
        EH(L'▆', 2, 3, 4, 5, 6, 7);
        EH(L'▇', 1, 2, 3, 4, 5, 6, 7);
#undef EH
#define EV(ch, ...) C(ch, eight_block, false, __VA_ARGS__, -1);
        EV(L'▉', 0, 1, 2, 3, 4, 5, 6);
        EV(L'▊', 0, 1, 2, 3, 4, 5);
        EV(L'▋', 0, 1, 2, 3, 4);
        EV(L'▌', 0, 1, 2, 3);
        EV(L'▍', 0, 1, 2);
        EV(L'▎', 0, 1);
        EV(L'▏', 0);
        EV(L'▕', 7);
        EV(L'▐', 4, 5, 6, 7);
#undef EV
#define SH(ch, ...) C(ch, shade, (Shade){ __VA_ARGS__ });
        SH(L'░', .xnum=12, .light=true);
        SH(L'▒', .xnum=12);
        SH(L'▓', .xnum=12, .light=true, .invert=true);
        SH(L'🮌', .xnum=12, .which_half=LEFT_EDGE);
        SH(L'🮍', .xnum=12, .which_half=RIGHT_EDGE);
        SH(L'🮎', .xnum=12, .which_half=TOP_EDGE);
        SH(L'🮏', .xnum=12, .which_half=BOTTOM_EDGE);
        SH(L'🮐', .xnum=12, .invert=true);
        SH(L'🮑', .xnum=12, .invert=true, .fill_blank=true, .which_half=BOTTOM_EDGE);
        SH(L'🮒', .xnum=12, .invert=true, .fill_blank=true, .which_half=TOP_EDGE);
        SH(L'🮓', .xnum=12, .invert=true, .fill_blank=true, .which_half=RIGHT_EDGE);
        SH(L'🮔', .xnum=12, .invert=true, .fill_blank=true, .which_half=LEFT_EDGE);
        SH(L'🮕', .xnum=4, .ynum=4);
        SH(L'🮖', .xnum=4, .ynum=4, .invert=true);
        SH(L'🮗', .xnum=1, .ynum=4, .invert=true);
#define M(ch, corner) SB(ch, corner_triangle(c, corner)); \
            memcpy(ss.mask, canvas.mask, sizeof(canvas.mask[0]) * canvas.width * canvas.height); \
            fill_canvas(&canvas, 0); shade(&canvas, (Shade){.xnum=12}); \
            apply_mask(&canvas, ss.mask); break;
        M(L'🮜', TOP_LEFT);
        M(L'🮝', TOP_RIGHT);
        M(L'🮞', BOTTOM_RIGHT);
        M(L'🮟', BOTTOM_LEFT);
#undef M
#undef SH
        S(L'🮘', cross_shade, false);
        S(L'🮙', cross_shade, true);

        C(L'▖', quad, BOTTOM_LEFT);
        C(L'▗', quad, BOTTOM_RIGHT);
        C(L'▘', quad, TOP_LEFT);
        C(L'▝', quad, TOP_RIGHT);
        C(L'▙', quads, TOP_LEFT, BOTTOM_LEFT, BOTTOM_RIGHT, 0);
        C(L'▚', quads, TOP_LEFT, BOTTOM_RIGHT, 0);
        C(L'▛', quads, TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, 0);
        C(L'▜', quads, TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT, 0);
        C(L'▞', quads, TOP_RIGHT, BOTTOM_LEFT, 0);
        C(L'▟', quads, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, 0);

        S(L'🬼', smooth_mosaic, true, 0, 2. / 3, 0.5, 1);
        S(L'🬽', smooth_mosaic, true, 0, 2. / 3, 1, 1);
        S(L'🬾', smooth_mosaic, true, 0, 1. / 3, 0.5, 1);
        S(L'🬿', smooth_mosaic, true, 0, 1. / 3, 1, 1);
        S(L'🭀', smooth_mosaic, true, 0, 0, 0.5, 1);

        S(L'🭁', smooth_mosaic, true, 0, 1. / 3, 0.5, 0);
        S(L'🭂', smooth_mosaic, true, 0, 1. / 3, 1, 0);
        S(L'🭃', smooth_mosaic, true, 0, 2. / 3, 0.5, 0);
        S(L'🭄', smooth_mosaic, true, 0, 2. / 3, 1, 0);
        S(L'🭅', smooth_mosaic, true, 0, 1, 0.5, 0);
        S(L'🭆', smooth_mosaic, true, 0, 2. / 3, 1, 1. / 3);

        S(L'🭇', smooth_mosaic, true, 0.5, 1, 1, 2. / 3);
        S(L'🭈', smooth_mosaic, true, 0, 1, 1, 2. / 3);
        S(L'🭉', smooth_mosaic, true, 0.5, 1, 1, 1. / 3);
        S(L'🭊', smooth_mosaic, true, 0, 1, 1, 1. / 3);
        S(L'🭋', smooth_mosaic, true, 0.5, 1, 1, 0);

        S(L'🭌', smooth_mosaic, true, 0.5, 0, 1, 1. / 3);
        S(L'🭍', smooth_mosaic, true, 0, 0, 1, 1. / 3);
        S(L'🭎', smooth_mosaic, true, 0.5, 0, 1, 2. / 3);
        S(L'🭏', smooth_mosaic, true, 0, 0, 1, 2. / 3);
        S(L'🭐', smooth_mosaic, true, 0.5, 0, 1, 1);
        S(L'🭑', smooth_mosaic, true, 0, 1. / 3, 1, 2. / 3);

        S(L'🭒', smooth_mosaic, false, 0, 2. / 3, 0.5, 1);
        S(L'🭓', smooth_mosaic, false, 0, 2. / 3, 1, 1);
        S(L'🭔', smooth_mosaic, false, 0, 1. / 3, 0.5, 1);
        S(L'🭕', smooth_mosaic, false, 0, 1. / 3, 1, 1);
        S(L'🭖', smooth_mosaic, false, 0, 0, 0.5, 1);

        S(L'🭗', smooth_mosaic, false, 0, 1. / 3, 0.5, 0);
        S(L'🭘', smooth_mosaic, false, 0, 1. / 3, 1, 0);
        S(L'🭙', smooth_mosaic, false, 0, 2. / 3, 0.5, 0);
        S(L'🭚', smooth_mosaic, false, 0, 2. / 3, 1, 0);
        S(L'🭛', smooth_mosaic, false, 0, 1, 0.5, 0);

        S(L'🭜', smooth_mosaic, false, 0, 2. / 3, 1, 1. / 3);
        S(L'🭝', smooth_mosaic, false, 0.5, 1, 1, 2. / 3);
        S(L'🭞', smooth_mosaic, false, 0, 1, 1, 2. / 3);
        S(L'🭟', smooth_mosaic, false, 0.5, 1, 1, 1. / 3);
        S(L'🭠', smooth_mosaic, false, 0, 1, 1, 1. / 3);
        S(L'🭡', smooth_mosaic, false, 0.5, 1, 1, 0);

        S(L'🭢', smooth_mosaic, false, 0.5, 0, 1, 1. / 3);
        S(L'🭣', smooth_mosaic, false, 0, 0, 1, 1. / 3);
        S(L'🭤', smooth_mosaic, false, 0.5, 0, 1, 2. / 3);
        S(L'🭥', smooth_mosaic, false, 0, 0, 1, 2. / 3);
        S(L'🭦', smooth_mosaic, false, 0.5, 0, 1, 1);
        S(L'🭧', smooth_mosaic, false, 0, 1. / 3, 1, 2. / 3);

        S(L'🭨', half_triangle, LEFT_EDGE, true);
        S(L'🭩', half_triangle, TOP_EDGE, true);
        S(L'🭪', half_triangle, RIGHT_EDGE, true);
        S(L'🭫', half_triangle, BOTTOM_EDGE, true);
        S(L'🭬', half_triangle, LEFT_EDGE, false);
        SS(L'🮛', half_triangle(c, LEFT_EDGE, false), half_triangle(c, RIGHT_EDGE, false));
        S(L'🭭', half_triangle, TOP_EDGE, false);
        S(L'🭮', half_triangle, RIGHT_EDGE, false);
        S(L'🭯', half_triangle, BOTTOM_EDGE, false);
        SS(L'🮚', half_triangle(c, BOTTOM_EDGE, false), half_triangle(c, TOP_EDGE, false));

        CC(L'🭼', eight_bar(c, 0, false); eight_bar(c, 7, true));
        CC(L'🭽', eight_bar(c, 0, false); eight_bar(c, 0, true));
        CC(L'🭾', eight_bar(c, 7, false); eight_bar(c, 0, true));
        CC(L'🭿', eight_bar(c, 7, false); eight_bar(c, 7, true));
        CC(L'🮀', eight_bar(c, 0, true); eight_bar(c, 7, true));
        CC(L'🮁', eight_bar(c, 0, true); eight_bar(c, 2, true); eight_bar(c, 4, true); eight_bar(c, 7, true));

        C(L'🮂', eight_block, true, 0, 1, -1);
        C(L'🮃', eight_block, true, 0, 1, 2, -1);
        C(L'🮄', eight_block, true, 0, 1, 2, 3, 4, -1);
        C(L'🮅', eight_block, true, 0, 1, 2, 3, 4, 5, -1);
        C(L'🮆', eight_block, true, 0, 1, 2, 3, 4, 5, 6, -1);
        C(L'🮇', eight_block, false, 6, 7, -1);
        C(L'🮈', eight_block, false, 5, 6, 7, -1);
        C(L'🮉', eight_block, false, 3, 4, 5, 6, 7, -1);
        C(L'🮊', eight_block, false, 2, 3, 4, 5, 6, 7, -1);
        C(L'🮋', eight_block, false, 1, 2, 3, 4, 5, 6, 7, -1);

        S(L'🮠', mid_lines, 1, TOP_LEFT, 0);
        S(L'🮡', mid_lines, 1, TOP_RIGHT, 0);
        S(L'🮢', mid_lines, 1, BOTTOM_LEFT, 0);
        S(L'🮣', mid_lines, 1, BOTTOM_RIGHT, 0);
        S(L'🮤', mid_lines, 1, TOP_LEFT, BOTTOM_LEFT, 0);
        S(L'🮥', mid_lines, 1, TOP_RIGHT, BOTTOM_RIGHT, 0);
        S(L'🮦', mid_lines, 1, BOTTOM_RIGHT, BOTTOM_LEFT, 0);
        S(L'🮧', mid_lines, 1, TOP_RIGHT, TOP_LEFT, 0);
        S(L'🮨', mid_lines, 1, BOTTOM_RIGHT, TOP_LEFT, 0);
        S(L'🮩', mid_lines, 1, BOTTOM_LEFT, TOP_RIGHT, 0);
        S(L'🮪', mid_lines, 1, BOTTOM_LEFT, TOP_RIGHT, BOTTOM_RIGHT, 0);
        S(L'🮫', mid_lines, 1, BOTTOM_LEFT, TOP_LEFT, BOTTOM_RIGHT, 0);
        S(L'🮬', mid_lines, 1, TOP_RIGHT, TOP_LEFT, BOTTOM_RIGHT, 0);
        S(L'🮭', mid_lines, 1, TOP_RIGHT, TOP_LEFT, BOTTOM_LEFT, 0);
        S(L'🮮', mid_lines, 1, TOP_RIGHT, BOTTOM_RIGHT, TOP_LEFT, BOTTOM_LEFT, 0);

        C(L'', hline, 1);
        C(L'', vline, 1);
        C(L'', fading_hline, 1, 4, RIGHT_EDGE);
        C(L'', fading_hline, 1, 4, LEFT_EDGE);
        C(L'', fading_vline, 1, 5, BOTTOM_EDGE);
        C(L'', fading_vline, 1, 5, TOP_EDGE);

        C(L'', rounded_corner, 1, TOP_LEFT);
        C(L'', rounded_corner, 1, TOP_RIGHT);
        C(L'', rounded_corner, 1, BOTTOM_LEFT);
        C(L'', rounded_corner, 1, BOTTOM_RIGHT);

        CC(L'', vline(c, 1); rounded_corner(c, 1, BOTTOM_LEFT));
        CC(L'', vline(c, 1); rounded_corner(c, 1, TOP_LEFT));
        CC(L'', rounded_corner(c, 1, BOTTOM_LEFT), rounded_corner(c, 1, TOP_LEFT));
        CC(L'', vline(c, 1); rounded_corner(c, 1, BOTTOM_RIGHT));
        CC(L'', vline(c, 1); rounded_corner(c, 1, TOP_RIGHT));
        CC(L'', rounded_corner(c, 1, TOP_RIGHT), rounded_corner(c, 1, BOTTOM_RIGHT));
        CC(L'', hline(c, 1); rounded_corner(c, 1, TOP_RIGHT));
        CC(L'', hline(c, 1); rounded_corner(c, 1, TOP_LEFT));
        CC(L'', rounded_corner(c, 1, TOP_LEFT), rounded_corner(c, 1, TOP_RIGHT));
        CC(L'', hline(c, 1); rounded_corner(c, 1, BOTTOM_RIGHT));
        CC(L'', hline(c, 1); rounded_corner(c, 1, BOTTOM_LEFT));
        CC(L'', rounded_corner(c, 1, BOTTOM_LEFT), rounded_corner(c, 1, BOTTOM_RIGHT));
        CC(L'', vline(c, 1); rounded_corner(c, 1, BOTTOM_LEFT), rounded_corner(c, 1, BOTTOM_RIGHT));
        CC(L'', vline(c, 1); rounded_corner(c, 1, TOP_LEFT), rounded_corner(c, 1, TOP_RIGHT));
        CC(L'', hline(c, 1); rounded_corner(c, 1, TOP_RIGHT), rounded_corner(c, 1, BOTTOM_RIGHT));
        CC(L'', hline(c, 1); rounded_corner(c, 1, BOTTOM_LEFT), rounded_corner(c, 1, TOP_LEFT));
        CC(L'', vline(c, 1); rounded_corner(c, 1, TOP_LEFT), rounded_corner(c, 1, BOTTOM_RIGHT));
        CC(L'', vline(c, 1); rounded_corner(c, 1, TOP_RIGHT), rounded_corner(c, 1, BOTTOM_LEFT));
        CC(L'', hline(c, 1); rounded_corner(c, 1, TOP_LEFT), rounded_corner(c, 1, BOTTOM_RIGHT));
        CC(L'', hline(c, 1); rounded_corner(c, 1, TOP_RIGHT), rounded_corner(c, 1, BOTTOM_LEFT));

#define P(ch, lines) S(ch, commit, lines, true); S(ch+1, commit, lines, false);
        P(L'', 0);
        P(L'', RIGHT_EDGE);
        P(L'', LEFT_EDGE);
        P(L'', LEFT_EDGE | RIGHT_EDGE);
        P(L'', BOTTOM_EDGE);
        P(L'', TOP_EDGE);
        P(L'', BOTTOM_EDGE | TOP_EDGE);
        P(L'', RIGHT_EDGE | BOTTOM_EDGE);
        P(L'', LEFT_EDGE | BOTTOM_EDGE);
        P(L'', RIGHT_EDGE | TOP_EDGE);
        P(L'', LEFT_EDGE | TOP_EDGE);
        P(L'', TOP_EDGE | BOTTOM_EDGE | RIGHT_EDGE);
        P(L'', TOP_EDGE | BOTTOM_EDGE | LEFT_EDGE);
        P(L'', LEFT_EDGE | RIGHT_EDGE | BOTTOM_EDGE);
        P(L'', LEFT_EDGE | RIGHT_EDGE | TOP_EDGE);
        P(L'', LEFT_EDGE | RIGHT_EDGE | TOP_EDGE | BOTTOM_EDGE);
#undef P
#define Q(ch, which) C(ch, corner, t, t, which); C(ch + 1, corner, f, t, which); C(ch + 2, corner, t, f, which); C(ch + 3, corner, f, f, which);
        Q(L'┌', BOTTOM_RIGHT); Q(L'┐', BOTTOM_LEFT); Q(L'└', TOP_RIGHT); Q(L'┘', TOP_LEFT);
#undef Q
        C(L'╭', rounded_corner, 1, TOP_LEFT);
        C(L'╮', rounded_corner, 1, TOP_RIGHT);
        C(L'╰', rounded_corner, 1, BOTTOM_LEFT);
        C(L'╯', rounded_corner, 1, BOTTOM_RIGHT);

        case L'┼' ... L'┼' + 15: cross(c, ch - L'┼'); break;
#define T(q, func) case q ... q + 7: func(c, q, ch - q); break;
        T(L'├', vert_t); T(L'┤', vert_t);
        T(L'┬', horz_t); T(L'┴', horz_t);
#undef T
        C(L'╒', dvcorner, 1, TOP_LEFT);
        C(L'╕', dvcorner, 1, TOP_RIGHT);
        C(L'╘', dvcorner, 1, BOTTOM_LEFT);
        C(L'╛', dvcorner, 1, BOTTOM_RIGHT);
        C(L'╓', dhcorner, 1, TOP_LEFT);
        C(L'╖', dhcorner, 1, TOP_RIGHT);
        C(L'╙', dhcorner, 1, BOTTOM_LEFT);
        C(L'╜', dhcorner, 1, BOTTOM_RIGHT);
        C(L'╔', dcorner, 1, TOP_LEFT);
        C(L'╗', dcorner, 1, TOP_RIGHT);
        C(L'╚', dcorner, 1, BOTTOM_LEFT);
        C(L'╝', dcorner, 1, BOTTOM_RIGHT);
        C(L'╟', dpip, 1, RIGHT_EDGE);
        C(L'╢', dpip, 1, LEFT_EDGE);
        C(L'╤', dpip, 1, BOTTOM_EDGE);
        C(L'╧', dpip, 1, TOP_EDGE);

        case 0x2800 ... 0x2800 + 255: braille(c, ch - 0x2800); break;
        case 0x1fb00 ... 0x1fb00 + 19: sextant(c, ch - 0x1fb00 + 1); break;
        case 0x1fb14 ... 0x1fb14 + 19: sextant(c, ch - 0x1fb00 + 2); break;
        case 0x1fb28 ... 0x1fb28 + 19: sextant(c, ch - 0x1fb00 + 3); break;
        case 0x1fb70 ... 0x1fb70 + 5: eight_bar(c, ch - 0x1fb6f, false); break;
        case 0x1fb76 ... 0x1fb76 + 5: eight_bar(c, ch - 0x1fb75, true); break;
        case 0x1fbe6: octant(c, 0xe6); break;
        case 0x1fbe7: octant(c, 0xe7); break;
        case 0x1cd00 ... 0x1cde5: octant(c, ch - 0x1cd00); break;
    }
    free(canvas.holes); free(canvas.y_limits);
    free(ss.holes); free(ss.y_limits);
END_ALLOW_CASE_RANGE
#undef CC
#undef SS
#undef C
#undef S
#undef SB
#undef t
#undef f
}
