Wow - I don’t even remember ever seeing that post!
Will take a look at that now
Thanks for pointing that out Michel
Wow - I don’t even remember ever seeing that post!
Will take a look at that now
Thanks for pointing that out Michel
I ran into some issues using Norman’s algorithm, and found 3 small changes made it work:
Revised code here:
Public Function MakeICOfromPicture(p as Picture) as MemoryBlock
' adatpted from https://forum.xojo.com/16314-create-a-ico-file?search=%22ico+exporter%22
' see also https://en.wikipedia.org/wiki/Favicon
' for standard web favicon.ico one should use 16x16, 32x32 and 48x48 (and possibly 64x64 says wikipedia)
if p = nil then
return nil
end if
'ICONDIR structure
'Offset# Size (in bytes) Purpose
'0 2 Reserved. Must always be 0.
'2 2 Specifies image type: 1 for icon (.ICO) image, 2 for cursor (.CUR) image. Other values are invalid.
'4 2 Specifies number of images in the file.
'Structure of image directory
'
'Image #1 Entry for the first image
'Image #2 Entry for the second image
'...
'Image #n Entry for the last image
'
'
'Image entry
'ICONDIRENTRY structure
'Offset# Size (in bytes) Purpose
'0 1 Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels.
'1 1 Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels.
'2 1 Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette.
'3 1 Reserved. Should be 0.[Notes 2]
'4 2 In ICO format: Specifies color planes. Should be 0 or 1.[Notes 3]
'In CUR format: Specifies the horizontal coordinates of the hotspot in number of pixels from the left.
'
'6 2 In ICO format: Specifies bits per pixel. [Notes 4]
'In CUR format: Specifies the vertical coordinates of the hotspot in number of pixels from the top.
'
'8 4 Specifies the size of the image's data in bytes
'12 4 Specifies the offset of BMP or PNG data from the beginning of the ICO/CUR file
' downsize master picture to each smaller size
dim sizes() as integer= array(16,32,48,64)
dim images() as Picture
dim patchAddresses() as int32
// downsize to needed sizes
for each size as integer in sizes
dim p2 as new Picture(size,size)
p2.graphics.drawPicture(p,0,0,p2.width,p2.height,0,0,p.width,p.height)
images.append p2
next
' write the header
Dim mb as New MemoryBlock(0)
Dim bs as New BinaryStream(mb)
bs.LittleEndian = true
dim offset as integer
bs.WriteInt16 0 ' Reserved. Must always be 0.
offset = offset + 2
bs.WriteInt16 1 ' Specifies image type: 1 for icon (.ICO)
offset = offset + 2
bs.WriteInt16 ubound(images)+1 ' Specifies number of images in the file.
offset = offset + 2
' write the catalog
for each img as PIcture in images
bs.WriteInt8 img.Width
offset = offset + 1
bs.WriteInt8 img.Height
offset = offset + 1
bs.WriteInt8 0 ' # of colors in color palette (0)
offset = offset + 1
bs.WriteInt8 0 ' reserved must always be zero
offset = offset + 1
//bs.WriteInt16 0 ' # of color planes
bs.WriteInt16 1 ' # of color planes (1 seems more standard)
offset = offset + 2
bs.WriteInt16 32 ' bits per pixel (supposedly can be zero, since we are using PNG, but 32 seems required)
offset = offset + 2
dim tmpmb as memoryblock = img.GetData( picture.FormatPNG )
bs.WriteInt32 tmpmb.Size
offset = offset + 4
bs.WriteInt32 0
// hang on to this position as we need to back patch it as we spit out images
patchAddresses.append offset
offset = offset + 4
next
dim i as integer
for each img as picture in images
mb.Int32Value(patchAddresses(i)) = offset
i = i + 1
dim tmpmb as memoryblock = img.GetData( picture.FormatPNG )
bs.Write tmpmb.StringValue(0, tmpmb.Size)
offset = offset + tmpmb.Size
next
bs.Close
return mb
End Function