reader.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. package ico
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. "image"
  7. "image/color"
  8. "image/draw"
  9. "image/png"
  10. "io"
  11. bmp "github.com/jsummers/gobmp"
  12. )
  13. func init() {
  14. image.RegisterFormat("ico", "\x00\x00\x01\x00?????\x00", Decode, DecodeConfig)
  15. }
  16. // ---- public ----
  17. func Decode(r io.Reader) (image.Image, error) {
  18. var d decoder
  19. if err := d.decode(r); err != nil {
  20. return nil, err
  21. }
  22. return d.images[0], nil
  23. }
  24. func DecodeAll(r io.Reader) ([]image.Image, error) {
  25. var d decoder
  26. if err := d.decode(r); err != nil {
  27. return nil, err
  28. }
  29. return d.images, nil
  30. }
  31. func DecodeConfig(r io.Reader) (image.Config, error) {
  32. var (
  33. d decoder
  34. cfg image.Config
  35. err error
  36. )
  37. if err = d.decodeHeader(r); err != nil {
  38. return cfg, err
  39. }
  40. if err = d.decodeEntries(r); err != nil {
  41. return cfg, err
  42. }
  43. e := d.entries[0]
  44. buf := make([]byte, e.Size+14)
  45. n, err := io.ReadFull(r, buf[14:])
  46. if err != nil && err != io.ErrUnexpectedEOF {
  47. return cfg, err
  48. }
  49. buf = buf[:14+n]
  50. if n > 8 && bytes.Equal(buf[14:22], pngHeader) {
  51. return png.DecodeConfig(bytes.NewReader(buf[14:]))
  52. }
  53. d.forgeBMPHead(buf, &e)
  54. return bmp.DecodeConfig(bytes.NewReader(buf))
  55. }
  56. // ---- private ----
  57. type direntry struct {
  58. Width byte
  59. Height byte
  60. Palette byte
  61. _ byte
  62. Plane uint16
  63. Bits uint16
  64. Size uint32
  65. Offset uint32
  66. }
  67. type head struct {
  68. Zero uint16
  69. Type uint16
  70. Number uint16
  71. }
  72. type decoder struct {
  73. head head
  74. entries []direntry
  75. images []image.Image
  76. }
  77. func (d *decoder) decode(r io.Reader) (err error) {
  78. if err = d.decodeHeader(r); err != nil {
  79. return err
  80. }
  81. if err = d.decodeEntries(r); err != nil {
  82. return err
  83. }
  84. d.images = make([]image.Image, d.head.Number)
  85. for i := range d.entries {
  86. e := &(d.entries[i])
  87. data := make([]byte, e.Size+14)
  88. n, err := io.ReadFull(r, data[14:])
  89. if err != nil && err != io.ErrUnexpectedEOF {
  90. return err
  91. }
  92. data = data[:14+n]
  93. if n > 8 && bytes.Equal(data[14:22], pngHeader) { // decode as PNG
  94. if d.images[i], err = png.Decode(bytes.NewReader(data[14:])); err != nil {
  95. return err
  96. }
  97. } else { // decode as BMP
  98. maskData := d.forgeBMPHead(data, e)
  99. if maskData != nil {
  100. data = data[:n+14-len(maskData)]
  101. }
  102. if d.images[i], err = bmp.Decode(bytes.NewReader(data)); err != nil {
  103. return err
  104. }
  105. bounds := d.images[i].Bounds()
  106. mask := image.NewAlpha(image.Rect(0, 0, bounds.Dx(), bounds.Dy()))
  107. masked := image.NewNRGBA(image.Rect(0, 0, bounds.Dx(), bounds.Dy()))
  108. for row := 0; row < int(e.Height); row++ {
  109. for col := 0; col < int(e.Width); col++ {
  110. if maskData != nil {
  111. rowSize := (int(e.Width) + 31) / 32 * 4
  112. if (maskData[row*rowSize+col/8]>>(7-uint(col)%8))&0x01 != 1 {
  113. mask.SetAlpha(col, int(e.Height)-row-1, color.Alpha{255})
  114. }
  115. } else { // 32-Bit
  116. rowSize := (int(e.Width)*32 + 31) / 32 * 4
  117. offset := int(binary.LittleEndian.Uint32(data[10:14]))
  118. mask.SetAlpha(col, int(e.Height)-row-1, color.Alpha{data[offset+row*rowSize+col*4+3]})
  119. }
  120. }
  121. }
  122. draw.DrawMask(masked, masked.Bounds(), d.images[i], bounds.Min, mask, bounds.Min, draw.Src)
  123. d.images[i] = masked
  124. }
  125. }
  126. return nil
  127. }
  128. func (d *decoder) decodeHeader(r io.Reader) error {
  129. binary.Read(r, binary.LittleEndian, &(d.head))
  130. if d.head.Zero != 0 || d.head.Type != 1 {
  131. return fmt.Errorf("corrupted head: [%x,%x]", d.head.Zero, d.head.Type)
  132. }
  133. return nil
  134. }
  135. func (d *decoder) decodeEntries(r io.Reader) error {
  136. n := int(d.head.Number)
  137. d.entries = make([]direntry, n)
  138. for i := 0; i < n; i++ {
  139. if err := binary.Read(r, binary.LittleEndian, &(d.entries[i])); err != nil {
  140. return err
  141. }
  142. }
  143. return nil
  144. }
  145. func (d *decoder) forgeBMPHead(buf []byte, e *direntry) (mask []byte) {
  146. // See en.wikipedia.org/wiki/BMP_file_format
  147. data := buf[14:]
  148. imageSize := len(data)
  149. if e.Bits != 32 {
  150. maskSize := (int(e.Width) + 31) / 32 * 4 * int(e.Height)
  151. imageSize -= maskSize
  152. if imageSize <= 0 {
  153. return
  154. }
  155. mask = data[imageSize:]
  156. }
  157. copy(buf[0:2], "\x42\x4D") // Magic number
  158. dibSize := binary.LittleEndian.Uint32(data[:4])
  159. w := binary.LittleEndian.Uint32(data[4:8])
  160. h := binary.LittleEndian.Uint32(data[8:12])
  161. if h > w {
  162. binary.LittleEndian.PutUint32(data[8:12], h/2)
  163. }
  164. binary.LittleEndian.PutUint32(buf[2:6], uint32(imageSize)) // File size
  165. // Calculate offset into image data
  166. numColors := binary.LittleEndian.Uint32(data[32:36])
  167. bits := binary.LittleEndian.Uint16(data[14:16])
  168. switch bits {
  169. case 1, 2, 4, 8:
  170. x := uint32(1 << bits)
  171. if numColors == 0 || numColors > x {
  172. numColors = x
  173. }
  174. default:
  175. numColors = 0
  176. }
  177. var numColorsSize uint32
  178. switch dibSize {
  179. case 12, 64:
  180. numColorsSize = numColors * 3
  181. default:
  182. numColorsSize = numColors * 4
  183. }
  184. offset := 14 + dibSize + numColorsSize
  185. if dibSize > 40 && int(dibSize-4) <= len(data) {
  186. offset += binary.LittleEndian.Uint32(data[dibSize-8 : dibSize-4])
  187. }
  188. binary.LittleEndian.PutUint32(buf[10:14], offset)
  189. return
  190. }
  191. var pngHeader = []byte{'\x89', 'P', 'N', 'G', '\r', '\n', '\x1a', '\n'}