ico.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. package vips
  2. /*
  3. #include "vips.h"
  4. */
  5. import "C"
  6. import (
  7. "bytes"
  8. "encoding/binary"
  9. "errors"
  10. "fmt"
  11. "unsafe"
  12. "github.com/imgproxy/imgproxy/v3/imagedata"
  13. "github.com/imgproxy/imgproxy/v3/imagemeta"
  14. "github.com/imgproxy/imgproxy/v3/imagetype"
  15. )
  16. func (img *Image) loadIco(data []byte, shrink int, scale float64, pages int) error {
  17. icoMeta, err := imagemeta.DecodeIcoMeta(bytes.NewReader(data))
  18. if err != nil {
  19. return err
  20. }
  21. offset := icoMeta.BestImageOffset()
  22. size := icoMeta.BestImageSize()
  23. internalData := data[offset : offset+size]
  24. var internalType imagetype.Type
  25. meta, err := imagemeta.DecodeMeta(bytes.NewReader(internalData))
  26. if err != nil {
  27. // Looks like it's BMP with an incomplete header
  28. d, err := icoFixBmpHeader(internalData)
  29. if err != nil {
  30. return err
  31. }
  32. return img.loadBmp(d, false)
  33. }
  34. internalType = meta.Format()
  35. if internalType == imagetype.ICO || !SupportsLoad(internalType) {
  36. return fmt.Errorf("Can't load %s from ICO", internalType)
  37. }
  38. imgdata := imagedata.ImageData{
  39. Type: internalType,
  40. Data: internalData,
  41. }
  42. return img.Load(&imgdata, shrink, scale, pages)
  43. }
  44. func (img *Image) saveAsIco() (*imagedata.ImageData, error) {
  45. if img.Width() > 256 || img.Height() > 256 {
  46. return nil, errors.New("Image dimensions is too big. Max dimension size for ICO is 256")
  47. }
  48. var ptr unsafe.Pointer
  49. imgsize := C.size_t(0)
  50. defer func() {
  51. C.g_free_go(&ptr)
  52. }()
  53. if C.vips_pngsave_go(img.VipsImage, &ptr, &imgsize, 0, 0, 256) != 0 {
  54. return nil, Error()
  55. }
  56. b := ptrToBytes(ptr, int(imgsize))
  57. buf := new(bytes.Buffer)
  58. buf.Grow(22 + int(imgsize))
  59. // ICONDIR header
  60. if _, err := buf.Write([]byte{0, 0, 1, 0, 1, 0}); err != nil {
  61. return nil, err
  62. }
  63. // ICONDIRENTRY
  64. if _, err := buf.Write([]byte{
  65. byte(img.Width() % 256),
  66. byte(img.Height() % 256),
  67. }); err != nil {
  68. return nil, err
  69. }
  70. // Number of colors. Not supported in our case
  71. if err := buf.WriteByte(0); err != nil {
  72. return nil, err
  73. }
  74. // Reserved
  75. if err := buf.WriteByte(0); err != nil {
  76. return nil, err
  77. }
  78. // Color planes. Always 1 in our case
  79. if _, err := buf.Write([]byte{1, 0}); err != nil {
  80. return nil, err
  81. }
  82. // Bits per pixel
  83. if img.HasAlpha() {
  84. if _, err := buf.Write([]byte{32, 0}); err != nil {
  85. return nil, err
  86. }
  87. } else {
  88. if _, err := buf.Write([]byte{24, 0}); err != nil {
  89. return nil, err
  90. }
  91. }
  92. // Image data size
  93. if err := binary.Write(buf, binary.LittleEndian, uint32(imgsize)); err != nil {
  94. return nil, err
  95. }
  96. // Image data offset. Always 22 in our case
  97. if _, err := buf.Write([]byte{22, 0, 0, 0}); err != nil {
  98. return nil, err
  99. }
  100. if _, err := buf.Write(b); err != nil {
  101. return nil, err
  102. }
  103. imgdata := imagedata.ImageData{
  104. Type: imagetype.ICO,
  105. Data: buf.Bytes(),
  106. }
  107. return &imgdata, nil
  108. }
  109. func icoFixBmpHeader(b []byte) ([]byte, error) {
  110. buf := new(bytes.Buffer)
  111. fileSize := uint32(14 + len(b))
  112. buf.Grow(int(fileSize))
  113. buf.WriteString("BM")
  114. if err := binary.Write(buf, binary.LittleEndian, &fileSize); err != nil {
  115. return nil, err
  116. }
  117. reserved := uint32(0)
  118. if err := binary.Write(buf, binary.LittleEndian, &reserved); err != nil {
  119. return nil, err
  120. }
  121. colorUsed := binary.LittleEndian.Uint32(b[32:36])
  122. bitCount := binary.LittleEndian.Uint16(b[14:16])
  123. var pixOffset uint32
  124. if colorUsed == 0 && bitCount <= 8 {
  125. pixOffset = 14 + 40 + 4*(1<<bitCount)
  126. } else {
  127. pixOffset = 14 + 40 + 4*colorUsed
  128. }
  129. if err := binary.Write(buf, binary.LittleEndian, &pixOffset); err != nil {
  130. return nil, err
  131. }
  132. // Write size and width
  133. buf.Write(b[:8])
  134. // For some reason ICO stores double height
  135. height := binary.LittleEndian.Uint32(b[8:12]) / 2
  136. if err := binary.Write(buf, binary.LittleEndian, &height); err != nil {
  137. return nil, err
  138. }
  139. // Write the rest
  140. buf.Write(b[12:])
  141. return buf.Bytes(), nil
  142. }