mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-13 02:42:09 +02:00
Ascii image output using chafa
This commit is contained in:
@@ -31,6 +31,7 @@ OPTION(ENABLE_RPM "Enable rpm" ON)
|
||||
OPTION(ENABLE_IMAGEMAGICK7 "Enable imagemagick 7" ON)
|
||||
OPTION(ENABLE_IMAGEMAGICK6 "Enable imagemagick 6" ON)
|
||||
OPTION(ENABLE_ZLIB "Enable zlib" ON)
|
||||
OPTION(ENABLE_CHAFA "Enable chafa" ON)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
@@ -321,6 +322,15 @@ if(ENABLE_ZLIB)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_CHAFA)
|
||||
pkg_check_modules(CHAFA chafa)
|
||||
if(CHAFA_FOUND)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_CHAFA=1)
|
||||
else()
|
||||
message(WARNING "Package chafa not found. Building without support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_include_directories(libfastfetch
|
||||
PUBLIC ${PROJECT_BINARY_DIR}
|
||||
PUBLIC ${PROJECT_SOURCE_DIR}/src
|
||||
@@ -340,6 +350,7 @@ target_include_directories(libfastfetch
|
||||
PRIVATE ${IMAGEMAGICK7_INCLUDE_DIRS}
|
||||
PRIVATE ${IMAGEMAGICK6_INCLUDE_DIRS}
|
||||
PRIVATE ${ZLIB_INCLUDE_DIRS}
|
||||
PRIVATE ${CHAFA_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(libfastfetch
|
||||
|
||||
@@ -29,6 +29,7 @@ The following libraries are used if present at runtime:
|
||||
* [`libGIO`](https://developer.gnome.org/gio/unstable/): Needed for values that are only stored GSettings.
|
||||
* [`libDConf`](https://developer.gnome.org/dconf/unstable/): Needed for values that are only stored in DConf + Fallback for GSettings.
|
||||
* [`libmagickcore` (ImageMagick)](https://www.imagemagick.org/): Images in terminal using sixel or kitty graphics protocol.
|
||||
* ['libchafa`](https://github.com/hpjansson/chafa): Image output as ascii art.
|
||||
* [`libZ`](https://www.zlib.net/): Faster image output when using kitty graphics protocol.
|
||||
* [`libDBus`](https://www.freedesktop.org/wiki/Software/dbus): Needed for detecting current media player and song.
|
||||
* [`libXFConf`](https://gitlab.xfce.org/xfce/xfconf): Needed for XFWM theme and XFCE Terminal font.
|
||||
|
||||
@@ -234,6 +234,7 @@ __fastfetch_completion()
|
||||
"--lib-RPM"
|
||||
"--lib-imagemagick"
|
||||
"--lib-z"
|
||||
"--lib-chafa"
|
||||
"--battery-dir"
|
||||
"--load-config"
|
||||
)
|
||||
|
||||
@@ -201,6 +201,7 @@ static void defaultConfig(FFinstance* instance)
|
||||
ffStrbufInitA(&instance->config.librpm, 0);
|
||||
ffStrbufInitA(&instance->config.libImageMagick, 0);
|
||||
ffStrbufInitA(&instance->config.libZ, 0);
|
||||
ffStrbufInitA(&instance->config.libChafa, 0);
|
||||
|
||||
ffStrbufInitA(&instance->config.diskFolders, 0);
|
||||
|
||||
|
||||
+2
-1
@@ -61,7 +61,7 @@
|
||||
|
||||
# Logo type option:
|
||||
# Sets the logo type to use.
|
||||
# Must be auto, builtin, file, raw, sixel or kitty.
|
||||
# Must be auto, builtin, file, raw, sixel, kitty or chafa.
|
||||
# Default is auto.
|
||||
#--logo-type auto
|
||||
|
||||
@@ -230,3 +230,4 @@
|
||||
#--lib-rpm /usr/lib/librpm.so
|
||||
#--lib-imagemagick /usr/lib/libMagickCore-7.Q16HDRI.so
|
||||
#--lib-z /usr/lib/libz.so
|
||||
#--lib-chafa /usr/lib/libchafa.so
|
||||
|
||||
+3
-1
@@ -21,7 +21,7 @@ General options:
|
||||
|
||||
Logo options:
|
||||
-l,--logo <logo>: set the logo to use. The type is specified by --logo-type. If default: the name of a builtin logo or a path to a file
|
||||
--logo-type <type>: set the type of the logo given. Must be auto, builtin, file, raw, sixel or kitty.
|
||||
--logo-type <type>: set the type of the logo given. Must be auto, builtin, file, raw, sixel, kitty or chafa.
|
||||
--logo-width <width>: set the width of the logo (in characters), if it is an image
|
||||
--logo-color-[1-9] <color>: overwrite a color in the logo
|
||||
--logo-padding <padding>: set the padding on the left and the right of the logo
|
||||
@@ -30,6 +30,7 @@ Logo options:
|
||||
--logo-print-remaining <?value>: weather to print the remaining logo, if it has more lines than modules to display
|
||||
--sixel <file>: short for --logo-type sixel --logo <file>
|
||||
--kitty <file>: short for --logo-type kitty --logo <file>
|
||||
--chafa <file>: short for --logo-type chafa --logo <file>
|
||||
--file <file>: short for --logo-type file --logo <file>
|
||||
--raw <file>: short for --logo-type raw --logo <file>
|
||||
|
||||
@@ -123,6 +124,7 @@ Library options: Set the path of a library to load
|
||||
--lib-RPM <path>
|
||||
--lib-imagemagick <path>
|
||||
--lib-z <path>
|
||||
--lib-chafa <path>
|
||||
|
||||
Module specific options:
|
||||
--separator-string <str>: Set the string printed by the separator module
|
||||
|
||||
@@ -735,6 +735,8 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con
|
||||
instance->config.logoType = FF_LOGO_TYPE_SIXEL;
|
||||
else if(strcasecmp(value, "kitty") == 0)
|
||||
instance->config.logoType = FF_LOGO_TYPE_KITTY;
|
||||
else if(strcasecmp(value, "chafa") == 0)
|
||||
instance->config.logoType = FF_LOGO_TYPE_CHAFA;
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Error: unknown logo type: %s\n", value);
|
||||
@@ -789,6 +791,11 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con
|
||||
optionParseString(key, value, &instance->config.logoSource);
|
||||
instance->config.logoType = FF_LOGO_TYPE_RAW;
|
||||
}
|
||||
else if(strcasecmp(key, "--chafa") == 0)
|
||||
{
|
||||
optionParseString(key, value, &instance->config.logoSource);
|
||||
instance->config.logoType = FF_LOGO_TYPE_CHAFA;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
//Display options//
|
||||
@@ -988,6 +995,8 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con
|
||||
optionParseString(key, value, &instance->config.libImageMagick);
|
||||
else if(strcasecmp(key, "--lib-z") == 0)
|
||||
optionParseString(key, value, &instance->config.libZ);
|
||||
else if(strcasecmp(key, "--lib-chafa") == 0)
|
||||
optionParseString(key, value, &instance->config.libChafa);
|
||||
|
||||
//////////////////
|
||||
//Module options//
|
||||
|
||||
+3
-1
@@ -38,7 +38,8 @@ typedef enum FFLogoType
|
||||
FF_LOGO_TYPE_FILE, //Raw text file, printed as is.
|
||||
FF_LOGO_TYPE_RAW, //Raw text file, printed with color codes replacement.
|
||||
FF_LOGO_TYPE_SIXEL, //Image file, printed as sixel codes.
|
||||
FF_LOGO_TYPE_KITTY //Image file, printed as kitty graphics protocol
|
||||
FF_LOGO_TYPE_KITTY, //Image file, printed as kitty graphics protocol
|
||||
FF_LOGO_TYPE_CHAFA //Image file, printed as ascii art using libchafa
|
||||
} FFLogoType;
|
||||
|
||||
typedef struct FFconfig
|
||||
@@ -138,6 +139,7 @@ typedef struct FFconfig
|
||||
FFstrbuf librpm;
|
||||
FFstrbuf libImageMagick;
|
||||
FFstrbuf libZ;
|
||||
FFstrbuf libChafa;
|
||||
|
||||
FFstrbuf diskFolders;
|
||||
|
||||
|
||||
+100
-9
@@ -8,6 +8,7 @@
|
||||
#define FF_CACHE_FILE_SIXEL "sixel"
|
||||
#define FF_CACHE_FILE_KITTY_COMPRESSED "kittyc"
|
||||
#define FF_CACHE_FILE_KITTY_UNCOMPRESSED "kittyu"
|
||||
#define FF_CACHE_FILE_CHAFA "chafa"
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@@ -88,6 +89,10 @@ static void finishPrinting(FFinstance* instance, const FFLogoRequestData* reques
|
||||
if(requestData->type == FF_LOGO_TYPE_SIXEL)
|
||||
instance->state.logoHeight += 1;
|
||||
|
||||
//Chafa ascii images don't have a newline at the end
|
||||
if(requestData->type == FF_LOGO_TYPE_CHAFA)
|
||||
instance->state.logoHeight -= 1;
|
||||
|
||||
fputs("\033[9999999D", stdout);
|
||||
printf("\033[%uA", instance->state.logoHeight);
|
||||
}
|
||||
@@ -193,6 +198,66 @@ static bool printImageKitty(FFinstance* instance, FFLogoRequestData* requestData
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef FF_HAVE_CHAFA
|
||||
#include <chafa/chafa.h>
|
||||
static bool printImageChafa(FFinstance* instance, FFLogoRequestData* requestData, const ImageData* imageData)
|
||||
{
|
||||
FF_LIBRARY_LOAD(chafa, instance->config.libChafa, false, "libchafa.so", 1)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_symbol_map_new, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_symbol_map_add_by_tags, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_new, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_set_geometry, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_set_symbol_map, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_new, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_draw_all_pixels, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_print, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_unref, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_unref, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_symbol_map_unref, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL(chafa, g_string_free, false)
|
||||
|
||||
imageData->ffCopyMagickString(imageData->imageInfo->magick, "RGBA", 5);
|
||||
size_t length;
|
||||
void* blob = imageData->ffImageToBlob(imageData->imageInfo, imageData->image, &length, imageData->exceptionInfo);
|
||||
if(!checkAllocationResult(blob, length))
|
||||
return false;
|
||||
|
||||
ChafaSymbolMap* symbolMap = ffchafa_symbol_map_new();
|
||||
ffchafa_symbol_map_add_by_tags(symbolMap, CHAFA_SYMBOL_TAG_ALL);
|
||||
|
||||
//imagePixelHeight is calculated, as if the image was already resized
|
||||
gint logoCharacterHeight = (gint) (requestData->imagePixelHeight / requestData->characterPixelHeight);
|
||||
|
||||
ChafaCanvasConfig* canvasConfig = ffchafa_canvas_config_new();
|
||||
ffchafa_canvas_config_set_geometry(canvasConfig, (gint) instance->config.logoWidth, logoCharacterHeight);
|
||||
ffchafa_canvas_config_set_symbol_map(canvasConfig, symbolMap);
|
||||
|
||||
ChafaCanvas* canvas = ffchafa_canvas_new(canvasConfig);
|
||||
ffchafa_canvas_draw_all_pixels(
|
||||
canvas,
|
||||
CHAFA_PIXEL_RGBA8_UNASSOCIATED,
|
||||
blob,
|
||||
(gint) imageData->image->columns,
|
||||
(gint) imageData->image->rows,
|
||||
(gint) imageData->image->columns * 4
|
||||
);
|
||||
|
||||
GString* str = ffchafa_canvas_print(canvas, NULL);
|
||||
|
||||
FFstrbuf result;
|
||||
result.chars = str->str;
|
||||
result.length = (uint32_t) str->len;
|
||||
writeResult(instance, requestData, &result, FF_CACHE_FILE_CHAFA);
|
||||
|
||||
ffg_string_free(str, TRUE);
|
||||
ffchafa_canvas_unref(canvas);
|
||||
ffchafa_canvas_config_unref(canvasConfig);
|
||||
ffchafa_symbol_map_unref(symbolMap);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData* requestData, const FFIMData* imData)
|
||||
{
|
||||
FF_LIBRARY_LOAD_SYMBOL(imData->library, AcquireExceptionInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR)
|
||||
@@ -239,12 +304,20 @@ FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData*
|
||||
return FF_LOGO_IMAGE_RESULT_RUN_ERROR;
|
||||
}
|
||||
|
||||
imageData.image = (Image*) imData->resizeFunc(originalImage, requestData->imagePixelWidth, requestData->imagePixelHeight, imageData.exceptionInfo);
|
||||
ffDestroyImage(originalImage);
|
||||
if(imageData.image == NULL)
|
||||
if(instance->config.logoType != FF_LOGO_TYPE_CHAFA)
|
||||
{
|
||||
ffDestroyExceptionInfo(imageData.exceptionInfo);
|
||||
return FF_LOGO_IMAGE_RESULT_RUN_ERROR;
|
||||
imageData.image = (Image*) imData->resizeFunc(originalImage, requestData->imagePixelWidth, requestData->imagePixelHeight, imageData.exceptionInfo);
|
||||
ffDestroyImage(originalImage);
|
||||
if(imageData.image == NULL)
|
||||
{
|
||||
ffDestroyExceptionInfo(imageData.exceptionInfo);
|
||||
return FF_LOGO_IMAGE_RESULT_RUN_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//We don't want to resize the image for chafa, because chafa does that while converting to ASCII.
|
||||
imageData.image = originalImage;
|
||||
}
|
||||
|
||||
imageData.imageInfo = ffAcquireImageInfo();
|
||||
@@ -256,10 +329,18 @@ FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData*
|
||||
}
|
||||
|
||||
bool printSuccessful;
|
||||
if(requestData->type == FF_LOGO_TYPE_KITTY)
|
||||
printSuccessful = printImageKitty(instance, requestData, &imageData);
|
||||
else
|
||||
if(requestData->type == FF_LOGO_TYPE_SIXEL)
|
||||
printSuccessful = printImageSixel(instance, requestData, &imageData);
|
||||
else if(requestData->type == FF_LOGO_TYPE_KITTY)
|
||||
printSuccessful = printImageKitty(instance, requestData, &imageData);
|
||||
else if(requestData->type == FF_LOGO_TYPE_CHAFA)
|
||||
#ifdef FF_HAVE_CHAFA
|
||||
printSuccessful = printImageChafa(instance, requestData, &imageData);
|
||||
#else //should never happen at this point
|
||||
printSuccessful = false;
|
||||
#endif
|
||||
else //should never happen at this point
|
||||
printSuccessful = false;
|
||||
|
||||
ffDestroyImageInfo(imageData.imageInfo);
|
||||
ffDestroyImage(imageData.image);
|
||||
@@ -302,8 +383,12 @@ static bool printCached(FFinstance* instance, FFLogoRequestData* requestData)
|
||||
if(fd == -1)
|
||||
fd = getCacheFD(requestData, FF_CACHE_FILE_KITTY_UNCOMPRESSED);
|
||||
}
|
||||
else
|
||||
else if(requestData->type == FF_LOGO_TYPE_SIXEL)
|
||||
fd = getCacheFD(requestData, FF_CACHE_FILE_SIXEL);
|
||||
else if(requestData->type == FF_LOGO_TYPE_CHAFA)
|
||||
fd = getCacheFD(requestData, FF_CACHE_FILE_CHAFA);
|
||||
else
|
||||
fd = -1; //should never happen at this point
|
||||
|
||||
if(fd == -1)
|
||||
return false;
|
||||
@@ -344,6 +429,12 @@ static bool getTermInfo(struct winsize* winsize)
|
||||
|
||||
bool ffLogoPrintImageIfExists(FFinstance* instance, FFLogoType type)
|
||||
{
|
||||
//Performance optimization
|
||||
#ifndef FF_HAVE_CHAFA
|
||||
if(type == FF_LOGO_TYPE_CHAFA)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
FFLogoRequestData requestData;
|
||||
|
||||
if(!getTermInfo(&requestData.winsize))
|
||||
|
||||
+6
-3
@@ -162,7 +162,8 @@ static void logoPrintDetected(FFinstance* instance)
|
||||
{
|
||||
if(
|
||||
!ffLogoPrintBuiltinIfExists(instance) &&
|
||||
!ffLogoPrintImageIfExists(instance, FF_LOGO_TYPE_KITTY)
|
||||
!ffLogoPrintImageIfExists(instance, FF_LOGO_TYPE_KITTY) &&
|
||||
!ffLogoPrintImageIfExists(instance, FF_LOGO_TYPE_CHAFA)
|
||||
) logoPrintFile(instance, true);
|
||||
return;
|
||||
}
|
||||
@@ -179,7 +180,9 @@ void ffPrintLogo(FFinstance* instance)
|
||||
instance->config.logoType == FF_LOGO_TYPE_BUILTIN ||
|
||||
instance->config.logoType == FF_LOGO_TYPE_FILE ||
|
||||
instance->config.logoType == FF_LOGO_TYPE_RAW ||
|
||||
instance->config.logoType == FF_LOGO_TYPE_SIXEL
|
||||
instance->config.logoType == FF_LOGO_TYPE_SIXEL ||
|
||||
instance->config.logoType == FF_LOGO_TYPE_KITTY ||
|
||||
instance->config.logoType == FF_LOGO_TYPE_CHAFA
|
||||
) && instance->config.logoSource.length == 0)
|
||||
ffLogoPrintUnknown(instance);
|
||||
else if(instance->config.logoType == FF_LOGO_TYPE_BUILTIN)
|
||||
@@ -191,7 +194,7 @@ void ffPrintLogo(FFinstance* instance)
|
||||
logoPrintFile(instance, true);
|
||||
else if(instance->config.logoType == FF_LOGO_TYPE_RAW)
|
||||
logoPrintFile(instance, false);
|
||||
else if(instance->config.logoType == FF_LOGO_TYPE_SIXEL || instance->config.logoType == FF_LOGO_TYPE_KITTY)
|
||||
else if(instance->config.logoType == FF_LOGO_TYPE_SIXEL || instance->config.logoType == FF_LOGO_TYPE_KITTY || instance->config.logoType == FF_LOGO_TYPE_CHAFA)
|
||||
{
|
||||
if(!ffLogoPrintImageIfExists(instance, instance->config.logoType))
|
||||
ffLogoPrintBuiltinDetected(instance);
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ void ffLogoPrintUnknown(FFinstance* instance);
|
||||
bool ffLogoPrintBuiltinIfExists(FFinstance* instance);
|
||||
void ffLogoPrintBuiltinDetected(FFinstance* instance);
|
||||
|
||||
//sixel/image.c
|
||||
//image/image.c
|
||||
bool ffLogoPrintImageIfExists(FFinstance* instance, FFLogoType type);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user