master ohmu/fastfetch-openbsd / src / logo / image / image.h
ohmu  ·  2026-10-15
  1#pragma once
  2
  3#include "../logo.h"
  4
  5#if defined(FF_HAVE_IMAGEMAGICK7) || defined(FF_HAVE_IMAGEMAGICK6) || defined(_WIN32) || defined(__APPLE__) || defined(__ANDROID__) || defined(FF_HAVE_SIXEL)
  6
  7typedef enum FFLogoImageResult: uint8_t {
  8    FF_LOGO_IMAGE_RESULT_SUCCESS,    // Logo printed
  9    FF_LOGO_IMAGE_RESULT_INIT_ERROR, // Failed to load library, try again with next IM version
 10    FF_LOGO_IMAGE_RESULT_RUN_ERROR   // Failed to load / convert image, cancel whole sixel code
 11} FFLogoImageResult;
 12
 13typedef struct FFLogoRequestData {
 14    FFLogoType type;
 15    FFstrbuf cacheDir;
 16
 17    double characterPixelWidth;
 18    double characterPixelHeight;
 19
 20    uint32_t logoPixelWidth;
 21    uint32_t logoPixelHeight;
 22
 23    uint32_t logoCharacterHeight;
 24    uint32_t logoCharacterWidth;
 25} FFLogoRequestData;
 26
 27// Decoded and resized image: RGBA8, straight (unassociated) alpha, no row padding
 28typedef struct FFImageBuffer {
 29    uint8_t* data;
 30    uint32_t width;
 31    uint32_t height;
 32} FFImageBuffer;
 33
 34// Backend contract. Both functions update requestData->logoPixelWidth / logoPixelHeight
 35// with the real dimensions of the produced image, so the caller can derive the character
 36// dimensions afterwards.
 37
 38// Decode the image source and resize it to the requested pixel size
 39bool ffImageCreate(FFLogoRequestData* requestData, FFImageBuffer* out, const char** error);
 40void ffImageDestroy(FFImageBuffer* buffer);
 41
 42// Encode the image source as a sixel byte stream (including the DCS envelope).
 43// The encoder belongs to the backend: ImageMagick's SIXEL coder off Windows,
 44// the embedded libsixel on Windows.
 45bool ffImageSixelEncode(FFLogoRequestData* requestData, FFstrbuf* out, const char** error);
 46
 47// The same, for pixels the caller already has. The selected-frame path needs this: it composes the
 48// frame itself, and ffImageSixelEncode would go back to the source and encode a different one.
 49bool ffImageSixelEncodeBuffer(const FFImageBuffer* buffer, FFstrbuf* out, const char** error);
 50
 51// One frame of an animation: a fully composed canvas, RGBA8, straight (unassociated) alpha,
 52// no row padding, exactly requestData->logoPixelWidth x logoPixelHeight.
 53typedef struct FFImageFrame {
 54    uint8_t* data;
 55    int32_t delayMs; // >0: wait this many milliseconds; -1: gapless; 0: unspecified
 56} FFImageFrame;
 57
 58void ffImageFrameDestroy(FFImageFrame* frame);
 59
 60// Animation session. Opaque outside src/logo/image/.
 61//
 62// Contract: the backend hands out *composed* full-canvas frames through a sequential iterator.
 63// Composition belongs to the backend (ImageIO already does it, WIC does not), never to the
 64// output layer. Taking a frame transfers its ownership to the caller, so the caller never holds
 65// more than one frame. Frames must be taken in non-decreasing index order: the composition of
 66// frame i depends on frames 0..i-1.
 67//
 68// That says nothing about what the backend holds, though. ImageMagick's CoalesceImages composes
 69// every frame up front and keeps the list, so its peak is the whole animation rather than one
 70// frame -- 60 frames of 500x500 measured 532 MiB. Bounding that would mean writing the compositor
 71// by hand, which is the cost reusing the library's composition exists to avoid, so it is accepted
 72// and recorded instead. See doc/kitty-animation.md 5.4 and 11-E.
 73typedef struct FFImageAnimation FFImageAnimation;
 74
 75// Open a session. Like ffImageCreate, it must fill in requestData->logoPixelWidth /
 76// logoPixelHeight, because every frame has those dimensions. frameCount / loopCount must be
 77// known without decoding a frame, because a negative --logo-animation-frame index is resolved
 78// before the first frame is taken.
 79bool ffImageAnimationOpen(FFLogoRequestData* requestData, FFImageAnimation** out, const char** error);
 80uint32_t ffImageAnimationFrameCount(const FFImageAnimation* animation);
 81int32_t ffImageAnimationLoopCount(const FFImageAnimation* animation); // 0 = infinite; -1 = the source declares none
 82bool ffImageAnimationGetFrame(FFImageAnimation* animation, uint32_t index, FFImageFrame* out, const char** error);
 83void ffImageAnimationClose(FFImageAnimation* animation);
 84
 85// Called by the backends once they have a session ready. `destroy` must release the session and
 86// its backend state; `impl` is handed back to them untouched.
 87FFImageAnimation* ffImageAnimationCreate(uint32_t frameCount, int32_t loopCount, void* impl,
 88    bool (*getFrame)(FFImageAnimation* animation, uint32_t index, FFImageFrame* out, const char** error),
 89    void (*destroy)(FFImageAnimation* animation));
 90
 91// The session is opaque to the backends; this is how the callbacks they supply get their own
 92// state back out of it. Never null for a session that ffImageAnimationCreate built.
 93void* ffImageAnimationGetImpl(const FFImageAnimation* animation);
 94
 95#endif
 96
 97#ifdef FF_HAVE_IMAGEMAGICK7
 98bool ffImageCreateIM7(FFLogoRequestData* requestData, FFImageBuffer* out, const char** error);
 99bool ffImageSixelEncodeIM7(FFLogoRequestData* requestData, FFstrbuf* out, const char** error);
100bool ffImageSixelEncodeBufferIM7(const FFImageBuffer* buffer, FFstrbuf* out, const char** error);
101// ImageMagick 7 is the only ImageMagick version that gets animation: 6 would need its own port
102// (PixelPacket / opacity instead of PixelInfo / alpha) and there is no way to test it here, so it
103// is left unimplemented rather than written blind. See doc/kitty-animation.md ยง11-E.
104bool ffImageAnimationOpenIM7(FFLogoRequestData* requestData, FFImageAnimation** out, const char** error);
105#endif
106
107#ifdef FF_HAVE_IMAGEMAGICK6
108bool ffImageCreateIM6(FFLogoRequestData* requestData, FFImageBuffer* out, const char** error);
109bool ffImageSixelEncodeIM6(FFLogoRequestData* requestData, FFstrbuf* out, const char** error);
110#endif
111
112#ifdef _WIN32
113bool ffImageCreateWIC(FFLogoRequestData* requestData, FFImageBuffer* out, const char** error);
114bool ffImageAnimationOpenWIC(FFLogoRequestData* requestData, FFImageAnimation** out, const char** error);
115#endif
116
117#ifdef __APPLE__
118bool ffImageCreateImageIO(FFLogoRequestData* requestData, FFImageBuffer* out, const char** error);
119bool ffImageAnimationOpenImageIO(FFLogoRequestData* requestData, FFImageAnimation** out, const char** error);
120#endif
121
122#ifdef __ANDROID__
123bool ffImageCreateAID(FFLogoRequestData* requestData, FFImageBuffer* out, const char** error);
124bool ffImageAnimationOpenAID(FFLogoRequestData* requestData, FFImageAnimation** out, const char** error);
125#endif
126
127#ifdef FF_HAVE_SIXEL
128bool ffSixelEncode(const FFImageBuffer* buffer, FFstrbuf* result, const char** error);
129#endif