heic.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. package main
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "image"
  8. "image/color"
  9. "io"
  10. )
  11. const heifBoxHeaderSize = int64(8)
  12. var heicBrand = []byte("heic")
  13. var heicPict = []byte("pict")
  14. type heifDimensionsData struct {
  15. Width, Height int64
  16. }
  17. func (d *heifDimensionsData) IsFilled() bool {
  18. return d.Width > 0 && d.Height > 0
  19. }
  20. func heifReadBoxHeader(r io.Reader) (boxType string, boxDataSize int64, err error) {
  21. b := make([]byte, heifBoxHeaderSize)
  22. _, err = r.Read(b)
  23. if err != nil {
  24. return
  25. }
  26. boxDataSize = int64(binary.BigEndian.Uint32(b[0:4])) - heifBoxHeaderSize
  27. boxType = string(b[4:8])
  28. return
  29. }
  30. func heifReadBoxData(r io.Reader, boxDataSize int64) (b []byte, err error) {
  31. b = make([]byte, boxDataSize)
  32. _, err = r.Read(b)
  33. return
  34. }
  35. func heifReadFtyp(r io.Reader, boxDataSize int64) error {
  36. if boxDataSize < 8 {
  37. return errors.New("Invalid ftyp data")
  38. }
  39. data, err := heifReadBoxData(r, boxDataSize)
  40. if err != nil {
  41. return err
  42. }
  43. if bytes.Equal(data[0:4], heicBrand) {
  44. return nil
  45. }
  46. if boxDataSize >= 12 {
  47. for i := int64(8); i < boxDataSize; i += 4 {
  48. if bytes.Equal(data[i:i+4], heicBrand) {
  49. return nil
  50. }
  51. }
  52. }
  53. return errors.New("Image is not compatible with heic")
  54. }
  55. func heifReadMeta(d *heifDimensionsData, r io.Reader, boxDataSize int64) error {
  56. if boxDataSize < 4 {
  57. return errors.New("Invalid meta data")
  58. }
  59. if _, err := r.Read(make([]byte, 4)); err != nil {
  60. return err
  61. }
  62. if boxDataSize > 4 {
  63. if err := heifReadBoxes(d, io.LimitReader(r, boxDataSize-4)); err != nil && err != io.EOF {
  64. return err
  65. }
  66. }
  67. return nil
  68. }
  69. func heifReadHldr(r io.Reader, boxDataSize int64) error {
  70. if boxDataSize < 12 {
  71. return errors.New("Invalid hdlr data")
  72. }
  73. data, err := heifReadBoxData(r, boxDataSize)
  74. if err != nil {
  75. return err
  76. }
  77. if !bytes.Equal(data[8:12], heicPict) {
  78. return fmt.Errorf("Invalid handler. Expected: pict, actual: %s", data[8:12])
  79. }
  80. return nil
  81. }
  82. func heifReadIspe(r io.Reader, boxDataSize int64) (w, h int64, err error) {
  83. if boxDataSize < 12 {
  84. return 0, 0, errors.New("Invalid ispe data")
  85. }
  86. data, err := heifReadBoxData(r, boxDataSize)
  87. if err != nil {
  88. return 0, 0, err
  89. }
  90. w = int64(binary.BigEndian.Uint32(data[4:8]))
  91. h = int64(binary.BigEndian.Uint32(data[8:12]))
  92. return
  93. }
  94. func heifReadBoxes(d *heifDimensionsData, r io.Reader) error {
  95. for {
  96. boxType, boxDataSize, err := heifReadBoxHeader(r)
  97. if err != nil {
  98. return err
  99. }
  100. if boxDataSize < 0 {
  101. return errors.New("Invalid box data")
  102. }
  103. // log.Printf("Box type: %s; Box data size: %d", boxType, boxDataSize)
  104. switch boxType {
  105. case "ftyp":
  106. if err := heifReadFtyp(r, boxDataSize); err != nil {
  107. return err
  108. }
  109. case "meta":
  110. if err := heifReadMeta(d, r, boxDataSize); err != nil {
  111. return err
  112. }
  113. if !d.IsFilled() {
  114. return errors.New("Dimensions data wan't found in meta box")
  115. }
  116. return nil
  117. case "hdlr":
  118. if err := heifReadHldr(r, boxDataSize); err != nil {
  119. return nil
  120. }
  121. case "iprp", "ipco":
  122. if err := heifReadBoxes(d, io.LimitReader(r, boxDataSize)); err != nil && err != io.EOF {
  123. return err
  124. }
  125. case "ispe":
  126. w, h, err := heifReadIspe(r, boxDataSize)
  127. if err != nil {
  128. return err
  129. }
  130. if w > d.Width || h > d.Height {
  131. d.Width, d.Height = w, h
  132. }
  133. case "mdat":
  134. return errors.New("mdat box occurred before meta box")
  135. default:
  136. if _, err := heifReadBoxData(r, boxDataSize); err != nil {
  137. return err
  138. }
  139. }
  140. }
  141. }
  142. func heifDecodeConfig(r io.Reader) (image.Config, error) {
  143. d := new(heifDimensionsData)
  144. if err := heifReadBoxes(d, r); err != nil && !d.IsFilled() {
  145. return image.Config{}, err
  146. }
  147. return image.Config{
  148. ColorModel: color.NRGBAModel,
  149. Width: int(d.Width),
  150. Height: int(d.Height),
  151. }, nil
  152. }
  153. func heifDecode(r io.Reader) (image.Image, error) {
  154. return image.NewRGBA(image.Rect(0, 0, 1, 1)), nil
  155. }
  156. func init() {
  157. image.RegisterFormat("heic", "????ftyp", heifDecode, heifDecodeConfig)
  158. }