[hb-view] Use Chafa for terminal graphics if available

This produces high-quality terminal graphics using symbols or sixels
according to the detected terminal capabilities.

Fixes #2430.
This commit is contained in:
Hans Petter Jansson 2021-04-23 18:37:58 +02:00 committed by Behdad Esfahbod
parent 425ba1f4ab
commit 8298c2f93b
6 changed files with 133 additions and 3 deletions

View File

@ -214,6 +214,24 @@ AM_CONDITIONAL(HAVE_CAIRO_FT, $have_cairo_ft)
dnl ==========================================================================
AC_ARG_WITH(chafa,
[AS_HELP_STRING([--with-chafa=@<:@yes/no/auto@:>@],
[Use chafa @<:@default=auto@:>@])],,
[with_chafa=auto])
have_chafa=false
if test "x$with_chafa" = "xyes" -o "x$with_chafa" = "xauto"; then
PKG_CHECK_MODULES(CHAFA, chafa >= 1.6.0, have_chafa=true, :)
fi
if test "x$with_chafa" = "xyes" -a "x$have_chafa" != "xtrue"; then
AC_MSG_ERROR([chafa support requested but not found])
fi
if $have_chafa; then
AC_DEFINE(HAVE_CHAFA, 1, [Have chafa terminal graphics library])
fi
AM_CONDITIONAL(HAVE_CHAFA, $have_chafa)
dnl ==========================================================================
AC_ARG_WITH(icu,
[AS_HELP_STRING([--with-icu=@<:@yes/no/builtin/auto@:>@],
[Use ICU @<:@default=auto@:>@])],,
@ -448,6 +466,7 @@ Font callbacks (the more the merrier):
Tools used for command-line utilities:
Cairo: ${have_cairo}
Chafa: ${have_chafa}
Additional shapers:
Graphite2: ${have_graphite2}

View File

@ -154,6 +154,8 @@ if not get_option('cairo').disabled()
endif
endif
chafa_dep = dependency('chafa', version: '>= 1.6.0', required: get_option('chafa'))
conf = configuration_data()
incconfig = include_directories('.')
@ -181,6 +183,10 @@ if cairo_ft_dep.found()
conf.set('HAVE_CAIRO_FT', 1)
endif
if chafa_dep.found()
conf.set('HAVE_CHAFA', 1)
endif
if graphite2_dep.found()
conf.set('HAVE_GRAPHITE2', 1)
endif
@ -363,6 +369,7 @@ build_summary = {
},
'Dependencies used for command-line utilities':
{'Cairo': conf.get('HAVE_CAIRO', 0) == 1,
'Chafa': conf.get('HAVE_CHAFA', 0) == 1,
},
'Additional shapers':
{'Graphite2': conf.get('HAVE_GRAPHITE2', 0) == 1,

View File

@ -5,6 +5,8 @@ option('gobject', type: 'feature', value: 'auto',
description: 'Enable GObject bindings')
option('cairo', type: 'feature', value: 'auto',
description: 'Use Cairo graphics library')
option('chafa', type: 'feature', value: 'auto',
description: 'Use Chafa terminal graphics library')
option('icu', type: 'feature', value: 'auto',
description: 'Enable ICU library unicode functions')
option('graphite', type: 'feature', value: 'disabled',

View File

@ -25,6 +25,7 @@ AM_CPPFLAGS = \
$(GLIB_CFLAGS) \
$(FREETYPE_CFLAGS) \
$(CAIRO_FT_CFLAGS) \
$(CHAFA_CFLAGS) \
$(NULL)
LDADD = \
$(top_builddir)/src/libharfbuzz.la \
@ -42,6 +43,7 @@ hb_view_LDADD = \
$(LDADD) \
$(CAIRO_LIBS) \
$(CAIRO_FT_LIBS) \
$(CHAFA_LIBS) \
$(NULL)
bin_PROGRAMS += hb-view
endif # HAVE_CAIRO_FT

View File

@ -26,9 +26,102 @@
#include "helper-cairo-ansi.hh"
#include "options.hh"
#include "ansi-print.hh"
#ifdef HAVE_CHAFA
# include <chafa.h>
/* Similar to ansi-print.cc */
# define CELL_W 8
# define CELL_H (2 * CELL_W)
static void
chafa_print_image_rgb24 (const void *data, int width, int height, int stride)
{
ChafaTermInfo *term_info;
ChafaSymbolMap *symbol_map;
ChafaCanvasConfig *config;
ChafaCanvas *canvas;
GString *gs;
unsigned int cols = (width + CELL_W - 1) / CELL_W;
unsigned int rows = (height + CELL_H - 1) / CELL_H;
gchar **environ;
ChafaCanvasMode mode;
ChafaPixelMode pixel_mode;
/* Adapt to terminal; use sixels if available, and fall back to symbols
* with as many colors as are supported */
environ = g_get_environ ();
term_info = chafa_term_db_detect (chafa_term_db_get_default (),
environ);
pixel_mode = CHAFA_PIXEL_MODE_SYMBOLS;
if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_BEGIN_SIXELS))
{
pixel_mode = CHAFA_PIXEL_MODE_SIXELS;
mode = CHAFA_CANVAS_MODE_TRUECOLOR;
}
else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_DIRECT))
mode = CHAFA_CANVAS_MODE_TRUECOLOR;
else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_256))
mode = CHAFA_CANVAS_MODE_INDEXED_240;
else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_16))
mode = CHAFA_CANVAS_MODE_INDEXED_16;
else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_INVERT_COLORS))
mode = CHAFA_CANVAS_MODE_FGBG_BGFG;
else
mode = CHAFA_CANVAS_MODE_FGBG;
/* Create the configuration */
symbol_map = chafa_symbol_map_new ();
chafa_symbol_map_add_by_tags (symbol_map,
(ChafaSymbolTags) (CHAFA_SYMBOL_TAG_BLOCK
| CHAFA_SYMBOL_TAG_SPACE));
config = chafa_canvas_config_new ();
chafa_canvas_config_set_canvas_mode (config, mode);
chafa_canvas_config_set_pixel_mode (config, pixel_mode);
chafa_canvas_config_set_cell_geometry (config, 10, 20);
chafa_canvas_config_set_geometry (config, cols, rows);
chafa_canvas_config_set_symbol_map (config, symbol_map);
chafa_canvas_config_set_color_extractor (config, CHAFA_COLOR_EXTRACTOR_MEDIAN);
chafa_canvas_config_set_work_factor (config, 1.0f);
/* Create canvas, draw to it and render output string */
canvas = chafa_canvas_new (config);
chafa_canvas_draw_all_pixels (canvas,
/* Cairo byte order is host native */
G_BYTE_ORDER == G_LITTLE_ENDIAN
? CHAFA_PIXEL_BGRA8_PREMULTIPLIED
: CHAFA_PIXEL_ARGB8_PREMULTIPLIED,
(const guint8 *) data,
width,
height,
stride);
gs = chafa_canvas_print (canvas, term_info);
/* Print the string */
fwrite (gs->str, sizeof (char), gs->len, stdout);
if (pixel_mode != CHAFA_PIXEL_MODE_SIXELS)
fputc ('\n', stdout);
/* Free resources */
g_string_free (gs, TRUE);
chafa_canvas_unref (canvas);
chafa_canvas_config_unref (config);
chafa_symbol_map_unref (symbol_map);
chafa_term_info_unref (term_info);
g_strfreev (environ);
}
#endif /* HAVE_CHAFA */
cairo_status_t
helper_cairo_surface_write_to_ansi_stream (cairo_surface_t *surface,
@ -95,7 +188,14 @@ helper_cairo_surface_write_to_ansi_stream (cairo_surface_t *surface,
height++; /* Add one last blank row for padding. */
if (width && height)
{
#ifdef HAVE_CHAFA
if (true)
chafa_print_image_rgb24 (data, width, height, stride);
else
#endif
ansi_print_image_rgb24 (data, width, height, stride / 4);
}
cairo_surface_destroy (surface);
return CAIRO_STATUS_SUCCESS;

View File

@ -31,7 +31,7 @@ if conf.get('HAVE_GLIB', 0) == 1
hb_view = executable('hb-view', hb_view_sources,
cpp_args: cpp_args,
include_directories: [incconfig, incsrc],
dependencies: util_deps,
dependencies: [util_deps, chafa_dep],
link_with: [libharfbuzz],
install: true,
)