### Package Gifski with cargo-c Source: https://context7.com/sindresorhus/gifski/llms.txt Instructions for using cargo-c to generate proper pkg-config files and install the library to a system directory. ```bash cargo install cargo-c cargo cbuild --prefix=/usr --release cargo cinstall --prefix=/usr --release --destdir=pkgroot ``` -------------------------------- ### Build Gifski Library using Rust Source: https://context7.com/sindresorhus/gifski/llms.txt Instructions to build the gifski library as a static or dynamic library. Requires Rust to be installed. Clones the repository and navigates into the directory. ```bash # Install Rust (if not already installed) curl https://sh.rustup.rs -sSf | sh # Clone repository git clone https://github.com/ImageOptim/gifski cd gifski ``` -------------------------------- ### Build Gifski from Source (Shell) Source: https://github.com/sindresorhus/gifski/blob/main/readme.md Instructions to build the Gifski application from source using Xcode. This process requires installing Rust and SwiftLint, and ensuring Xcode command-line tools are selected. ```shell curl https://sh.rustup.rs -sSf | sh brew install SwiftLint xcode-select --install ``` -------------------------------- ### Rust Library: Basic GIF Encoding Source: https://context7.com/sindresorhus/gifski/llms.txt Utilize the gifski Rust library for programmatic GIF encoding. This example demonstrates configuring GIF settings, creating a collector and writer pair, and processing PNG frames concurrently using threads. It handles frame collection and GIF writing efficiently. ```rust use gifski::{Settings, Repeat}; use std::fs::File; fn main() -> Result<(), gifski::Error> { // Configure GIF settings let settings = Settings { width: Some(640), // Max width (None for original) height: Some(480), // Max height (None for original) quality: 90, // 1-100, recommended 90+ fast: false, // true for faster but lower quality repeat: Repeat::Infinite, // Infinite, Finite(n), or Finite(0) for no loop }; // Create collector and writer pair let (collector, writer) = gifski::new(settings)?; // Use thread scope to run collector and writer concurrently std::thread::scope(|scope| -> Result<(), gifski::Error> { // Spawn thread to collect frames let collector_thread = scope.spawn(move || { for i in 0..60 { let path = format!("frame{:04}.png", i); // PTS = presentation timestamp in seconds let pts = i as f64 / 30.0; // 30 FPS collector.add_frame_png_file(i, path.into(), pts)?; } drop(collector); // Signal end of frames Ok::<_, gifski::Error>(()) }); // Write GIF on main thread let output = File::create("output.gif")?; writer.write(output, &mut gifski::progress::NoProgress {})?; collector_thread.join().unwrap() })?; Ok(()) } ``` -------------------------------- ### Convert PNG sequence to GIF using FFmpeg and Gifski Source: https://github.com/sindresorhus/gifski/blob/main/readme.md This command sequence converts a series of sequentially numbered PNG images into a ProRes MOV file using FFmpeg, then opens the resulting file in Gifski for final GIF conversion. It requires FFmpeg to be installed and expects files named in the format 'image_000001.png'. ```bash TMPFILE="$(mktemp /tmp/XXXXXXXXXXX).mov"; \ ffmpeg -f image2 -framerate 30 -i image_%06d.png -c:v prores_ks -profile:v 5 "$TMPFILE" \ && open -a Gifski "$TMPFILE" ``` -------------------------------- ### Build C library and dynamic library Source: https://github.com/sindresorhus/gifski/blob/main/gifski-api/README.md Instructions for building the C library for integration into other applications and generating a dynamic library with pkg-config support. ```shell rustup update cargo build --release # For dynamic library cargo install cargo-c cargo cbuild --prefix=/usr --release cargo cinstall --prefix=/usr --release --destdir=pkgroot ``` -------------------------------- ### Launch multiple Gifski instances via Terminal Source: https://github.com/sindresorhus/gifski/blob/main/readme.md This command opens a new instance of the Gifski application, allowing for multiple concurrent video conversion processes. Note that editor settings should not be modified simultaneously across multiple instances to avoid configuration conflicts. ```bash open -na Gifski ``` -------------------------------- ### Build gifski from source Source: https://github.com/sindresorhus/gifski/blob/main/gifski-api/README.md Standard commands to build the gifski project from the source repository using the Rust toolchain. ```shell git clone https://github.com/ImageOptim/gifski cd gifski cargo build --release ``` -------------------------------- ### Debug Gifski System Service with Finder (Bash) Source: https://github.com/sindresorhus/gifski/blob/main/maintaining.md This command launches the Finder application with a specific debug flag enabled for the Gifski system service. This is useful for troubleshooting issues by examining the logs generated by the service. ```bash /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder -NSDebugServices com.sindresorhus.Gifski ``` -------------------------------- ### Build Gifski Library using Cargo Source: https://context7.com/sindresorhus/gifski/llms.txt Commands to compile the Gifski library for various environments, including standard release builds, video-enabled builds, and cross-compilation for iOS. ```bash # Standard release build cargo build --release --lib # Build with video support cargo build --release --features=video # Build for iOS rustup target add aarch64-apple-ios cargo build --release --lib --target aarch64-apple-ios ``` -------------------------------- ### Implement Custom Write and Progress Callbacks in C API Source: https://context7.com/sindresorhus/gifski/llms.txt Shows how to use custom write and progress callbacks with the Gifski C API. The write callback allows writing to any destination (e.g., memory buffer, network stream), while the progress callback monitors encoding progress and can be used to abort the process. Requires the gifski library. ```c #include "gifski.h" #include // Custom write callback - writes to any destination int write_callback(size_t buffer_length, const uint8_t *buffer, void *user_data) { FILE *file = (FILE *)user_data; if (buffer_length == 0) { // Flush signal fflush(file); return GIFSKI_OK; } size_t written = fwrite(buffer, 1, buffer_length, file); return (written == buffer_length) ? GIFSKI_OK : GIFSKI_WRITE_ZERO; } // Progress callback - return 1 to continue, 0 to abort int progress_callback(void *user_data) { int *frame_count = (int *)user_data; (*frame_count)++; printf("Processed frame %d\n", *frame_count); return 1; // Continue processing } // Error message callback for debugging void error_callback(const char *message, void *user_data) { fprintf(stderr, "Gifski error: %s\n", message); } int main() { GifskiSettings settings = { .width = 640, .height = 480, .quality = 90, .fast = false, .repeat = 0, }; gifski *g = gifski_new(&settings); // Set error callback (optional, for debugging) gifski_set_error_message_callback(g, error_callback, NULL); // Set progress callback (must be before set_file_output or set_write_callback) int frames_processed = 0; gifski_set_progress_callback(g, progress_callback, &frames_processed); // Use custom write callback instead of file output FILE *output = fopen("callback_output.gif", "wb"); gifski_set_write_callback(g, write_callback, output); // Add frames... for (int i = 0; i < 30; i++) { char path[64]; snprintf(path, sizeof(path), "frame%04d.png", i); gifski_add_frame_png_file(g, i, path, i / 20.0); } GifskiError result = gifski_finish(g); fclose(output); printf("Finished! Processed %d frames\n", frames_processed); return result == GIFSKI_OK ? 0 : 1; } ``` -------------------------------- ### CLI: Basic Video to GIF Conversion Source: https://context7.com/sindresorhus/gifski/llms.txt Convert videos or PNG sequences to GIFs using the gifski command-line tool. Supports piping from ffmpeg for video input and custom frame rate and dimensions for PNG sequences. Recommended for basic conversions. ```bash # Basic conversion from video using ffmpeg pipe (recommended method) ffmpeg -i video.mp4 -f yuv4mpegpipe - | gifski -o output.gif - # Convert PNG frame sequence to GIF at 20 FPS gifski -o animation.gif frame*.png # High quality conversion with custom dimensions gifski --width 800 --height 600 --quality 95 -o output.gif frame*.png # Fast encoding (50% faster, slightly lower quality) gifski --fast --fps 30 -o quick.gif video_frames/*.png # Extra effort encoding (slower but better quality) gifski --extra --quality 100 -o premium.gif frame*.png ``` -------------------------------- ### Convert PNG frames to GIF Source: https://github.com/sindresorhus/gifski/blob/main/gifski-api/README.md Shows the process of exporting video frames as PNG files using ffmpeg and then compiling those frames into an animated GIF using gifski. ```shell ffmpeg -i video.webm frame%04d.png gifski -o anim.gif frame*.png ``` -------------------------------- ### Add RGBA Frames in Rust Source: https://context7.com/sindresorhus/gifski/llms.txt Demonstrates how to process raw RGBA pixel data directly into the encoder. This approach is ideal for real-time rendering pipelines where intermediate PNG files are not desired. ```rust use gifski::{Settings, Repeat}; use gifski::collector::{ImgVec, RGBA8}; use std::fs::File; fn main() -> Result<(), gifski::Error> { let settings = Settings { width: None, height: None, quality: 95, fast: false, repeat: Repeat::Infinite, }; let (collector, writer) = gifski::new(settings)?; std::thread::scope(|scope| -> Result<(), gifski::Error> { let collector_thread = scope.spawn(move || { let width = 320; let height = 240; for frame_idx in 0..30 { let mut pixels = Vec::with_capacity(width * height); for y in 0..height { for x in 0..width { pixels.push(RGBA8::new( ((x + frame_idx * 10) % 256) as u8, ((y + frame_idx * 5) % 256) as u8, 128, 255, )); } } let frame = ImgVec::new(pixels, width, height); let pts = frame_idx as f64 / 15.0; collector.add_frame_rgba(frame_idx, frame, pts)?; } drop(collector); Ok::<_, gifski::Error>(()) }); let output = File::create("generated.gif")?; writer.write(output, &mut gifski::progress::NoProgress {})?; collector_thread.join().unwrap() })?; Ok(()) } ``` -------------------------------- ### Implement Progress Reporting in Rust Source: https://context7.com/sindresorhus/gifski/llms.txt Shows how to track encoding progress by implementing the ProgressReporter trait. This allows developers to monitor frame processing and byte output in real-time. ```rust use gifski::{Settings, Repeat}; use gifski::progress::ProgressReporter; use std::fs::File; struct MyProgress { frames_done: u64, total_bytes: u64, } impl ProgressReporter for MyProgress { fn increase(&mut self) -> bool { self.frames_done += 1; println!("Encoded frame {}", self.frames_done); true } fn written_bytes(&mut self, bytes: u64) { self.total_bytes = bytes; println!("Written {} KB so far", bytes / 1024); } fn done(&mut self, msg: &str) { println!("{} - Total size: {} KB", msg, self.total_bytes / 1024); } } fn main() -> Result<(), gifski::Error> { let settings = Settings::default(); let (collector, writer) = gifski::new(settings)?; std::thread::scope(|scope| -> Result<(), gifski::Error> { let collector_thread = scope.spawn(move || { for i in 0..100 { collector.add_frame_png_file(i, format!("frame{i:04}.png").into(), i as f64 * 0.05)?; } drop(collector); Ok::<_, gifski::Error>(()) }); let output = File::create("with_progress.gif")?; let mut progress = MyProgress { frames_done: 0, total_bytes: 0 }; writer.write(output, &mut progress)?; collector_thread.join().unwrap() })?; Ok(()) } ``` -------------------------------- ### Basic C API Usage Source: https://context7.com/sindresorhus/gifski/llms.txt Provides a standard implementation for using the Gifski C API. It covers initialization, setting output files, adding PNG frames, and finalizing the encoding process. ```c #include "gifski.h" #include #include int main() { GifskiSettings settings = { .width = 640, .height = 480, .quality = 90, .fast = false, .repeat = 0, }; gifski *g = gifski_new(&settings); if (!g) { fprintf(stderr, "Failed to create gifski instance\n"); return 1; } GifskiError err = gifski_set_file_output(g, "output.gif"); if (err != GIFSKI_OK) { fprintf(stderr, "Failed to set output: %d\n", err); return 1; } for (int i = 0; i < 60; i++) { char path[256]; snprintf(path, sizeof(path), "frame%04d.png", i); double pts = i / 30.0; err = gifski_add_frame_png_file(g, i, path, pts); if (err != GIFSKI_OK) { fprintf(stderr, "Failed to add frame %d: %d\n", i, err); break; } } err = gifski_finish(g); if (err != GIFSKI_OK) { fprintf(stderr, "Encoding failed: %d\n", err); return 1; } printf("GIF created successfully!\n"); return 0; } ``` -------------------------------- ### Convert video to GIF using ffmpeg and gifski Source: https://github.com/sindresorhus/gifski/blob/main/gifski-api/README.md Demonstrates how to stream video frames from ffmpeg directly into gifski using a pipe. This method avoids creating large intermediate files by reading from standard input. ```shell ffmpeg -i video.mp4 -f yuv4mpegpipe - | gifski -o anim.gif - ``` -------------------------------- ### Callbacks and Progress Tracking Source: https://context7.com/sindresorhus/gifski/llms.txt Configuration of custom write destinations and progress monitoring during the encoding process. ```APIDOC ## [FUNCTION] gifski_set_write_callback ### Description Sets a custom callback function to handle the writing of encoded GIF data to a destination. ### Parameters - **g** (gifski*) - Required - The gifski instance - **callback** (function) - Required - Function pointer: int (*)(size_t, const uint8_t*, void*) - **user_data** (void*) - Optional - Pointer to custom user data --- ## [FUNCTION] gifski_set_progress_callback ### Description Registers a callback to track encoding progress. The callback should return 1 to continue or 0 to abort. ### Parameters - **g** (gifski*) - Required - The gifski instance - **callback** (function) - Required - Function pointer: int (*)(void*) - **user_data** (void*) - Optional - Pointer to custom user data ``` -------------------------------- ### CLI: Advanced GIF Conversion Options Source: https://context7.com/sindresorhus/gifski/llms.txt Fine-tune GIF output with advanced options like motion quality, lossy compression, looping behavior, and color palette customization. Allows for creating non-looping GIFs, GIFs with specific loop counts, bounce effects, and controlling playback speed. ```bash # Control motion quality and lossy compression for smaller files gifski --quality 80 --motion-quality 60 --lossy-quality 60 -o smaller.gif frames/*.png # Create non-looping GIF (plays once) gifski --repeat -1 -o play_once.gif frames/*.png # Create GIF that loops 3 times gifski --repeat 3 -o loops_three_times.gif frames/*.png # Bounce effect (plays forward then backward) gifski --bounce -o bounce.gif frames/*.png # Speed up video 2x during conversion gifski --fast-forward 2 --fps 25 -o fast.gif frames/*.png # Preserve specific colors in palette gifski --fixed-color "#FF0000" --fixed-color "#00FF00" -o branded.gif frames/*.png # Set background color for semi-transparent pixels gifski --matte "#FFFFFF" -o with_bg.gif frames/*.png # Output to stdout for piping gifski -o - frames/*.png > output.gif ``` -------------------------------- ### Check Gifski System Service Status (Bash) Source: https://github.com/sindresorhus/gifski/blob/main/maintaining.md This command checks if the Gifski application is correctly registered as a system service. It queries the system's property list service dump and filters for the Gifski app path. If the path is not found, it suggests updating the service cache. ```bash /System/Library/CoreServices/pbs -dump | grep Gifski.app ``` -------------------------------- ### Implement C API Error Handling Source: https://context7.com/sindresorhus/gifski/llms.txt Defines the GifskiError enumeration and demonstrates a switch-case pattern for handling specific return codes in C applications. ```c typedef enum GifskiError { GIFSKI_OK = 0, GIFSKI_NULL_ARG, GIFSKI_INVALID_STATE, GIFSKI_QUANT, GIFSKI_GIF, GIFSKI_THREAD_LOST, GIFSKI_NOT_FOUND, GIFSKI_PERMISSION_DENIED, GIFSKI_ALREADY_EXISTS, GIFSKI_INVALID_INPUT, GIFSKI_TIMED_OUT, GIFSKI_WRITE_ZERO, GIFSKI_INTERRUPTED, GIFSKI_UNEXPECTED_EOF, GIFSKI_ABORTED, GIFSKI_OTHER } GifskiError; GifskiError err = gifski_add_frame_png_file(g, 0, "frame.png", 0.0); switch (err) { case GIFSKI_OK: break; case GIFSKI_NOT_FOUND: fprintf(stderr, "Frame file not found\n"); break; case GIFSKI_INVALID_STATE: fprintf(stderr, "Cannot add frame after finish() called\n"); break; default: fprintf(stderr, "Error code: %d\n", err); } ``` -------------------------------- ### Add RGBA Pixel Data to GIF using C API Source: https://context7.com/sindresorhus/gifski/llms.txt Demonstrates how to add frames to a GIF using RGBA pixel data from memory buffers. This is useful for real-time applications. It covers adding frames with default stride, custom stride, ARGB format, and RGB format. Requires the gifski library. ```c #include "gifski.h" #include #include int main() { GifskiSettings settings = { .width = 320, .height = 240, .quality = 90, .fast = false, .repeat = 0, }; gifski *g = gifski_new(&settings); gifski_set_file_output(g, "rgba_output.gif"); int width = 320; int height = 240; unsigned char *pixels = malloc(width * height * 4); // RGBA = 4 bytes/pixel for (int frame = 0; frame < 30; frame++) { // Generate frame content for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int idx = (y * width + x) * 4; pixels[idx + 0] = (x + frame * 10) % 256; // R pixels[idx + 1] = (y + frame * 5) % 256; // G pixels[idx + 2] = 128; // B pixels[idx + 3] = 255; // A (opaque) } } double pts = frame / 15.0; // 15 FPS GifskiError err = gifski_add_frame_rgba(g, frame, width, height, pixels, pts); if (err != GIFSKI_OK) break; } free(pixels); GifskiError result = gifski_finish(g); return result == GIFSKI_OK ? 0 : 1; } // Alternative: Add frame with custom stride (bytes per row) void add_frame_with_stride(gifski *g, int frame_num, int width, int height, int bytes_per_row, unsigned char *pixels, double pts) { gifski_add_frame_rgba_stride(g, frame_num, width, height, bytes_per_row, pixels, pts); } // For ARGB format (alpha byte first) void add_argb_frame(gifski *g, int frame_num, int width, int bytes_per_row, int height, unsigned char *argb_pixels, double pts) { gifski_add_frame_argb(g, frame_num, width, bytes_per_row, height, argb_pixels, pts); } // For RGB format (no alpha, 3 bytes per pixel) void add_rgb_frame(gifski *g, int frame_num, int width, int bytes_per_row, int height, unsigned char *rgb_pixels, double pts) { gifski_add_frame_rgb(g, frame_num, width, bytes_per_row, height, rgb_pixels, pts); } ``` -------------------------------- ### C API: Configure Quality Settings for Gifski Source: https://context7.com/sindresorhus/gifski/llms.txt Configure motion quality, lossy compression, and extra effort for GIF encoding using the C API. Requires gifski_new to be called first. Outputs to a specified file. ```c #include "gifski.h" int main() { GifskiSettings settings = { .width = 800, .height = 600, .quality = 90, .fast = false, .repeat = 0, }; gifski *g = gifski_new(&settings); // Set motion quality (1-100) - lower values reduce motion/noise // Must be called immediately after gifski_new, before adding frames gifski_set_motion_quality(g, 70); // Set lossy quality (1-100) - lower values add noise but reduce file size // Requires the 'gifsicle' feature to be enabled gifski_set_lossy_quality(g, 80); // Enable extra effort mode - slower but slightly better quality gifski_set_extra_effort(g, true); gifski_set_file_output(g, "high_quality.gif"); // Add frames and finish... for (int i = 0; i < 60; i++) { char path[64]; snprintf(path, sizeof(path), "frame%04d.png", i); gifski_add_frame_png_file(g, i, path, i / 30.0); } return gifski_finish(g) == GIFSKI_OK ? 0 : 1; } ``` -------------------------------- ### Swift/macOS: GIFGenerator for Video to GIF Conversion Source: https://context7.com/sindresorhus/gifski/llms.txt Convert AVAsset videos to GIF data with trimming, cropping, and quality control using the GIFGenerator actor. Supports progress tracking and bounce effect. Outputs Data. ```swift import AVFoundation // Create conversion configuration var conversion = GIFGenerator.Conversion( asset: AVAsset(url: videoURL), sourceURL: videoURL, loop: .forever, bounce: false ) // Configure conversion settings conversion.timeRange = 0.0...5.0 // Trim to first 5 seconds conversion.quality = 0.9 // Quality 0.1-1.0 conversion.dimensions = (width: 480, height: 320) conversion.frameRate = 15 // Output FPS conversion.bounce = true // Play forward then backward // Run conversion with progress tracking let gifData = try await GIFGenerator.run(conversion) { progress in print("Progress: \(Int(progress * 100))%") } // Save result try gifData.write(to: outputURL) // Alternative: Convert a single frame let singleFrameGif = try await GIFGenerator.convertOneFrame( frame: cgImage, dimensions: (width: 200, height: 200), quality: 0.95, fast: true ) ``` -------------------------------- ### Swift/macOS: GifskiWrapper Low-Level API Bindings Source: https://context7.com/sindresorhus/gifski/llms.txt Direct Swift bindings to the C API for low-level control over GIF encoding. Allows setting callbacks for errors, progress, and writing data chunks. Outputs Data. ```swift import Foundation // Create settings var settings = GifskiSettings( width: 640, height: 480, quality: 90, fast: false, repeat: 0 // 0=infinite, -1=none, >0=count ) // Initialize wrapper guard let wrapper = GifskiWrapper(settings) else { throw GifskiWrapper.Error.invalidInput } // Set error callback for debugging wrapper.setErrorMessageCallback { message in print("Error: \(message)") } // Set progress callback (return 1 to continue, 0 to abort) wrapper.setProgressCallback { print("Frame processed") return 1 } // Set write callback to receive GIF data chunks var outputData = Data() wrapper.setWriteCallback { outputData.append(pointer, count: length) return 0 // Return 0 for success } // Add frames for (index, pixels) in framePixels.enumerated() { try wrapper.addFrame( pixelFormat: .rgba, // .rgba, .argb, or .rgb frameNumber: index, width: 640, height: 480, bytesPerRow: 640 * 4, pixels: pixels, presentationTimestamp: Double(index) / 30.0 ) } // Finish encoding try wrapper.finish() // outputData now contains the complete GIF ``` -------------------------------- ### Update Gifski System Service Cache (Bash) Source: https://github.com/sindresorhus/gifski/blob/main/maintaining.md This command updates the system's cache for property list services. It is used when the Gifski system service is not detected correctly, potentially resolving issues with service registration. ```bash /System/Library/CoreServices/pbs -update ``` -------------------------------- ### Adding Pixel Data Frames Source: https://context7.com/sindresorhus/gifski/llms.txt Functions for adding image frames directly from memory buffers, supporting various pixel formats and stride configurations. ```APIDOC ## [FUNCTION] gifski_add_frame_rgba ### Description Adds a frame from an RGBA pixel buffer to the gifski instance. ### Parameters - **g** (gifski*) - Required - The gifski instance pointer - **frame_num** (int) - Required - The sequential frame number - **width** (int) - Required - Width of the image - **height** (int) - Required - Height of the image - **pixels** (unsigned char*) - Required - Pointer to RGBA pixel data - **pts** (double) - Required - Presentation timestamp in seconds ### Response - **GifskiError** (enum) - Returns GIFSKI_OK on success, otherwise an error code. --- ## [FUNCTION] gifski_add_frame_rgba_stride ### Description Adds an RGBA frame with a custom stride (bytes per row) for memory alignment. ### Parameters - **g** (gifski*) - Required - The gifski instance pointer - **frame_num** (int) - Required - The sequential frame number - **width** (int) - Required - Width of the image - **height** (int) - Required - Height of the image - **bytes_per_row** (int) - Required - Number of bytes per row - **pixels** (unsigned char*) - Required - Pointer to pixel data - **pts** (double) - Required - Presentation timestamp ``` -------------------------------- ### Swift/macOS: Gifski Class for CGImage to GIF Conversion Source: https://context7.com/sindresorhus/gifski/llms.txt Encode CGImage frames into GIF data using the Swift Gifski class. Supports custom dimensions, quality, loop count, and progress callbacks. Outputs Data. ```swift import Foundation import CoreGraphics // Initialize Gifski encoder let gifski = try Gifski( dimensions: (width: 640, height: 480), // nil for original size quality: 0.9, // 0.1 to 1.0 loop: .forever, // .forever, .never, .count(n) fast: false // true for faster encoding ) // Optional: Set progress callback gifski.onProgress = { print("Frame processed") } // Add frames with automatic frame numbering for (index, image) in images.enumerated() { let pts = Double(index) / 30.0 // 30 FPS try gifski.addFrame(image, presentationTimestamp: pts) } // Or add frames with explicit frame numbers (for out-of-order) try gifski.addFrame(image, frameNumber: 5, presentationTimestamp: 0.167) // Finish encoding and get GIF data let gifData: Data = try gifski.finish() // Save to file try gifData.write(to: URL(fileURLWithPath: "output.gif")) ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.