77#include <string.h>
88
99#include "stable-diffusion.h"
10+ #include "./gif.h" // charlietangora/gif-h
1011
1112#ifndef INCLUDE_STB_IMAGE_WRITE_H
1213#include "stb_image_write.h"
@@ -390,4 +391,107 @@ int create_mjpg_avi_membuf_from_sd_images(sd_image_t* images, int num_images, in
390391 return 0 ;
391392}
392393
394+ /// kcpp gif writer
395+
396+ // ---------------- Helper: create_gif_buf_from_sd_images ----------------
397+ // Builds a GIF in memory from an array of sd_image_t. Returns 0 on success, -1 on failure.
398+ // Caller must free(*out_data) when done.
399+ int create_gif_buf_from_sd_images (sd_image_t * images , int num_images , int fps , int quality , uint8_t * * out_data , size_t * out_len )
400+ {
401+ if (!images || num_images <= 0 || !out_data || !out_len ) return -1 ;
402+
403+ // basic parameter heuristics
404+ if (fps <= 0 ) fps = 16 ;
405+ uint32_t delay = (uint32_t )(100 / fps ); // hundredths of a second per frame
406+
407+ // map quality [1..100] to bitDepth and dithering
408+ if (quality < 1 ) quality = 1 ;
409+ if (quality > 100 ) quality = 100 ;
410+ int bitDepth = 8 ;
411+ bool dither = false;
412+ // if(quality >= 80) { bitDepth = 8; dither = false; }
413+ // else if(quality >= 50) { bitDepth = 6; dither = false; }
414+ // else { bitDepth = 5; dither = true; }
415+
416+ // assume all images same size; use first
417+ uint32_t width = images [0 ].width ;
418+ uint32_t height = images [0 ].height ;
419+
420+ GifWriter gw ;
421+ memset (& gw , 0 , sizeof (gw ));
422+
423+ if (!GifBegin (& gw , width , height , delay , bitDepth , dither ))
424+ {
425+ if (gw .oldImage ) GIF_FREE (gw .oldImage );
426+
427+ fprintf (stderr , "Error: GifBegin failed.\n" );
428+ return -1 ;
429+ }
430+
431+ // Feed frames
432+ for (int i = 0 ; i < num_images ; i ++ )
433+ {
434+ sd_image_t * img = & images [i ];
435+
436+ if (img -> width != width || img -> height != height ) {
437+ fprintf (stderr , "Frame %d has mismatched dimensions.\n" , i );
438+ GifEnd (& gw );
439+ memfile_free (& gw .mem );
440+ return -1 ;
441+ }
442+
443+ // gif-h expects 4 channels (RGBA) or 3 channels (RGB). It quantizes internally.
444+ // If your images have 3 channels, that’s fine. If 4 channels, it also works.
445+ int channels = img -> channel ;
446+ if (channels != 3 && channels != 4 ) {
447+ fprintf (stderr , "Unsupported channel count: %d\n" , channels );
448+ GifEnd (& gw );
449+ memfile_free (& gw .mem );
450+ return -1 ;
451+ }
452+
453+ // gif-h requires 4 channels (RGBA). If you only have RGB, add opaque alpha.
454+ uint8_t * frame_rgba = NULL ;
455+ if (channels == 3 ) {
456+ frame_rgba = (uint8_t * )malloc (width * height * 4 );
457+ for (int p = 0 ; p < width * height ; p ++ ) {
458+ frame_rgba [p * 4 + 0 ] = img -> data [p * 3 + 0 ];
459+ frame_rgba [p * 4 + 1 ] = img -> data [p * 3 + 1 ];
460+ frame_rgba [p * 4 + 2 ] = img -> data [p * 3 + 2 ];
461+ frame_rgba [p * 4 + 3 ] = 255 ;
462+ }
463+ } else {
464+ frame_rgba = img -> data ; // already RGBA
465+ }
466+
467+ if (!GifWriteFrame (& gw , frame_rgba , width , height , delay , bitDepth , dither ))
468+ {
469+ fprintf (stderr , "GIF Write Failed\n" );
470+ GifEnd (& gw );
471+ memfile_free (& gw .mem );
472+ return -1 ;
473+ }
474+
475+ if (channels == 3 ) {
476+ free (frame_rgba );
477+ }
478+ }
479+
480+ if (!GifEnd (& gw ))
481+ {
482+ memfile_free (& gw .mem );
483+ return -1 ;
484+ }
485+
486+ uint8_t * buf = memfile_detach (& gw .mem , out_len );
487+ if (!buf )
488+ {
489+ memfile_free (& gw .mem );
490+ return -1 ;
491+ }
492+
493+ * out_data = buf ;
494+ return 0 ;
495+ }
496+
393497#endif // __AVI_WRITER_H__
0 commit comments