bmp.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. // Based on https://cs.opensource.google/go/x/image/+/6944b10b:bmp/reader.go
  2. // and https://cs.opensource.google/go/x/image/+/6944b10b:bmp/writer.go
  3. package vips
  4. /*
  5. #include "vips.h"
  6. */
  7. import "C"
  8. import (
  9. "bytes"
  10. "encoding/binary"
  11. "errors"
  12. "io"
  13. "unsafe"
  14. "github.com/imgproxy/imgproxy/v3/imagedata"
  15. "github.com/imgproxy/imgproxy/v3/imagetype"
  16. )
  17. type bmpHeader struct {
  18. sigBM [2]byte
  19. fileSize uint32
  20. resverved [2]uint16
  21. pixOffset uint32
  22. dibHeaderSize uint32
  23. width uint32
  24. height uint32
  25. colorPlane uint16
  26. bpp uint16
  27. compression uint32
  28. imageSize uint32
  29. xPixelsPerMeter uint32
  30. yPixelsPerMeter uint32
  31. colorUse uint32
  32. colorImportant uint32
  33. }
  34. // errBmpUnsupported means that the input BMP image uses a valid but unsupported
  35. // feature.
  36. var errBmpUnsupported = errors.New("unsupported BMP image")
  37. func readUint16(b []byte) uint16 {
  38. return uint16(b[0]) | uint16(b[1])<<8
  39. }
  40. func readUint32(b []byte) uint32 {
  41. return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
  42. }
  43. func prepareBmpCanvas(width, height, bands int) (*C.VipsImage, []byte, error) {
  44. var tmp *C.VipsImage
  45. if C.vips_black_go(&tmp, C.int(width), C.int(height), C.int(bands)) != 0 {
  46. return nil, nil, Error()
  47. }
  48. data := unsafe.Pointer(C.vips_image_get_data(tmp))
  49. datalen := int(tmp.Bands) * int(tmp.Xsize) * int(tmp.Ysize)
  50. return tmp, ptrToBytes(data, datalen), nil
  51. }
  52. // decodeBmpPaletted reads an 8 bit-per-pixel BMP image from r.
  53. // If topDown is false, the image rows will be read bottom-up.
  54. func (img *Image) decodeBmpPaletted(r io.Reader, width, height, bpp int, palette []Color, topDown bool) error {
  55. tmp, imgData, err := prepareBmpCanvas(width, height, 3)
  56. if err != nil {
  57. return err
  58. }
  59. defer func() {
  60. if rerr := recover(); rerr != nil {
  61. C.clear_image(&tmp)
  62. panic(rerr)
  63. }
  64. }()
  65. // Each row is 4-byte aligned.
  66. cap := 8 / bpp
  67. b := make([]byte, ((width+cap-1)/cap+3)&^3)
  68. y0, y1, yDelta := height-1, -1, -1
  69. if topDown {
  70. y0, y1, yDelta = 0, height, +1
  71. }
  72. stride := width * 3
  73. for y := y0; y != y1; y += yDelta {
  74. if _, err = io.ReadFull(r, b); err != nil {
  75. C.clear_image(&tmp)
  76. return err
  77. }
  78. p := imgData[y*stride : (y+1)*stride]
  79. j, bit := 0, 8-bpp
  80. for i := 0; i < len(p); i += 3 {
  81. pind := (b[j] >> bit) & (1<<bpp - 1)
  82. if bit == 0 {
  83. bit = 8 - bpp
  84. j++
  85. } else {
  86. bit -= bpp
  87. }
  88. c := palette[pind]
  89. p[i+0] = c.R
  90. p[i+1] = c.G
  91. p[i+2] = c.B
  92. }
  93. }
  94. C.swap_and_clear(&img.VipsImage, tmp)
  95. var bitdepth int
  96. colors := len(palette)
  97. switch {
  98. case colors > 16:
  99. bitdepth = 8
  100. case colors > 4:
  101. bitdepth = 4
  102. case colors > 2:
  103. bitdepth = 2
  104. }
  105. img.SetInt("palette-bit-depth", bitdepth)
  106. return nil
  107. }
  108. // decodeBmpRGB reads a 24/32 bit-per-pixel BMP image from r.
  109. // If topDown is false, the image rows will be read bottom-up.
  110. func (img *Image) decodeBmpRGB(r io.Reader, width, height, bands int, topDown, noAlpha bool) error {
  111. if bands != 3 && bands != 4 {
  112. return errBmpUnsupported
  113. }
  114. imgBands := 3
  115. if bands == 4 && !noAlpha {
  116. // Create RGBA image only when source has 4 bands and the last one is alpha
  117. imgBands = 4
  118. }
  119. tmp, imgData, err := prepareBmpCanvas(width, height, imgBands)
  120. if err != nil {
  121. return err
  122. }
  123. defer func() {
  124. if rerr := recover(); rerr != nil {
  125. C.clear_image(&tmp)
  126. panic(rerr)
  127. }
  128. }()
  129. // Each row is 4-byte aligned.
  130. b := make([]byte, (bands*width+3)&^3)
  131. y0, y1, yDelta := height-1, -1, -1
  132. if topDown {
  133. y0, y1, yDelta = 0, height, +1
  134. }
  135. stride := width * imgBands
  136. for y := y0; y != y1; y += yDelta {
  137. if _, err = io.ReadFull(r, b); err != nil {
  138. C.clear_image(&tmp)
  139. return err
  140. }
  141. p := imgData[y*stride : (y+1)*stride]
  142. for i, j := 0, 0; i < len(p); i, j = i+imgBands, j+bands {
  143. // BMP images are stored in BGR order rather than RGB order.
  144. p[i+0] = b[j+2]
  145. p[i+1] = b[j+1]
  146. p[i+2] = b[j+0]
  147. if imgBands == 4 {
  148. p[i+3] = b[j+3]
  149. }
  150. }
  151. }
  152. C.swap_and_clear(&img.VipsImage, tmp)
  153. return nil
  154. }
  155. func (img *Image) loadBmp(data []byte) error {
  156. // We only support those BMP images that are a BITMAPFILEHEADER
  157. // immediately followed by a BITMAPINFOHEADER.
  158. const (
  159. fileHeaderLen = 14
  160. infoHeaderLen = 40
  161. v4InfoHeaderLen = 108
  162. v5InfoHeaderLen = 124
  163. )
  164. r := bytes.NewReader(data)
  165. var b [1024]byte
  166. if _, err := io.ReadFull(r, b[:fileHeaderLen+4]); err != nil {
  167. if err == io.EOF {
  168. err = io.ErrUnexpectedEOF
  169. }
  170. return err
  171. }
  172. if string(b[:2]) != "BM" {
  173. return errors.New("not a BMP image")
  174. }
  175. offset := readUint32(b[10:14])
  176. infoLen := readUint32(b[14:18])
  177. if infoLen != infoHeaderLen && infoLen != v4InfoHeaderLen && infoLen != v5InfoHeaderLen {
  178. return errBmpUnsupported
  179. }
  180. if _, err := io.ReadFull(r, b[fileHeaderLen+4:fileHeaderLen+infoLen]); err != nil {
  181. if err == io.EOF {
  182. err = io.ErrUnexpectedEOF
  183. }
  184. return err
  185. }
  186. width := int(int32(readUint32(b[18:22])))
  187. height := int(int32(readUint32(b[22:26])))
  188. topDown := false
  189. if height < 0 {
  190. height, topDown = -height, true
  191. }
  192. if width <= 0 || height <= 0 {
  193. return errBmpUnsupported
  194. }
  195. // We only support 1 plane and 8, 24 or 32 bits per pixel and no
  196. // compression.
  197. planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34])
  198. // if compression is set to BITFIELDS, but the bitmask is set to the default bitmask
  199. // that would be used if compression was set to 0, we can continue as if compression was 0
  200. if compression == 3 && infoLen > infoHeaderLen &&
  201. readUint32(b[54:58]) == 0xff0000 && readUint32(b[58:62]) == 0xff00 &&
  202. readUint32(b[62:66]) == 0xff && readUint32(b[66:70]) == 0xff000000 {
  203. compression = 0
  204. }
  205. if planes != 1 || compression != 0 {
  206. return errBmpUnsupported
  207. }
  208. switch bpp {
  209. case 1, 2, 4, 8:
  210. palColors := readUint32(b[46:50])
  211. _, err := io.ReadFull(r, b[:palColors*4])
  212. if err != nil {
  213. return err
  214. }
  215. palette := make([]Color, palColors)
  216. for i := range palette {
  217. // BMP images are stored in BGR order rather than RGB order.
  218. // Every 4th byte is padding.
  219. palette[i] = Color{b[4*i+2], b[4*i+1], b[4*i+0]}
  220. }
  221. if _, err := r.Seek(int64(offset), io.SeekStart); err != nil {
  222. return err
  223. }
  224. return img.decodeBmpPaletted(r, width, height, int(bpp), palette, topDown)
  225. case 24:
  226. if _, err := r.Seek(int64(offset), io.SeekStart); err != nil {
  227. return err
  228. }
  229. return img.decodeBmpRGB(r, width, height, 3, topDown, true)
  230. case 32:
  231. noAlpha := true
  232. if infoLen >= 70 {
  233. // Alpha mask is empty, so no alpha here
  234. noAlpha = readUint32(b[66:70]) == 0
  235. }
  236. if _, err := r.Seek(int64(offset), io.SeekStart); err != nil {
  237. return err
  238. }
  239. return img.decodeBmpRGB(r, width, height, 4, topDown, noAlpha)
  240. }
  241. return errBmpUnsupported
  242. }
  243. func (img *Image) saveAsBmp() (*imagedata.ImageData, error) {
  244. width, height := img.Width(), img.Height()
  245. h := &bmpHeader{
  246. sigBM: [2]byte{'B', 'M'},
  247. fileSize: 14 + 40,
  248. resverved: [2]uint16{0, 0},
  249. pixOffset: 14 + 40,
  250. dibHeaderSize: 40,
  251. width: uint32(width),
  252. height: uint32(height),
  253. colorPlane: 1,
  254. bpp: 24,
  255. compression: 0,
  256. xPixelsPerMeter: 2835,
  257. yPixelsPerMeter: 2835,
  258. colorUse: 0,
  259. colorImportant: 0,
  260. }
  261. lineSize := (width*3 + 3) &^ 3
  262. h.imageSize = uint32(height * lineSize)
  263. h.fileSize += h.imageSize
  264. buf := new(bytes.Buffer)
  265. buf.Grow(int(h.fileSize))
  266. if err := binary.Write(buf, binary.LittleEndian, h); err != nil {
  267. return nil, err
  268. }
  269. if err := img.CopyMemory(); err != nil {
  270. return nil, err
  271. }
  272. data := unsafe.Pointer(C.vips_image_get_data(img.VipsImage))
  273. datalen := int(img.VipsImage.Bands) * int(img.VipsImage.Xsize) * int(img.VipsImage.Ysize)
  274. imgData := ptrToBytes(data, datalen)
  275. bands := int(img.VipsImage.Bands)
  276. stride := width * bands
  277. line := make([]byte, lineSize)
  278. for y := height - 1; y >= 0; y-- {
  279. min := y * stride
  280. max := min + stride
  281. for i, j := min, 0; i < max; i, j = i+bands, j+3 {
  282. line[j+0] = imgData[i+2]
  283. line[j+1] = imgData[i+1]
  284. line[j+2] = imgData[i+0]
  285. if bands == 4 && imgData[i+3] < 255 {
  286. line[j+0] = byte(int(line[j+0]) * int(imgData[i+3]) / 255)
  287. line[j+1] = byte(int(line[j+1]) * int(imgData[i+3]) / 255)
  288. line[j+2] = byte(int(line[j+2]) * int(imgData[i+3]) / 255)
  289. }
  290. }
  291. if _, err := buf.Write(line); err != nil {
  292. return nil, err
  293. }
  294. }
  295. return &imagedata.ImageData{
  296. Type: imagetype.BMP,
  297. Data: buf.Bytes(),
  298. }, nil
  299. }