classdef GifWriter < handle %GIFWRITER Simple class to create GIFs from figures (parallel-safe) % % Example: % g = GifWriter('Name','mySim','Parallel',true); % parfor i = 1:10 % plot(rand(10,1)); % g.addFrame(1,i); % end % g.compile(1); properties Name (1,:) char = 'default' % GIF base name DelayTime (1,1) double = 0.1 % Frame delay in seconds Parallel (1,1) logical = false % Enable parallel-safe mode BaseDir (1,:) char % Base directory for temp frames OutputDir (1,:) char % Final GIF output directory end methods %% Constructor function obj = GifWriter(varargin) % Parse name/value pairs p = inputParser; addParameter(p, 'Name', 'default', @ischar); addParameter(p, 'DelayTime', 0.1, @isnumeric); addParameter(p, 'Parallel', false, @islogical); addParameter(p, 'OutputDir', fullfile(pwd, 'gif_output'), @ischar); parse(p, varargin{:}); obj.Name = p.Results.Name; obj.DelayTime = p.Results.DelayTime; obj.Parallel = p.Results.Parallel; obj.OutputDir = p.Results.OutputDir; obj.BaseDir = fullfile(obj.OutputDir, 'tmp', obj.Name); if ~exist(obj.BaseDir, 'dir'), mkdir(obj.BaseDir); end if ~exist(obj.OutputDir, 'dir'), mkdir(obj.OutputDir); end end %% function addFrame(obj, figInput, pos) %ADDFRAME Add a figure frame to the GIF (supports parallel mode) % % Usage: % obj.addFrame(figHandle) % obj.addFrame(figNum) % obj.addFrame(figHandle, pos) % parallel mode % obj.addFrame(figNum, pos) % % In parallel mode, 'pos' must be a unique integer (loop index). if nargin < 3, pos = []; end % --- Resolve figure handle --- if isnumeric(figInput) % User passed a figure number if ~ishandle(figInput) warning('GifWriter:addFrame', 'Figure %d not found.', figInput); return; end figHandle = figure(figInput); elseif isa(figInput, 'matlab.ui.Figure') figHandle = figInput; else error('GifWriter:addFrame:InvalidInput', ... 'Input must be a figure handle or figure number.'); end % --- Parallel-safe frame writing --- if obj.Parallel if isempty(pos) error('GifWriter:ParallelMode', ... 'In parallel mode, provide a unique ''pos'' identifier.'); end % Directory for this figure number frameDir = fullfile(obj.BaseDir, sprintf('fig_%d', figHandle.Number)); if ~exist(frameDir, 'dir') mkdir(frameDir); end % File path for this frame frameFile = fullfile(frameDir, sprintf('frame_%05d.png', pos)); % Export to PNG (headless-safe) exportgraphics(figHandle, frameFile, 'Resolution', 150); else % --- Serial mode: append directly to GIF --- gifFile = fullfile(obj.OutputDir, ... sprintf('%s_fig_%d.gif', obj.Name, figHandle.Number)); % Export frame temporarily tmpFile = [tempname, '.png']; exportgraphics(figHandle, tmpFile, 'Resolution', 150); img = imread(tmpFile); delete(tmpFile); % Append to GIF [A, map] = rgb2ind(img, 256); if ~isfile(gifFile) imwrite(A, map, gifFile, 'gif', ... 'LoopCount', Inf, 'DelayTime', obj.DelayTime); else imwrite(A, map, gifFile, 'gif', ... 'WriteMode', 'append', 'DelayTime', obj.DelayTime); end end end %% Compile all PNGs into a GIF (and clean up) function compile(obj, fignum) figDir = fullfile(obj.BaseDir, sprintf('fig_%d', fignum)); gifFile = fullfile(obj.OutputDir, sprintf('%s_fig_%d.gif', obj.Name, fignum)); frames = dir(fullfile(figDir, 'frame_*.png')); if isempty(frames) warning('GifWriter:NoFrames', 'No frames found for figure %d.', fignum); return; end % Sort by frame name [~, idx] = sort({frames.name}); frames = frames(idx); % Combine into a GIF for i = 1:numel(frames) img = imread(fullfile(frames(i).folder, frames(i).name)); [A, map] = rgb2ind(img, 256); if i == 1 imwrite(A, map, gifFile, 'gif', ... 'LoopCount', Inf, 'DelayTime', obj.DelayTime); else imwrite(A, map, gifFile, 'gif', ... 'WriteMode', 'append', 'DelayTime', obj.DelayTime); end end % Clean up temporary frames rmdir(figDir, 's'); end end end