From 99a3b034c33bd88822fb802ee12324eb262ce172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 5 May 2021 20:44:46 +0200 Subject: [PATCH] box-drawing: apply antialiasing to LIGHT ARCs When drawing on an a8 buffer, apply antialiasing to LIGHT ARCs. This is done by supersampling; draw to a 4 times bigger buffer, then downsample, where each downsampled pixel is the average of the corresponding 4x4 pixel from the supersampled buffer. We also need to take supersampling into account while adjusting the row and col when pixel aligning the arcs with the regular horizontal/vertical lines. The shape of the ARCs can still be improved. Still, this is a much needed improvement over the current implementation. Closes #279 --- box-drawing.c | 113 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 73 insertions(+), 40 deletions(-) diff --git a/box-drawing.c b/box-drawing.c index dfb868e8..05fad40b 100644 --- a/box-drawing.c +++ b/box-drawing.c @@ -1242,25 +1242,28 @@ draw_box_drawings_double_vertical_and_horizontal(struct buf *buf) static void draw_box_drawings_light_arc(wchar_t wc, struct buf *buf) { - if (pixman_image_get_format(buf->pix) == PIXMAN_a8) { - /* - * Hack until we’ve implemented ARCs with pixman - * - * Replace the a8 buffer with a a1 buffer, and use our “old” - * non-antialiased technique to draw arcs. - */ - change_buffer_format(buf, PIXMAN_a1); - } + const pixman_format_code_t fmt = pixman_image_get_format(buf->pix); + const int supersample = fmt == PIXMAN_a8 ? 4 : 1; + const int height = buf->height * supersample; + const int width = buf->width * supersample; + const int stride = fmt == PIXMAN_a8 + ? stride_for_format_and_width(PIXMAN_a8, width) : buf->stride; + uint8_t *data = supersample > 1 ? xcalloc(height * stride, 1) : buf->data; - int thick = thickness(LIGHT); + const int thick = thickness(LIGHT) * supersample; - double a = (buf->width - thick) / 2; - double b = (buf->height - thick) / 2; + const bool thick_is_odd = (thick / supersample) % 2; + const bool height_is_odd = buf->height % 2; + const bool width_is_odd = buf->width % 2; - double a2 = a * a; - double b2 = b * b; + const double a = (width - thick) / 2; + const double b = (height - thick) / 2; + + const double a2 = a * a; + const double b2 = b * b; + + const int num_samples = height * 16; - int num_samples = buf->height * 16; for (int i = 0; i < num_samples; i++) { double y = i / 16.; double x = sqrt(a2 * (1. - y * y / b2)); @@ -1310,30 +1313,30 @@ draw_box_drawings_light_arc(wchar_t wc, struct buf *buf) */ switch (wc) { case L'╭': - row_end = buf->height - row - (thick % 2 ^ buf->height % 2); + row_end = height - row - (thick_is_odd ^ height_is_odd); row_start = row_end - thick; - col_end = buf->width - col - (thick % 2 ^ buf->width % 2); + col_end = width - col - (thick_is_odd ^ width_is_odd); col_start = col_end - thick; break; case L'╮': - row_end = buf->height - row - (thick % 2 ^ buf->height % 2); + row_end = height - row - (thick_is_odd ^ height_is_odd); row_start = row_end - thick; - col_start = col; + col_start = col - ((thick_is_odd ^ width_is_odd) ? supersample / 2 : 0); col_end = col_start + thick; break; case L'╰': - row_start = row; + row_start = row - ((thick_is_odd ^ height_is_odd) ? supersample / 2 : 0); row_end = row_start + thick; - col_end = buf->width - col - (thick % 2 ^ buf->width % 2); + col_end = width - col - (thick_is_odd ^ width_is_odd); col_start = col_end - thick; break; case L'╯': - row_start = row; + row_start = row - ((thick_is_odd ^ height_is_odd) ? supersample / 2 : 0); row_end = row_start + thick; - col_start = col; + col_start = col - ((thick_is_odd ^ width_is_odd) ? supersample / 2 : 0); col_end = col_start + thick; break; } @@ -1341,11 +1344,14 @@ draw_box_drawings_light_arc(wchar_t wc, struct buf *buf) xassert(row_end > row_start); xassert(col_end > col_start); - for (int r = max(row_start, 0); r < max(min(row_end, buf->height), 0); r++) { - for (int c = max(col_start, 0); c < max(min(col_end, buf->width), 0); c++) { - size_t idx = c / 8; - size_t bit_no = c % 8; - buf->data[r * buf->stride + idx] |= 1 << bit_no; + for (int r = max(row_start, 0); r < max(min(row_end, height), 0); r++) { + for (int c = max(col_start, 0); c < max(min(col_end, width), 0); c++) { + if (fmt == PIXMAN_a1) { + size_t idx = c / 8; + size_t bit_no = c % 8; + data[r * stride + idx] |= 1 << bit_no; + } else + data[r * stride + c] = 0xff; } } } @@ -1359,27 +1365,54 @@ draw_box_drawings_light_arc(wchar_t wc, struct buf *buf) if (wc == L'╭' || wc == L'╰') { for (int y = 0; y < thick; y++) { - int row = (buf->height - thick) / 2 + y; - int col = buf->width - 1; - if (row >= 0 && row < buf->height && col >= 0 && col < buf->width) { - size_t ofs = col / 8; - size_t bit_no = col % 8; - buf->data[row * buf->stride + ofs] |= 1 << bit_no; + int row = (height - thick) / 2 + y - ((thick_is_odd ^ height_is_odd) ? supersample / 2 : 0); + for (int col = width - supersample; col < width; col++) { + if (row >= 0 && row < height && col >= 0) { + if (fmt == PIXMAN_a1) { + size_t ofs = col / 8; + size_t bit_no = col % 8; + data[row * stride + ofs] |= 1 << bit_no; + } else + data[row * stride + col] = 0xff; + } } } } if (wc == L'╭' || wc == L'╮') { for (int x = 0; x < thick; x++) { - int row = buf->height - 1; - int col = (buf->width - thick) / 2 + x; - if (row >= 0 && row < buf->height && col >= 0 && col < buf->width) { - size_t ofs = col / 8; - size_t bit_no = col % 8; - buf->data[row * buf->stride + ofs] |= 1 << bit_no; + int col = (width - thick) / 2 + x - ((thick_is_odd ^ width_is_odd) ? supersample / 2 : 0); + for (int row = height - supersample; row < height; row++) { + if (row >= 0 && col >= 0 && col < width) { + if (fmt == PIXMAN_a1) { + size_t ofs = col / 8; + size_t bit_no = col % 8; + data[row * stride + ofs] |= 1 << bit_no; + } else + data[row * stride + col] = 0xff; + } } } } + + if (fmt == PIXMAN_a8) { + xassert(data != buf->data); + + /* Downsample */ + for (size_t r = 0; r < buf->height; r++) { + for (size_t c = 0; c < buf->width; c++) { + uint32_t total = 0; + for (size_t i = 0; i < supersample; i++) { + for (size_t j = 0; j < supersample; j++) + total += data[(r * supersample + i) * stride + c * supersample + j]; + } + uint8_t average = min(total / (supersample * supersample), 0xff); + buf->data[r * buf->stride + c] = average; + } + } + + free(data); + } } static void