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