ico.go 3.7 KB

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