function Imag2Ico(icon, map, mask, filename)
%Imag2Ico converts bitmaps to a WIN32 icon and writes it to a file.
%
% Imag2Ico(BITMAP, MAP, MASK, FILENAME); BITMAP and MASK are cell
% arrays of equal length. MAP is a cell array of arbitrary length > 0.
% BITMAP are indexed and may be rectangular. Numeric input can be DOUBLE
% or UINT8. If BITMAP is DOUBLE, the pixels may range from 1 to
% SIZE(MAP, 1); if BITMAP is UINT8, the pixels may range from 0 to
% SIZE(MAP, 1) - 1. MAP must contain at least one colormap. If there are
% fewer colormaps than bitmaps, the last colormap will be copied as
% often as necessary. If MASK is empty it is filled with zeros. A zero
% mask pixel is opaque, a 1 mask pixel is transparent.
%
% Examples for how to create an icon:
%
% Adobe Photoshop Elements
% ------------------------
% New; 32 pixel, 32 pixel, 2 pixel/cm, RGB-color; pencil 1 pixel; tear
% off color palette; draw image; save as GIF, exact, primary colors,
% normal. The GIF-image is UINT8, indexed, the colormap is complemented
% with zeros to a power of 2. The first eight colors, 0 (white) to 7
% (black) are colors of 1s and 0s, the other colors are fractions
% between 0 and 1 as needed. In Matlab read as:
% [gifimage, gifmap] = imread('filename.gif');
% Imag2Ico({gifbild}, {gifmap}, {}, 'filename.ico').
%
% ACD Foto Canvas
% ---------------
% New, 32, 32, 8bpp, zoom in, pencil, view, tool options, line width 1;
% select primary and secondary colors with the left and the right mouse
% button, draw with both buttons. Save as BMP. The bitmap is indexed
% UINT8, the colormap is 216 lines of colors and 40 lines of zeros. In
% Matlab read as:
% [bmpimage, bmpmap] = imread('filename.bmp');
% Imag2Ico({bmpimage}, {bmpmap}, {}, 'filename.ico').
%
% 48 x 48, 96 dpi, works fine for desktop icons. PAINT works also.
%
% See also: imread
% Background info: http://www.iconolog.net/info/icoFormat.html.
% PMWNave@yahoo.de
% 2004-12-10, started.
% 2006-11-03, cosmetics, comments.
%----------------------------------------------------------------------O
%
% Check and complement the input arguments.
%
if nargin ~= 4,
error(' ### %s requires exactly four input arguments.', mfilename)
end
imcount = length(icon);
if imcount == 0,
error(' ### %s needs at least one input bitmap.', mfilename)
end
dm = length(map);
if dm == 0,
error(' ### %s needs (a) colormap(s).', mfilename)
end
if dm < imcount, % pad maps.
for ii = dm + 1:imcount,
map{ii} = map{ii - 1};
end
end
dm = length(mask);
for ii = dm + 1:imcount,
mask{ii} = zeros(size(icon{ii}));
end
for ii = 1:dm,
if ~isequal(size(icon{ii}), size(mask{ii})),
mask{ii} = zeros(size(icon{ii}));
end
end
%
% Open the output file.
%
[dpath, dfilename, dm] = fileparts(filename); %#ok.
filename = fullfile(dpath, [dfilename, '.ico']);
clear dpath dfilename dm
[fid, mess] = fopen(filename, 'w', 'ieee-le'); % little-endian.
if fid == -1,
error([' ### %s could not open the output file \n ', ...
'%s\n %s', mfilename, filename, mess])
end
%
% Write the signature and the number of icons to write.
%
fwrite(fid, [0, 1, imcount], 'uint16');
%
% Write all directories. Loop over all icons.
%
resourceoffset = imcount * 16 + 6; % total length of directories.
cmapsize = zeros(1, imcount);
ipaddedwidth = zeros(1, imcount);
mpaddedwidth = zeros(1, imcount);
bitmapsize = zeros(1, imcount);
resourcesize = zeros(1, imcount);
masksize = zeros(1, imcount);
dessize = zeros(1, imcount);
bitdepth = zeros(1, imcount);
height = zeros(1, imcount);
width = zeros(1, imcount);
for p = 1:imcount,
plane = icon{p};
bool = IsNotIndexed(plane);
if bool, % not an indexed bitmap.
fclose(fid);
delete(filename);
error(' ### %s: bitmap %d is not an indexed image.', ...
mfilename, p)
end
[height(p), width(p)] = size(plane);
%
% Make sure that the length of the colormap is one of these: 2^1, 2^4,
% or 2^8. --- 2^1 does not seem to work.
%
dmap = map{p};
dm = size(dmap, 1);
if dm <= 2,
bitdepth(p) = 4; %1; % <<===<<
elseif dm <= 16,
bitdepth(p) = 4;
else
bitdepth(p) = 8;
end
dessize(p) = 2^bitdepth(p);
numofcolors = dessize(p); % not used by Matlab but may be
if numofcolors > 16, % required by other
numofcolors = 0; % applications.
end
if dm < dessize(p),
map{p} = [dmap; zeros(dessize(p) - dm, 3)];
elseif dm > dessize(p),
map{p} = dmap(1:dessize(p), :);
end
headersize = 40; % 40 is minimum, may be larger.
cmapsize(p) = dessize(p) * 4;
ipaddedwidth(p) = ceil(width(p) / 8) * 8; % for icons.
mpaddedwidth(p) = ceil(width(p) / 32) * 32; % for masks.
bitmapsize(p) = ipaddedwidth(p) * height(p) * bitdepth(p) / 8;
masksize(p) = mpaddedwidth(p) * height(p) / 8;
resourcesize(p) = headersize + cmapsize(p) + ...
bitmapsize(p) + masksize(p);
fwrite(fid, [width(p), height(p), numofcolors, zeros(1, 5)], ...
'uint8');
fwrite(fid, [resourcesize(p), resourceoffset], 'uint32');
resourceoffset = resourceoffset + resourcesize(p);
end
%
% Write the resources.
%
for p = 1:imcount,
plane = icon{p};
%
% Bitmap header. The two longwords following HEADERSIZE are not used
% by Matlab but are required by many other applications.
%
fwrite(fid, [headersize, width(p), height(p) * 2], 'uint32');
%
% NUMPLANES = number of planes; details of purpose not known.
%
numplanes = 1;
fwrite(fid, [numplanes, bitdepth(p), 0, 0], 'uint16');
fwrite(fid, [bitmapsize(p) + masksize(p), 0, 0, 0, 0], 'uint32');
%
% Color map.
%
dmap = map{p};
dmap = ([fliplr(floor(dmap * 255)), zeros(dessize(p), 1)])';
fwrite(fid, dmap(:), 'uint8');
%
% Bitmap.
%
if isa(plane, 'double'),
dicon = plane - 1; % <<===<<
else
dicon = double(plane);
end
if any(dicon < 0),
fclose(fid);
delete(filename)
error(' ### %s: illegal pixel(s) in bitmap no. %d', ...
mfilename, p) % also catches true color images.
end
dicon(dicon > dessize(p) - 1) = dessize(p) - 1;
if width(p) ~= ipaddedwidth(p),
dicon = [dicon, zeros(height(p), ...
ipaddedwidth(p) - width(p))]; %#ok.
end
dicon = rot90(dicon, -1); % rotate 90 degrees clockwise.
if bitdepth(p) == 1,
fwrite(fid, dicon(:), 'ubit1');
elseif bitdepth(p) == 4,
dicon = dicon(2:2:end) + dicon(1:2:end) * 16;
fwrite(fid, dicon(:), 'uint8');
else
fwrite(fid, dicon(:), 'uint8');
end
dmask = double(mask{p});
if width(p) ~= mpaddedwidth(p),
dmask = [dmask, zeros(height(p), ...
mpaddedwidth(p) - width(p))]; %#ok.
end
dmask = reshape(dmask, 8 * height(p), mpaddedwidth(p) / 8);
dmask = fliplr(dmask);
dmask = reshape(dmask, height(p), mpaddedwidth(p));
dmask = rot90(flipud(dmask));
fwrite(fid, dmask(:), 'ubit1');
end
fclose(fid);
end
function bool = IsNotIndexed(iniarg)
bool = true;
if ndims(iniarg) ~= 2, %#ok.
return
end
if isa(iniarg, 'logical'),
iniarg = double(iniarg);
end
if (isa(iniarg, 'uint8') || isa(iniarg, 'uint16')),
bool = false;
return
end
if any(iniarg ~= floor(iniarg)) | any(iniarg < 1), %#ok.
return
end
bool = false;
end
% EOF Imag2Ico.