MBS Plugins and .ico creation?

Wow - I don’t even remember ever seeing that post!
Will take a look at that now :slight_smile:

Thanks for pointing that out Michel :slight_smile:

I ran into some issues using Norman’s algorithm, and found 3 small changes made it work:

  • set the # of color planes to 1 (rather than 0)
  • set the bits per pixel to 32 (rather than 0)
  • set the BinaryStream to littleEndian.

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