bmp.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  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. "github.com/imgproxy/imgproxy/v3/imath"
  17. )
  18. type bmpHeader struct {
  19. sigBM [2]byte
  20. fileSize uint32
  21. resverved [2]uint16
  22. pixOffset uint32
  23. dibHeaderSize uint32
  24. width uint32
  25. height uint32
  26. colorPlane uint16
  27. bpp uint16
  28. compression uint32
  29. imageSize uint32
  30. xPixelsPerMeter uint32
  31. yPixelsPerMeter uint32
  32. colorUse uint32
  33. colorImportant uint32
  34. }
  35. // errBmpUnsupported means that the input BMP image uses a valid but unsupported
  36. // feature.
  37. var errBmpUnsupported = errors.New("unsupported BMP image")
  38. func readUint16(b []byte) uint16 {
  39. return uint16(b[0]) | uint16(b[1])<<8
  40. }
  41. func readUint32(b []byte) uint32 {
  42. return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
  43. }
  44. func prepareBmpCanvas(width, height, bands int) (*C.VipsImage, []byte, error) {
  45. var tmp *C.VipsImage
  46. if C.vips_black_go(&tmp, C.int(width), C.int(height), C.int(bands)) != 0 {
  47. return nil, nil, Error()
  48. }
  49. data := unsafe.Pointer(C.vips_image_get_data(tmp))
  50. datalen := int(tmp.Bands) * int(tmp.Xsize) * int(tmp.Ysize)
  51. return tmp, ptrToBytes(data, datalen), nil
  52. }
  53. func bmpClearOnPanic(img **C.VipsImage) {
  54. if rerr := recover(); rerr != nil {
  55. C.clear_image(img)
  56. panic(rerr)
  57. }
  58. }
  59. func bmpSetBitDepth(img *Image, colors int) {
  60. var bitdepth int
  61. switch {
  62. case colors > 16:
  63. bitdepth = 8
  64. case colors > 4:
  65. bitdepth = 4
  66. case colors > 2:
  67. bitdepth = 2
  68. }
  69. img.SetInt("palette-bit-depth", bitdepth)
  70. }
  71. // decodeBmpPaletted reads an 8/4/2/1 bit-per-pixel BMP image from r.
  72. // If topDown is false, the image rows will be read bottom-up.
  73. func (img *Image) decodeBmpPaletted(r io.Reader, width, height, bpp int, palette []Color, topDown bool) error {
  74. tmp, imgData, err := prepareBmpCanvas(width, height, 3)
  75. if err != nil {
  76. return err
  77. }
  78. defer bmpClearOnPanic(&tmp)
  79. // Each row is 4-byte aligned.
  80. cap := 8 / bpp
  81. b := make([]byte, ((width+cap-1)/cap+3)&^3)
  82. y0, y1, yDelta := height-1, -1, -1
  83. if topDown {
  84. y0, y1, yDelta = 0, height, +1
  85. }
  86. stride := width * 3
  87. for y := y0; y != y1; y += yDelta {
  88. if _, err = io.ReadFull(r, b); err != nil {
  89. C.clear_image(&tmp)
  90. return err
  91. }
  92. p := imgData[y*stride : (y+1)*stride]
  93. j, bit := 0, 8-bpp
  94. for i := 0; i < len(p); i += 3 {
  95. pind := (b[j] >> bit) & (1<<bpp - 1)
  96. if bit == 0 {
  97. bit = 8 - bpp
  98. j++
  99. } else {
  100. bit -= bpp
  101. }
  102. c := palette[pind]
  103. p[i+0] = c.R
  104. p[i+1] = c.G
  105. p[i+2] = c.B
  106. }
  107. }
  108. C.swap_and_clear(&img.VipsImage, tmp)
  109. bmpSetBitDepth(img, len(palette))
  110. return nil
  111. }
  112. // decodeBmpRLE reads an 8/4 bit-per-pixel RLE-encoded BMP image from r.
  113. func (img *Image) decodeBmpRLE(r io.Reader, width, height, bpp int, palette []Color) error {
  114. tmp, imgData, err := prepareBmpCanvas(width, height, 3)
  115. if err != nil {
  116. return err
  117. }
  118. defer bmpClearOnPanic(&tmp)
  119. b := make([]byte, 256)
  120. readPair := func() (byte, byte, error) {
  121. _, err := io.ReadFull(r, b[:2])
  122. return b[0], b[1], err
  123. }
  124. x, y := 0, height-1
  125. cap := 8 / bpp
  126. Loop:
  127. for {
  128. b1, b2, err := readPair()
  129. if err != nil {
  130. C.clear_image(&tmp)
  131. return err
  132. }
  133. if b1 == 0 {
  134. switch b2 {
  135. case 0: // End of line
  136. x, y = 0, y-1
  137. if y < 0 {
  138. // We should probably return an error here,
  139. // but it's safier to just stop decoding
  140. break Loop
  141. }
  142. case 1: // End of file
  143. break Loop
  144. case 2:
  145. dx, dy, err := readPair()
  146. if err != nil {
  147. C.clear_image(&tmp)
  148. return err
  149. }
  150. x = imath.Min(x+int(dx), width)
  151. y -= int(dy)
  152. if y < 0 {
  153. break Loop
  154. }
  155. default:
  156. pixelsCount := int(b2)
  157. n := ((pixelsCount+cap-1)/cap + 1) &^ 1
  158. if _, err := io.ReadFull(r, b[:n]); err != nil {
  159. C.clear_image(&tmp)
  160. return err
  161. }
  162. pixelsCount = imath.Min(pixelsCount, width-x)
  163. if pixelsCount > 0 {
  164. start := (y*width + x) * 3
  165. p := imgData[start : start+pixelsCount*3]
  166. j, bit := 0, 8-bpp
  167. for i := 0; i < len(p); i += 3 {
  168. pind := (b[j] >> bit) & (1<<bpp - 1)
  169. if bit == 0 {
  170. bit = 8 - bpp
  171. j++
  172. } else {
  173. bit -= bpp
  174. }
  175. c := palette[pind]
  176. p[i+0] = c.R
  177. p[i+1] = c.G
  178. p[i+2] = c.B
  179. }
  180. x += pixelsCount
  181. }
  182. }
  183. } else {
  184. pixelsCount := imath.Min(int(b1), width-x)
  185. if pixelsCount > 0 {
  186. start := (y*width + x) * 3
  187. p := imgData[start : start+pixelsCount*3]
  188. bit := 8 - bpp
  189. for i := 0; i < len(p); i += 3 {
  190. pind := (b2 >> bit) & (1<<bpp - 1)
  191. if bit == 0 {
  192. bit = 8 - bpp
  193. } else {
  194. bit -= bpp
  195. }
  196. c := palette[pind]
  197. p[i+0] = c.R
  198. p[i+1] = c.G
  199. p[i+2] = c.B
  200. }
  201. x += pixelsCount
  202. }
  203. }
  204. }
  205. C.swap_and_clear(&img.VipsImage, tmp)
  206. bmpSetBitDepth(img, len(palette))
  207. return nil
  208. }
  209. // decodeBmpRGB reads a 24/32 bit-per-pixel BMP image from r.
  210. // If topDown is false, the image rows will be read bottom-up.
  211. func (img *Image) decodeBmpRGB(r io.Reader, width, height, bands int, topDown, noAlpha bool) error {
  212. if bands != 3 && bands != 4 {
  213. return errBmpUnsupported
  214. }
  215. imgBands := 3
  216. if bands == 4 && !noAlpha {
  217. // Create RGBA image only when source has 4 bands and the last one is alpha
  218. imgBands = 4
  219. }
  220. tmp, imgData, err := prepareBmpCanvas(width, height, imgBands)
  221. if err != nil {
  222. return err
  223. }
  224. defer bmpClearOnPanic(&tmp)
  225. // Each row is 4-byte aligned.
  226. b := make([]byte, (bands*width+3)&^3)
  227. y0, y1, yDelta := height-1, -1, -1
  228. if topDown {
  229. y0, y1, yDelta = 0, height, +1
  230. }
  231. stride := width * imgBands
  232. for y := y0; y != y1; y += yDelta {
  233. if _, err = io.ReadFull(r, b); err != nil {
  234. C.clear_image(&tmp)
  235. return err
  236. }
  237. p := imgData[y*stride : (y+1)*stride]
  238. for i, j := 0, 0; i < len(p); i, j = i+imgBands, j+bands {
  239. // BMP images are stored in BGR order rather than RGB order.
  240. p[i+0] = b[j+2]
  241. p[i+1] = b[j+1]
  242. p[i+2] = b[j+0]
  243. if imgBands == 4 {
  244. p[i+3] = b[j+3]
  245. }
  246. }
  247. }
  248. C.swap_and_clear(&img.VipsImage, tmp)
  249. return nil
  250. }
  251. func (img *Image) loadBmp(data []byte, noAlpha bool) error {
  252. // We only support those BMP images that are a BITMAPFILEHEADER
  253. // immediately followed by a BITMAPINFOHEADER.
  254. const (
  255. fileHeaderLen = 14
  256. infoHeaderLen = 40
  257. v4InfoHeaderLen = 108
  258. v5InfoHeaderLen = 124
  259. )
  260. r := bytes.NewReader(data)
  261. var b [1024]byte
  262. if _, err := io.ReadFull(r, b[:fileHeaderLen+4]); err != nil {
  263. if err == io.EOF {
  264. err = io.ErrUnexpectedEOF
  265. }
  266. return err
  267. }
  268. if string(b[:2]) != "BM" {
  269. return errors.New("not a BMP image")
  270. }
  271. offset := readUint32(b[10:14])
  272. infoLen := readUint32(b[14:18])
  273. if infoLen != infoHeaderLen && infoLen != v4InfoHeaderLen && infoLen != v5InfoHeaderLen {
  274. return errBmpUnsupported
  275. }
  276. if _, err := io.ReadFull(r, b[fileHeaderLen+4:fileHeaderLen+infoLen]); err != nil {
  277. if err == io.EOF {
  278. err = io.ErrUnexpectedEOF
  279. }
  280. return err
  281. }
  282. width := int(int32(readUint32(b[18:22])))
  283. height := int(int32(readUint32(b[22:26])))
  284. topDown := false
  285. if height < 0 {
  286. height, topDown = -height, true
  287. }
  288. if width <= 0 || height <= 0 {
  289. return errBmpUnsupported
  290. }
  291. // We only support 1 plane and 8, 24 or 32 bits per pixel
  292. planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34])
  293. if planes != 1 {
  294. return errBmpUnsupported
  295. }
  296. // if compression is set to BITFIELDS, but the bitmask is set to the default bitmask
  297. // that would be used if compression was set to 0, we can continue as if compression was 0
  298. if compression == 3 && infoLen > infoHeaderLen &&
  299. readUint32(b[54:58]) == 0xff0000 && readUint32(b[58:62]) == 0xff00 &&
  300. readUint32(b[62:66]) == 0xff && readUint32(b[66:70]) == 0xff000000 {
  301. compression = 0
  302. }
  303. rle := false
  304. if compression == 1 && bpp == 8 || compression == 2 && bpp == 4 {
  305. rle = true
  306. } else if compression != 0 {
  307. return errBmpUnsupported
  308. }
  309. var palette []Color
  310. if bpp <= 8 {
  311. palColors := readUint32(b[46:50])
  312. if palColors == 0 {
  313. palColors = 1 << bpp
  314. }
  315. _, err := io.ReadFull(r, b[:palColors*4])
  316. if err != nil {
  317. return err
  318. }
  319. palette = make([]Color, palColors)
  320. for i := range palette {
  321. // BMP images are stored in BGR order rather than RGB order.
  322. // Every 4th byte is padding.
  323. palette[i] = Color{b[4*i+2], b[4*i+1], b[4*i+0]}
  324. }
  325. }
  326. if _, err := r.Seek(int64(offset), io.SeekStart); err != nil {
  327. return err
  328. }
  329. if rle {
  330. return img.decodeBmpRLE(r, width, height, int(bpp), palette)
  331. }
  332. switch bpp {
  333. case 1, 2, 4, 8:
  334. return img.decodeBmpPaletted(r, width, height, int(bpp), palette, topDown)
  335. case 24:
  336. return img.decodeBmpRGB(r, width, height, 3, topDown, true)
  337. case 32:
  338. if infoLen >= 70 {
  339. // Alpha mask is empty, so no alpha here
  340. noAlpha = readUint32(b[66:70]) == 0
  341. }
  342. return img.decodeBmpRGB(r, width, height, 4, topDown, noAlpha)
  343. }
  344. return errBmpUnsupported
  345. }
  346. func (img *Image) saveAsBmp() (*imagedata.ImageData, error) {
  347. width, height := img.Width(), img.Height()
  348. h := &bmpHeader{
  349. sigBM: [2]byte{'B', 'M'},
  350. fileSize: 14 + 40,
  351. resverved: [2]uint16{0, 0},
  352. pixOffset: 14 + 40,
  353. dibHeaderSize: 40,
  354. width: uint32(width),
  355. height: uint32(height),
  356. colorPlane: 1,
  357. bpp: 24,
  358. compression: 0,
  359. xPixelsPerMeter: 2835,
  360. yPixelsPerMeter: 2835,
  361. colorUse: 0,
  362. colorImportant: 0,
  363. }
  364. lineSize := (width*3 + 3) &^ 3
  365. h.imageSize = uint32(height * lineSize)
  366. h.fileSize += h.imageSize
  367. buf := new(bytes.Buffer)
  368. buf.Grow(int(h.fileSize))
  369. if err := binary.Write(buf, binary.LittleEndian, h); err != nil {
  370. return nil, err
  371. }
  372. if err := img.CopyMemory(); err != nil {
  373. return nil, err
  374. }
  375. data := unsafe.Pointer(C.vips_image_get_data(img.VipsImage))
  376. datalen := int(img.VipsImage.Bands) * int(img.VipsImage.Xsize) * int(img.VipsImage.Ysize)
  377. imgData := ptrToBytes(data, datalen)
  378. bands := int(img.VipsImage.Bands)
  379. stride := width * bands
  380. line := make([]byte, lineSize)
  381. for y := height - 1; y >= 0; y-- {
  382. min := y * stride
  383. max := min + stride
  384. for i, j := min, 0; i < max; i, j = i+bands, j+3 {
  385. line[j+0] = imgData[i+2]
  386. line[j+1] = imgData[i+1]
  387. line[j+2] = imgData[i+0]
  388. if bands == 4 && imgData[i+3] < 255 {
  389. line[j+0] = byte(int(line[j+0]) * int(imgData[i+3]) / 255)
  390. line[j+1] = byte(int(line[j+1]) * int(imgData[i+3]) / 255)
  391. line[j+2] = byte(int(line[j+2]) * int(imgData[i+3]) / 255)
  392. }
  393. }
  394. if _, err := buf.Write(line); err != nil {
  395. return nil, err
  396. }
  397. }
  398. return &imagedata.ImageData{
  399. Type: imagetype.BMP,
  400. Data: buf.Bytes(),
  401. }, nil
  402. }