From 5bceb29c79b66625046fba9eaa203677d8b43a51 Mon Sep 17 00:00:00 2001 From: Linus Dierheimer Date: Thu, 21 Apr 2022 15:50:45 +0200 Subject: [PATCH] Image caching --- src/common/io.c | 38 +++- src/fastfetch.h | 2 +- src/logo/image/im6.c | 16 +- src/logo/image/im7.c | 15 +- src/logo/image/image.c | 382 +++++++++++++++++++++++++++++------------ src/logo/image/image.h | 27 ++- 6 files changed, 344 insertions(+), 136 deletions(-) diff --git a/src/common/io.c b/src/common/io.c index 2075ad878..84a36c29a 100644 --- a/src/common/io.c +++ b/src/common/io.c @@ -12,15 +12,41 @@ bool ffWriteFDContent(int fd, const FFstrbuf* content) return write(fd, content->chars, content->length) != -1; } -void ffWriteFileContent(const char* fileName, const FFstrbuf* content) +static void createSubfolders(const char* fileName) { - int fd = open(fileName, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if(fd == -1) - return; + FFstrbuf path; + ffStrbufInit(&path); - ffWriteFDContent(fd, content); + while(*fileName != '\0') + { + ffStrbufAppendC(&path, *fileName); + if(*fileName == '/') + mkdir(path.chars, S_IRWXU | S_IRGRP | S_IROTH); + ++fileName; + } + + ffStrbufDestroy(&path); +} + +bool ffWriteFileContent(const char* fileName, const FFstrbuf* content) +{ + int openFlagsModes = O_WRONLY | O_CREAT | O_TRUNC; + int openFlagsRights = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + + int fd = open(fileName, openFlagsModes, openFlagsRights); + if(fd == -1) + { + createSubfolders(fileName); + fd = open(fileName, openFlagsModes, openFlagsRights); + if(fd == -1) + return false; + } + + bool ret = ffWriteFDContent(fd, content); close(fd); + + return ret; } void ffAppendFDContent(int fd, FFstrbuf* buffer) @@ -118,6 +144,8 @@ void ffGetTerminalResponse(const char* request, char end, const char* format, .. while((size_t) pos < sizeof(buffer) - 1) { char c = (char) getc(stdin); + if(c == '\0') + break; buffer[pos++] = c; if(c == end) break; diff --git a/src/fastfetch.h b/src/fastfetch.h index 32ad5f5a8..c0d4fd75c 100644 --- a/src/fastfetch.h +++ b/src/fastfetch.h @@ -400,7 +400,7 @@ void ffAppendFDContent(int fd, FFstrbuf* buffer); bool ffAppendFileContent(const char* fileName, FFstrbuf* buffer); //returns true if open() succeeds. This is used to differentiate between and bool ffGetFileContent(const char* fileName, FFstrbuf* buffer); bool ffWriteFDContent(int fd, const FFstrbuf* content); -void ffWriteFileContent(const char* fileName, const FFstrbuf* buffer); +bool ffWriteFileContent(const char* fileName, const FFstrbuf* buffer); bool ffFileExists(const char* fileName, mode_t mode); void ffSuppressIO(bool suppress); // Not thread safe! diff --git a/src/logo/image/im6.c b/src/logo/image/im6.c index e4f9d7e51..2f8421eb0 100644 --- a/src/logo/image/im6.c +++ b/src/logo/image/im6.c @@ -4,26 +4,22 @@ #include static FF_LIBRARY_SYMBOL(ResizeImage); -static FF_LIBRARY_SYMBOL(WriteImage); static void* logoResize(const void* image, size_t width, size_t height, void* exceptionInfo) { return ffResizeImage(image, width, height, UndefinedFilter, 1.0, exceptionInfo); } -static bool logoWrite(void* imageInfo, void* image, void* exceptionInfo) -{ - FF_UNUSED(exceptionInfo); - return ffWriteImage(imageInfo, image) == MagickTrue; -} - -FFLogoImageResult ffLogoPrintImageIM6(FFinstance* instance, FFLogoType type) +FFLogoImageResult ffLogoPrintImageIM6(FFinstance* instance, FFLogoRequestData* requestData) { FF_LIBRARY_LOAD(imageMagick, instance->config.libImageMagick, FF_LOGO_IMAGE_RESULT_INIT_ERROR, "libMagickCore-6.Q16HDRI.so", 8, "libMagickCore-6.Q16.so", 8) FF_LIBRARY_LOAD_SYMBOL_ADRESS(imageMagick, ffResizeImage, ResizeImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR); - FF_LIBRARY_LOAD_SYMBOL_ADRESS(imageMagick, ffWriteImage, WriteImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR); - FFLogoImageResult result = ffLogoPrintImageImpl(instance, imageMagick, logoResize, logoWrite, type); + FFIMData imData; + imData.resizeFunc = logoResize; + imData.library = imageMagick; + + FFLogoImageResult result = ffLogoPrintImageImpl(instance, requestData, &imData); dlclose(imageMagick); diff --git a/src/logo/image/im7.c b/src/logo/image/im7.c index 9225373de..7f76cfd59 100644 --- a/src/logo/image/im7.c +++ b/src/logo/image/im7.c @@ -4,25 +4,22 @@ #include static FF_LIBRARY_SYMBOL(ResizeImage); -static FF_LIBRARY_SYMBOL(WriteImage); static void* logoResize(const void* image, size_t width, size_t height, void* exceptionInfo) { return ffResizeImage(image, width, height, UndefinedFilter, exceptionInfo); } -static bool logoWrite(void* imageInfo, void* image, void* exceptionInfo) -{ - return ffWriteImage(imageInfo, image, exceptionInfo) == MagickTrue; -} - -FFLogoImageResult ffLogoPrintImageIM7(FFinstance* instance, FFLogoType type) +FFLogoImageResult ffLogoPrintImageIM7(FFinstance* instance, FFLogoRequestData* requestData) { FF_LIBRARY_LOAD(imageMagick, instance->config.libImageMagick, FF_LOGO_IMAGE_RESULT_INIT_ERROR, "libMagickCore-7.Q16HDRI.so", 11, "libMagickCore-7.Q16.so", 11) FF_LIBRARY_LOAD_SYMBOL_ADRESS(imageMagick, ffResizeImage, ResizeImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR); - FF_LIBRARY_LOAD_SYMBOL_ADRESS(imageMagick, ffWriteImage, WriteImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR); - FFLogoImageResult result = ffLogoPrintImageImpl(instance, imageMagick, logoResize, logoWrite, type); + FFIMData imData; + imData.resizeFunc = logoResize; + imData.library = imageMagick; + + FFLogoImageResult result = ffLogoPrintImageImpl(instance, requestData, &imData); dlclose(imageMagick); diff --git a/src/logo/image/image.c b/src/logo/image/image.c index c21ab8f9e..126a22945 100644 --- a/src/logo/image/image.c +++ b/src/logo/image/image.c @@ -4,29 +4,14 @@ #define FF_KITTY_MAX_CHUNK_SIZE 4096 -#include +#define FF_CACHE_FILE_HEIGHT "height" +#define FF_CACHE_FILE_SIXEL "sixel" +#define FF_CACHE_FILE_KITTY_COMPRESSED "kittyc" +#define FF_CACHE_FILE_KITTY_UNCOMPRESSED "kittyu" + +#include #include -#include // memset - -static bool getTermInfo(struct winsize* winsize) -{ - //Initialize every member to 0, because it isn't guaranteed that every terminal sets them all - memset(winsize, 0, sizeof(struct winsize)); - - ioctl(STDOUT_FILENO, TIOCGWINSZ, winsize); - - if(winsize->ws_row == 0 || winsize->ws_col == 0) - ffGetTerminalResponse("\033[18t", 't', "\033[8;%hu;%hut", &winsize->ws_row, &winsize->ws_col); - - if(winsize->ws_ypixel == 0 || winsize->ws_xpixel == 0) - ffGetTerminalResponse("\033[14t", 't', "\033[4;%hu;%hut", &winsize->ws_ypixel, &winsize->ws_xpixel); - - return - winsize->ws_row > 0 && - winsize->ws_col > 0 && - winsize->ws_ypixel > 0 && - winsize->ws_xpixel > 0; -} +#include #ifdef FF_HAVE_ZLIB #include @@ -69,14 +54,29 @@ static bool compressBlob(const FFinstance* instance, void** blob, size_t* length #include #endif -typedef struct ImageMagickFunctions +typedef struct ImageData { FF_LIBRARY_SYMBOL(CopyMagickString) FF_LIBRARY_SYMBOL(ImageToBlob) FF_LIBRARY_SYMBOL(Base64Encode) - FFLogoIMWriteFunc writeFunc; -} ImageMagickFunctions; + ImageInfo* imageInfo; + Image* image; + ExceptionInfo* exceptionInfo; +} ImageData; + +static void finishPrinting(FFinstance* instance, const FFLogoRequestData* requestData) +{ + instance->state.logoWidth = (uint32_t) (requestData->imagePixelWidth / requestData->characterPixelWidth) + instance->config.logoPaddingLeft + instance->config.logoPaddingRight; + instance->state.logoHeight = (uint32_t) (requestData->imagePixelHeight / requestData->characterPixelHeight); + + //Seems to be required on all consoles that support sixel. TBH i don't know why. + if(requestData->type == FF_LOGO_TYPE_SIXEL) + instance->state.logoHeight += 1; + + fputs("\033[9999999D", stdout); + printf("\033[%uA", instance->state.logoHeight); +} static inline bool checkAllocationResult(void* data, size_t length) { @@ -92,177 +92,347 @@ static inline bool checkAllocationResult(void* data, size_t length) return true; } -static void printImageKittyChunc(char** blob, size_t* length, uint32_t chunkSize) +static void writeCacheFile(FFLogoRequestData* requestData, const char* filename, char* blob, size_t length) { - size_t toWrite = chunkSize < *length ? chunkSize : *length; + uint32_t cacheDirLength = requestData->cacheDir.length; + ffStrbufAppendS(&requestData->cacheDir, filename); + + FFstrbuf cachedContent; + cachedContent.chars = blob; + cachedContent.length = (uint32_t) length; + ffWriteFileContent(requestData->cacheDir.chars, &cachedContent); + + ffStrbufSubstrBefore(&requestData->cacheDir, cacheDirLength); +} + + +static void printImageKittyBlobChunk(const char** blob, size_t* length) +{ + size_t toWrite = FF_KITTY_MAX_CHUNK_SIZE < *length ? FF_KITTY_MAX_CHUNK_SIZE : *length; fputs("\033_Gm=1;", stdout); fwrite(*blob, sizeof(**blob), toWrite, stdout); fputs("\033\\", stdout); - *blob = *blob + toWrite; + *blob += toWrite; *length -= toWrite; } -static bool printImageKitty(const FFinstance* instance, ImageInfo* imageInfo, Image* image, ExceptionInfo* ExceptionInfo, uint32_t paddingLeft, ImageMagickFunctions* functions) +static bool printImageKitty(const FFinstance* instance, FFLogoRequestData* requestData, const ImageData* imageData) { - functions->ffCopyMagickString(imageInfo->magick, "RGBA", 5); + imageData->ffCopyMagickString(imageData->imageInfo->magick, "RGBA", 5); size_t length; - void* blob = functions->ffImageToBlob(imageInfo, image, &length, ExceptionInfo); + void* blob = imageData->ffImageToBlob(imageData->imageInfo, imageData->image, &length, imageData->exceptionInfo); if(!checkAllocationResult(blob, length)) return false; #ifdef FF_HAVE_ZLIB bool isCompressed = compressBlob(instance, &blob, &length); #else - FF_UNUSED(instance) bool isCompressed = false; #endif - char* encoded = functions->ffBase64Encode(blob, length, &length); + char* chars = imageData->ffBase64Encode(blob, length, &length); free(blob); - if(!checkAllocationResult(encoded, length)) + if(!checkAllocationResult(chars, length)) return false; - ffPrintCharTimes(' ', paddingLeft); + ffPrintCharTimes(' ', instance->config.logoPaddingLeft); - char* currentPos = encoded; + const char* currentPos = chars; + size_t remainingLength = length; - printf("\033_Ga=T,f=32,s=%u,v=%u,m=1%s;\033\\", (uint32_t) image->columns, (uint32_t) image->rows, isCompressed ? ",o=z" : ""); - while (length > 0) - printImageKittyChunc(¤tPos, &length, FF_KITTY_MAX_CHUNK_SIZE); + printf("\033_Ga=T,f=32,s=%u,v=%u%s,m=1;\033\\", requestData->imagePixelWidth, requestData->imagePixelHeight, isCompressed ? ",o=z" : ""); + while(remainingLength > 0) + printImageKittyBlobChunk(¤tPos, &remainingLength); fputs("\033_Gm=0;\033\\", stdout); - free(encoded); + if(isCompressed) + writeCacheFile(requestData, FF_CACHE_FILE_KITTY_COMPRESSED, chars, length); + else + writeCacheFile(requestData, FF_CACHE_FILE_KITTY_UNCOMPRESSED, chars, length); + + free(chars); return true; } -static bool printImageSixel(ImageInfo* imageInfo, Image* image, ExceptionInfo* exceptionInfo, uint32_t paddingLeft, ImageMagickFunctions* functions) +static bool printImageSixel(const FFinstance* instance, FFLogoRequestData* requestData, const ImageData* imageData) { - ffPrintCharTimes(' ', paddingLeft); + imageData->ffCopyMagickString(imageData->imageInfo->magick, "SIXEL", 6); - functions->ffCopyMagickString(imageInfo->magick, "SIXEL", 6); - imageInfo->file = stdout; - bool writeResult = functions->writeFunc(imageInfo, image, exceptionInfo); + size_t length; + void* blob = imageData->ffImageToBlob(imageData->imageInfo, imageData->image, &length, imageData->exceptionInfo); + if(!checkAllocationResult(blob, length)) + return false; - if(!writeResult) - printf("\033[%uD", paddingLeft); + ffPrintCharTimes(' ', instance->config.logoPaddingLeft); + fwrite(blob, sizeof(*blob), length, stdout); - return writeResult; + writeCacheFile(requestData, FF_CACHE_FILE_SIXEL, blob, length); + + free(blob); + return true; } -FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, void* imageMagick, FFLogoIMResizeFunc resizeFunc, FFLogoIMWriteFunc writeFunc, FFLogoType type) +FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData* requestData, const FFIMData* imData) { - struct winsize winsize; - if(!getTermInfo(&winsize)) - return FF_LOGO_IMAGE_RESULT_RUN_ERROR; + FF_LIBRARY_LOAD_SYMBOL(imData->library, AcquireExceptionInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR) + FF_LIBRARY_LOAD_SYMBOL(imData->library, DestroyExceptionInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR) + FF_LIBRARY_LOAD_SYMBOL(imData->library, AcquireImageInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR) + FF_LIBRARY_LOAD_SYMBOL(imData->library, DestroyImageInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR) + FF_LIBRARY_LOAD_SYMBOL(imData->library, ReadImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR) + FF_LIBRARY_LOAD_SYMBOL(imData->library, DestroyImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR) - FF_LIBRARY_LOAD_SYMBOL(imageMagick, AcquireExceptionInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR) - FF_LIBRARY_LOAD_SYMBOL(imageMagick, DestroyExceptionInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR) - FF_LIBRARY_LOAD_SYMBOL(imageMagick, AcquireImageInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR) - FF_LIBRARY_LOAD_SYMBOL(imageMagick, DestroyImageInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR) - FF_LIBRARY_LOAD_SYMBOL(imageMagick, ReadImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR) - FF_LIBRARY_LOAD_SYMBOL(imageMagick, DestroyImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR) + ImageData imageData; - ImageMagickFunctions functions; - functions.writeFunc = writeFunc; + FF_LIBRARY_LOAD_SYMBOL_ADRESS(imData->library, imageData.ffCopyMagickString, CopyMagickString, FF_LOGO_IMAGE_RESULT_INIT_ERROR) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(imData->library, imageData.ffImageToBlob, ImageToBlob, FF_LOGO_IMAGE_RESULT_INIT_ERROR) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(imData->library, imageData.ffBase64Encode, Base64Encode, FF_LOGO_IMAGE_RESULT_INIT_ERROR) - FF_LIBRARY_LOAD_SYMBOL_ADRESS(imageMagick, functions.ffCopyMagickString, CopyMagickString, FF_LOGO_IMAGE_RESULT_INIT_ERROR) - FF_LIBRARY_LOAD_SYMBOL_ADRESS(imageMagick, functions.ffImageToBlob, ImageToBlob, FF_LOGO_IMAGE_RESULT_INIT_ERROR) - FF_LIBRARY_LOAD_SYMBOL_ADRESS(imageMagick, functions.ffBase64Encode, Base64Encode, FF_LOGO_IMAGE_RESULT_INIT_ERROR) - - ExceptionInfo* exceptionInfo = ffAcquireExceptionInfo(); - if(exceptionInfo == NULL) + imageData.exceptionInfo = ffAcquireExceptionInfo(); + if(imageData.exceptionInfo == NULL) return FF_LOGO_IMAGE_RESULT_RUN_ERROR; ImageInfo* imageInfoIn = ffAcquireImageInfo(); if(imageInfoIn == NULL) { - ffDestroyExceptionInfo(exceptionInfo); + ffDestroyExceptionInfo(imageData.exceptionInfo); return FF_LOGO_IMAGE_RESULT_RUN_ERROR; } //+1, because we need to copy the null byte too - functions.ffCopyMagickString(imageInfoIn->filename, instance->config.logoName.chars, instance->config.logoName.length + 1); + imageData.ffCopyMagickString(imageInfoIn->filename, instance->config.logoName.chars, instance->config.logoName.length + 1); - Image* originalImage = ffReadImage(imageInfoIn, exceptionInfo); + Image* originalImage = ffReadImage(imageInfoIn, imageData.exceptionInfo); ffDestroyImageInfo(imageInfoIn); if(originalImage == NULL) { - ffDestroyExceptionInfo(exceptionInfo); + ffDestroyExceptionInfo(imageData.exceptionInfo); return FF_LOGO_IMAGE_RESULT_RUN_ERROR; } - double characterPixelWidth = winsize.ws_xpixel / (double) winsize.ws_col; - double characterPixelHeight = winsize.ws_ypixel / (double) winsize.ws_row; - - //We keep the values as double, to have more precise calculations later - double imagePixelWidth = (double) instance->config.logoWidth * characterPixelWidth; - double imagePixelHeight = (imagePixelWidth / (double) originalImage->columns) * (double) originalImage->rows; - - if(imagePixelWidth < 1.0 || imagePixelHeight < 1.0) + //imagePixelWidth is set in ffLogoPrintImageIfExists + requestData->imagePixelHeight = (uint32_t) ((requestData->imagePixelWidth / (double) originalImage->columns) * (double) originalImage->rows); + if(requestData->imagePixelHeight < 1) { ffDestroyImage(originalImage); - ffDestroyExceptionInfo(exceptionInfo); + ffDestroyExceptionInfo(imageData.exceptionInfo); return FF_LOGO_IMAGE_RESULT_RUN_ERROR; } - Image* resizedImage = (Image*) resizeFunc(originalImage, (size_t) imagePixelWidth, (size_t) imagePixelHeight, exceptionInfo); + imageData.image = (Image*) imData->resizeFunc(originalImage, requestData->imagePixelWidth, requestData->imagePixelHeight, imageData.exceptionInfo); ffDestroyImage(originalImage); - if(resizedImage == NULL) + if(imageData.image == NULL) { - ffDestroyExceptionInfo(exceptionInfo); + ffDestroyExceptionInfo(imageData.exceptionInfo); return FF_LOGO_IMAGE_RESULT_RUN_ERROR; } - ImageInfo* imageInfoOut = ffAcquireImageInfo(); - if(imageInfoOut == NULL) + imageData.imageInfo = ffAcquireImageInfo(); + if(imageData.imageInfo == NULL) { - ffDestroyImage(resizedImage); - ffDestroyExceptionInfo(exceptionInfo); + ffDestroyImage(imageData.image); + ffDestroyExceptionInfo(imageData.exceptionInfo); return FF_LOGO_IMAGE_RESULT_RUN_ERROR; } bool printSuccessful; - if(type == FF_LOGO_TYPE_KITTY) - printSuccessful = printImageKitty(instance, imageInfoOut, resizedImage, exceptionInfo, instance->config.logoPaddingLeft, &functions); + if(requestData->type == FF_LOGO_TYPE_KITTY) + printSuccessful = printImageKitty(instance, requestData, &imageData); else - printSuccessful = printImageSixel(imageInfoOut, resizedImage, exceptionInfo, instance->config.logoPaddingLeft, &functions); + printSuccessful = printImageSixel(instance, requestData, &imageData); - ffDestroyImageInfo(imageInfoOut); - ffDestroyImage(resizedImage); - ffDestroyExceptionInfo(exceptionInfo); + ffDestroyImageInfo(imageData.imageInfo); + ffDestroyImage(imageData.image); + ffDestroyExceptionInfo(imageData.exceptionInfo); if(!printSuccessful) return FF_LOGO_IMAGE_RESULT_RUN_ERROR; - instance->state.logoHeight = (uint32_t) (imagePixelHeight / characterPixelHeight); - instance->state.logoWidth = instance->config.logoWidth + instance->config.logoPaddingLeft + instance->config.logoPaddingRight; + //Write height file + uint32_t folderPathLength = requestData->cacheDir.length; + ffStrbufAppendS(&requestData->cacheDir, FF_CACHE_FILE_HEIGHT); - fputs("\033[9999999D", stdout); - printf("\033[%uA", instance->state.logoHeight); + FFstrbuf content; + content.chars = (char*) &requestData->imagePixelHeight; + content.length = sizeof(requestData->imagePixelHeight); + ffWriteFileContent(requestData->cacheDir.chars, &content); + ffStrbufSubstrBefore(&requestData->cacheDir, folderPathLength); + + finishPrinting(instance, requestData); return FF_LOGO_IMAGE_RESULT_SUCCESS; } #endif //FF_HAVE_IMAGEMAGICK{6, 7} +static bool printCachedSixelFile(const FFinstance* instance, const FFLogoRequestData* requestData) +{ + int fd = open(requestData->cacheDir.chars, O_RDONLY); + if(fd == -1) + return false; + + ffPrintCharTimes(' ', instance->config.logoPaddingLeft); + fflush(stdout); + + char buffer[4096]; + ssize_t readBytes; + + while((readBytes = read(fd, buffer, sizeof(buffer))) > 0) + write(STDOUT_FILENO, buffer, (size_t) readBytes); + + close(fd); + return true; +} + +static bool printCachedSixel(const FFinstance* instance, FFLogoRequestData* requestData) +{ + uint32_t cacheDirLength = requestData->cacheDir.length; + bool result; + + ffStrbufAppendS(&requestData->cacheDir, FF_CACHE_FILE_SIXEL); + result = printCachedSixelFile(instance, requestData); + ffStrbufSubstrBefore(&requestData->cacheDir, cacheDirLength); + + return result; +} + +static bool printCachedKittyFile(const FFinstance* instance, const FFLogoRequestData* requestData, bool isCompressed) +{ + int fd = open(requestData->cacheDir.chars, O_RDONLY); + if(fd == -1) + return false; + + char buffer[FF_KITTY_MAX_CHUNK_SIZE]; + + ffPrintCharTimes(' ', instance->config.logoPaddingLeft); + printf("\033_Ga=T,f=32,s=%u,v=%u%s,m=1;\033\\", requestData->imagePixelWidth, requestData->imagePixelHeight, isCompressed ? ",o=z" : ""); + + ssize_t readBytes; + while((readBytes = read(fd, buffer, sizeof(buffer))) > 0) + { + fputs("\033_Gm=1;",stdout); + fwrite(buffer, 1, (size_t) readBytes, stdout); + fputs("\033\\", stdout); + } + + fputs("\033_Gm=0;\033\\", stdout); + + close(fd); + return true; +} + +static bool printCachedKitty(const FFinstance* instance, FFLogoRequestData* requestData) +{ + uint32_t cacheDirLength = requestData->cacheDir.length; + bool result; + + ffStrbufAppendS(&requestData->cacheDir, FF_CACHE_FILE_KITTY_COMPRESSED); + result = printCachedKittyFile(instance, requestData, true); + ffStrbufSubstrBefore(&requestData->cacheDir, cacheDirLength); + if(result) + return true; + + ffStrbufAppendS(&requestData->cacheDir, FF_CACHE_FILE_KITTY_UNCOMPRESSED); + result = printCachedKittyFile(instance, requestData, false); + ffStrbufSubstrBefore(&requestData->cacheDir, cacheDirLength); + + return result; +} + +static bool printCached(const FFinstance* instance, FFLogoRequestData* requestData) +{ + uint32_t cacheDirLength = requestData->cacheDir.length; + ffStrbufAppendS(&requestData->cacheDir, FF_CACHE_FILE_HEIGHT); + + FFstrbuf content; + ffStrbufInitA(&content, sizeof(requestData->imagePixelHeight) + 1); // +1 because strbuf is null terminated + memset(content.chars, 0, sizeof(requestData->imagePixelHeight)); + + ffAppendFileContent(requestData->cacheDir.chars, &content); + memcpy(&requestData->imagePixelHeight, content.chars, sizeof(requestData->imagePixelHeight)); + + ffStrbufDestroy(&content); + ffStrbufSubstrBefore(&requestData->cacheDir, cacheDirLength); + + if(requestData->imagePixelHeight < 1) + return false; + + if(requestData->type == FF_LOGO_TYPE_SIXEL) + return printCachedSixel(instance, requestData); + else + return printCachedKitty(instance, requestData); +} + +static bool getTermInfo(struct winsize* winsize) +{ + //Initialize every member to 0, because it isn't guaranteed that every terminal sets them all + memset(winsize, 0, sizeof(struct winsize)); + + ioctl(STDOUT_FILENO, TIOCGWINSZ, winsize); + + if(winsize->ws_row == 0 || winsize->ws_col == 0) + ffGetTerminalResponse("\033[18t", 't', "\033[8;%hu;%hut", &winsize->ws_row, &winsize->ws_col); + + if(winsize->ws_ypixel == 0 || winsize->ws_xpixel == 0) + ffGetTerminalResponse("\033[14t", 't', "\033[4;%hu;%hut", &winsize->ws_ypixel, &winsize->ws_xpixel); + + return + winsize->ws_row > 0 && + winsize->ws_col > 0 && + winsize->ws_ypixel > 0 && + winsize->ws_xpixel > 0; +} + bool ffLogoPrintImageIfExists(FFinstance* instance, FFLogoType type) { - #if !defined(FF_HAVE_IMAGEMAGICK7) && !defined(FF_HAVE_IMAGEMAGICK6) - FF_UNUSED(instance); - #endif + FFLogoRequestData requestData; + + if(!getTermInfo(&requestData.winsize)) + return false; + + requestData.type = type; + requestData.characterPixelWidth = requestData.winsize.ws_xpixel / (double) requestData.winsize.ws_col; + requestData.characterPixelHeight = requestData.winsize.ws_ypixel / (double) requestData.winsize.ws_row; + requestData.imagePixelWidth = (uint32_t) (instance->config.logoWidth * requestData.characterPixelWidth); + if(requestData.imagePixelWidth < 1) + return false; + + ffStrbufInitA(&requestData.cacheDir, PATH_MAX * 2); + ffStrbufAppend(&requestData.cacheDir, &instance->state.cacheDir); + ffStrbufAppendS(&requestData.cacheDir, "images"); + + ffStrbufEnsureFree(&requestData.cacheDir, PATH_MAX); + if(realpath(instance->config.logoName.chars, requestData.cacheDir.chars + requestData.cacheDir.length) == NULL) + { + //We can safely return here, because if realpath failed, we surely won't be able to read the file + ffStrbufDestroy(&requestData.cacheDir); + return false; + } + ffStrbufRecalculateLength(&requestData.cacheDir); + if(!ffStrbufEndsWithC(&requestData.cacheDir, '/')) + ffStrbufAppendC(&requestData.cacheDir, '/'); + + ffStrbufAppendF(&requestData.cacheDir, "%u", requestData.imagePixelWidth); + ffStrbufAppendC(&requestData.cacheDir, '/'); + + if(!instance->config.recache && printCached(instance, &requestData)) + { + finishPrinting(instance, &requestData); + ffStrbufDestroy(&requestData.cacheDir); + return true; + } + + FFLogoImageResult result = FF_LOGO_IMAGE_RESULT_INIT_ERROR; #ifdef FF_HAVE_IMAGEMAGICK7 - FFLogoImageResult result = ffLogoPrintImageIM7(instance, type); - if(result == FF_LOGO_IMAGE_RESULT_SUCCESS) - return true; - else if(result == FF_LOGO_IMAGE_RESULT_RUN_ERROR) - return false; + result = ffLogoPrintImageIM7(instance, &requestData); #endif #ifdef FF_HAVE_IMAGEMAGICK6 - return ffLogoPrintImageIM6(instance, type) == FF_LOGO_IMAGE_RESULT_SUCCESS; + if(result == FF_LOGO_IMAGE_RESULT_INIT_ERROR) + result = ffLogoPrintImageIM6(instance, &requestData); #endif - return false; + ffStrbufDestroy(&requestData.cacheDir); + return result == FF_LOGO_IMAGE_RESULT_SUCCESS; } diff --git a/src/logo/image/image.h b/src/logo/image/image.h index 48dfe3fe9..08e7b0622 100644 --- a/src/logo/image/image.h +++ b/src/logo/image/image.h @@ -7,6 +7,8 @@ #if defined(FF_HAVE_IMAGEMAGICK7) || defined(FF_HAVE_IMAGEMAGICK6) +#include + #define MAGICKCORE_HDRI_ENABLE 1 #define MAGICKCORE_QUANTUM_DEPTH 16 @@ -17,20 +19,35 @@ typedef enum FFLogoImageResult FF_LOGO_IMAGE_RESULT_RUN_ERROR //Failed to load / convert image, cancle whole sixel code } FFLogoImageResult; -typedef void*(*FFLogoIMResizeFunc)(const void* image, size_t width, size_t height, void* exceptionInfo); -typedef bool(*FFLogoIMWriteFunc)(void* imageInfo, void* image, void* exceptionInfo); +typedef struct FFLogoRequestData +{ + FFLogoType type; + FFstrbuf cacheDir; -FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, void* library, FFLogoIMResizeFunc resizeFunc, FFLogoIMWriteFunc writeFunc, FFLogoType type); + struct winsize winsize; + double characterPixelWidth; + double characterPixelHeight; + uint32_t imagePixelWidth; + uint32_t imagePixelHeight; +} FFLogoRequestData; + +typedef struct FFIMData +{ + void* library; + void*(*resizeFunc)(const void* image, size_t width, size_t height, void* exceptionInfo); +} FFIMData; + +FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData* requestData, const FFIMData* imData); #endif #ifdef FF_HAVE_IMAGEMAGICK7 -FFLogoImageResult ffLogoPrintImageIM7(FFinstance* instance, FFLogoType type); +FFLogoImageResult ffLogoPrintImageIM7(FFinstance* instance, FFLogoRequestData* requestData); #endif #ifdef FF_HAVE_IMAGEMAGICK6 #include -FFLogoImageResult ffLogoPrintImageIM6(FFinstance* instance, FFLogoType type); +FFLogoImageResult ffLogoPrintImageIM6(FFinstance* instance, FFLogoRequestData* requestData); #endif