From 8298c2f93b0dbe39eae66455c80e68e812213a2f Mon Sep 17 00:00:00 2001 From: Hans Petter Jansson Date: Fri, 23 Apr 2021 18:37:58 +0200 Subject: [PATCH] [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. --- configure.ac | 19 +++++++ meson.build | 7 +++ meson_options.txt | 2 + util/Makefile.am | 2 + util/helper-cairo-ansi.cc | 104 +++++++++++++++++++++++++++++++++++++- util/meson.build | 2 +- 6 files changed, 133 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 2ef6cc656..0406690c2 100644 --- a/configure.ac +++ b/configure.ac @@ -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} diff --git a/meson.build b/meson.build index 09320826a..b20bed51d 100644 --- a/meson.build +++ b/meson.build @@ -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, diff --git a/meson_options.txt b/meson_options.txt index 700407689..602d053b7 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -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', diff --git a/util/Makefile.am b/util/Makefile.am index 29f690948..cabb9eff5 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -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 diff --git a/util/helper-cairo-ansi.cc b/util/helper-cairo-ansi.cc index cbda4939e..8f105c99f 100644 --- a/util/helper-cairo-ansi.cc +++ b/util/helper-cairo-ansi.cc @@ -26,9 +26,102 @@ #include "helper-cairo-ansi.hh" #include "options.hh" - #include "ansi-print.hh" +#ifdef HAVE_CHAFA +# include + +/* 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) - ansi_print_image_rgb24 (data, width, height, stride / 4); + { +#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; diff --git a/util/meson.build b/util/meson.build index 3c10d5dfe..5aa7585e8 100644 --- a/util/meson.build +++ b/util/meson.build @@ -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, )