ico.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package imagemeta
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "io"
  6. )
  7. type IcoMeta struct {
  8. Meta
  9. offset int
  10. size int
  11. }
  12. func (m *IcoMeta) BestImageOffset() int {
  13. return m.offset
  14. }
  15. func (m *IcoMeta) BestImageSize() int {
  16. return m.size
  17. }
  18. func icoBestSize(r io.Reader) (width, height byte, offset uint32, size uint32, err error) {
  19. var tmp [16]byte
  20. if _, err = io.ReadFull(r, tmp[:6]); err != nil {
  21. return
  22. }
  23. count := binary.LittleEndian.Uint16(tmp[4:6])
  24. for i := uint16(0); i < count; i++ {
  25. if _, err = io.ReadFull(r, tmp[:]); err != nil {
  26. return
  27. }
  28. if tmp[0] > width || tmp[1] > height || tmp[0] == 0 || tmp[1] == 0 {
  29. width = tmp[0]
  30. height = tmp[1]
  31. size = binary.LittleEndian.Uint32(tmp[8:12])
  32. offset = binary.LittleEndian.Uint32(tmp[12:16])
  33. }
  34. }
  35. return
  36. }
  37. func BestIcoPage(r io.Reader) (int, int, error) {
  38. _, _, offset, size, err := icoBestSize(r)
  39. return int(offset), int(size), err
  40. }
  41. func DecodeIcoMeta(r io.Reader) (*IcoMeta, error) {
  42. bwidth, bheight, offset, size, err := icoBestSize(r)
  43. if err != nil {
  44. return nil, err
  45. }
  46. width := int(bwidth)
  47. height := int(bheight)
  48. if width == 0 {
  49. width = 256
  50. }
  51. if height == 0 {
  52. height = 256
  53. }
  54. return &IcoMeta{
  55. Meta: &meta{
  56. format: "ico",
  57. width: width,
  58. height: height,
  59. },
  60. offset: int(offset),
  61. size: int(size),
  62. }, nil
  63. }
  64. func init() {
  65. RegisterFormat(
  66. "\x00\x00\x01\x00",
  67. func(r io.Reader) (Meta, error) { return DecodeIcoMeta(r) },
  68. )
  69. }
  70. // FixBmpHeader fixes an incomplete header of BMP stored in ICO
  71. func FixBmpHeader(b []byte) ([]byte, error) {
  72. buf := new(bytes.Buffer)
  73. fileSize := uint32(14 + len(b))
  74. buf.Grow(int(fileSize))
  75. buf.Write(bmpMagick)
  76. if err := binary.Write(buf, binary.LittleEndian, &fileSize); err != nil {
  77. return nil, err
  78. }
  79. reserved := uint32(0)
  80. if err := binary.Write(buf, binary.LittleEndian, &reserved); err != nil {
  81. return nil, err
  82. }
  83. colorUsed := binary.LittleEndian.Uint32(b[32:36])
  84. bitCount := binary.LittleEndian.Uint16(b[14:16])
  85. var pixOffset uint32
  86. if colorUsed == 0 && bitCount <= 8 {
  87. pixOffset = 14 + 40 + 4*(1<<bitCount)
  88. } else {
  89. pixOffset = 14 + 40 + 4*colorUsed
  90. }
  91. if err := binary.Write(buf, binary.LittleEndian, &pixOffset); err != nil {
  92. return nil, err
  93. }
  94. // Write size and width
  95. buf.Write(b[:8])
  96. // For some reason ICO stores double height
  97. height := binary.LittleEndian.Uint32(b[8:12]) / 2
  98. if err := binary.Write(buf, binary.LittleEndian, &height); err != nil {
  99. return nil, err
  100. }
  101. // Write the rest
  102. buf.Write(b[12:])
  103. return buf.Bytes(), nil
  104. }