package ico import ( "bytes" "encoding/binary" "fmt" "image" "image/png" ) type IconDir struct { Reserved uint16 // must be 0 Type uint16 // 1 for ICO, 2 for CUR Count uint16 // number of images } type IconDirEntry struct { Width uint8 // 0 means 256 Height uint8 // 0 means 256 ColorCount uint8 Reserved uint8 // must be 0 ColorPlanes uint16 // 0 or 1 BitsPerPixel uint16 SizeInBytes uint32 Offset uint32 } func ImageToIco(img image.Image) ([]byte, error) { // encode image as png var pngBuf bytes.Buffer if err := png.Encode(&pngBuf, img); err != nil { return nil, fmt.Errorf("failed to encode PNG: %w", err) } pngData := pngBuf.Bytes() // get image dimensions bounds := img.Bounds() width := bounds.Dx() height := bounds.Dy() // prepare output buffer var icoBuf bytes.Buffer iconDir := IconDir{ Reserved: 0, Type: 1, // ICO format Count: 1, // One image } w := uint8(width) h := uint8(height) // width/height of 256 should be stored as 0 if width == 256 { w = 0 } if height == 256 { h = 0 } iconDirEntry := IconDirEntry{ Width: w, Height: h, ColorCount: 0, // 0 for PNG (32-bit) Reserved: 0, ColorPlanes: 1, BitsPerPixel: 32, // PNG with alpha SizeInBytes: uint32(len(pngData)), Offset: 6 + 16, // Size of ICONDIR + ICONDIRENTRY } // write IconDir if err := binary.Write(&icoBuf, binary.LittleEndian, iconDir); err != nil { return nil, fmt.Errorf("failed to write ICONDIR: %w", err) } // write IconDirEntry if err := binary.Write(&icoBuf, binary.LittleEndian, iconDirEntry); err != nil { return nil, fmt.Errorf("failed to write ICONDIRENTRY: %w", err) } // write PNG data directly if _, err := icoBuf.Write(pngData); err != nil { return nil, fmt.Errorf("failed to write PNG data: %w", err) } return icoBuf.Bytes(), nil }