bmp.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  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. // decodeBmpRGB16 reads a 16 bit-per-pixel BMP image from r.
  252. // If topDown is false, the image rows will be read bottom-up.
  253. func (img *Image) decodeBmpRGB16(r io.Reader, width, height int, topDown, bmp565 bool) error {
  254. tmp, imgData, err := prepareBmpCanvas(width, height, 3)
  255. if err != nil {
  256. return err
  257. }
  258. defer bmpClearOnPanic(&tmp)
  259. // Each row is 4-byte aligned.
  260. b := make([]byte, (2*width+3)&^3)
  261. y0, y1, yDelta := height-1, -1, -1
  262. if topDown {
  263. y0, y1, yDelta = 0, height, +1
  264. }
  265. stride := width * 3
  266. for y := y0; y != y1; y += yDelta {
  267. if _, err = io.ReadFull(r, b); err != nil {
  268. C.clear_image(&tmp)
  269. return err
  270. }
  271. p := imgData[y*stride : (y+1)*stride]
  272. for i, j := 0, 0; i < len(p); i, j = i+3, j+2 {
  273. pixel := readUint16(b[j:])
  274. if bmp565 {
  275. p[i+0] = uint8((pixel&0xF800)>>11) << 3
  276. p[i+1] = uint8((pixel&0x7E0)>>5) << 2
  277. } else {
  278. p[i+0] = uint8((pixel&0x7C00)>>10) << 3
  279. p[i+1] = uint8((pixel&0x3E0)>>5) << 3
  280. }
  281. p[i+2] = uint8(pixel&0x1F) << 3
  282. }
  283. }
  284. C.swap_and_clear(&img.VipsImage, tmp)
  285. return nil
  286. }
  287. func (img *Image) loadBmp(data []byte, noAlpha bool) error {
  288. // We only support those BMP images that are a BITMAPFILEHEADER
  289. // immediately followed by a BITMAPINFOHEADER.
  290. const (
  291. fileHeaderLen = 14
  292. infoHeaderLen = 40
  293. v4InfoHeaderLen = 108
  294. v5InfoHeaderLen = 124
  295. )
  296. r := bytes.NewReader(data)
  297. var b [1024]byte
  298. if _, err := io.ReadFull(r, b[:fileHeaderLen+4]); err != nil {
  299. if err == io.EOF {
  300. err = io.ErrUnexpectedEOF
  301. }
  302. return err
  303. }
  304. if string(b[:2]) != "BM" {
  305. return errors.New("not a BMP image")
  306. }
  307. offset := readUint32(b[10:14])
  308. infoLen := readUint32(b[14:18])
  309. if infoLen != infoHeaderLen && infoLen != v4InfoHeaderLen && infoLen != v5InfoHeaderLen {
  310. return errBmpUnsupported
  311. }
  312. if _, err := io.ReadFull(r, b[fileHeaderLen+4:fileHeaderLen+infoLen]); err != nil {
  313. if err == io.EOF {
  314. err = io.ErrUnexpectedEOF
  315. }
  316. return err
  317. }
  318. width := int(int32(readUint32(b[18:22])))
  319. height := int(int32(readUint32(b[22:26])))
  320. topDown := false
  321. if height < 0 {
  322. height, topDown = -height, true
  323. }
  324. if width <= 0 || height <= 0 {
  325. return errBmpUnsupported
  326. }
  327. // We only support 1 plane and 8, 24 or 32 bits per pixel
  328. planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34])
  329. if planes != 1 {
  330. return errBmpUnsupported
  331. }
  332. rle := false
  333. bmp565 := false
  334. switch {
  335. case compression == 0:
  336. // Go ahead
  337. case compression == 1 && bpp == 8 || compression == 2 && bpp == 4:
  338. rle = true
  339. case compression == 3 && infoLen >= infoHeaderLen:
  340. if infoLen == infoHeaderLen {
  341. // Color mask is stored after the info header
  342. if _, err := io.ReadFull(r, b[54:66]); err != nil {
  343. if err == io.EOF {
  344. err = io.ErrUnexpectedEOF
  345. }
  346. return err
  347. }
  348. }
  349. rmask := readUint32(b[54:58])
  350. gmask := readUint32(b[58:62])
  351. bmask := readUint32(b[62:66])
  352. amask := readUint32(b[66:70])
  353. switch {
  354. case bpp == 16 && rmask == 0xF800 && gmask == 0x7E0 && bmask == 0x1F:
  355. bmp565 = true
  356. case bpp == 16 && rmask == 0x7C00 && gmask == 0x3E0 && bmask == 0x1F:
  357. // Go ahead, it's a regular 16 bit image
  358. case bpp == 32 && rmask == 0xff0000 && gmask == 0xff00 && bmask == 0xff && amask == 0xff000000:
  359. // Go ahead, it's a regular 32-bit image
  360. default:
  361. return errBmpUnsupported
  362. }
  363. default:
  364. return errBmpUnsupported
  365. }
  366. var palette []Color
  367. if bpp <= 8 {
  368. palColors := readUint32(b[46:50])
  369. if palColors == 0 {
  370. palColors = 1 << bpp
  371. }
  372. _, err := io.ReadFull(r, b[:palColors*4])
  373. if err != nil {
  374. return err
  375. }
  376. palette = make([]Color, palColors)
  377. for i := range palette {
  378. // BMP images are stored in BGR order rather than RGB order.
  379. // Every 4th byte is padding.
  380. palette[i] = Color{b[4*i+2], b[4*i+1], b[4*i+0]}
  381. }
  382. }
  383. if _, err := r.Seek(int64(offset), io.SeekStart); err != nil {
  384. return err
  385. }
  386. if rle {
  387. return img.decodeBmpRLE(r, width, height, int(bpp), palette)
  388. }
  389. switch bpp {
  390. case 1, 2, 4, 8:
  391. return img.decodeBmpPaletted(r, width, height, int(bpp), palette, topDown)
  392. case 16:
  393. return img.decodeBmpRGB16(r, width, height, topDown, bmp565)
  394. case 24:
  395. return img.decodeBmpRGB(r, width, height, 3, topDown, true)
  396. case 32:
  397. if infoLen >= 70 {
  398. // Alpha mask is empty, so no alpha here
  399. noAlpha = readUint32(b[66:70]) == 0
  400. }
  401. return img.decodeBmpRGB(r, width, height, 4, topDown, noAlpha)
  402. }
  403. return errBmpUnsupported
  404. }
  405. func (img *Image) saveAsBmp() (*imagedata.ImageData, error) {
  406. width, height := img.Width(), img.Height()
  407. h := &bmpHeader{
  408. sigBM: [2]byte{'B', 'M'},
  409. fileSize: 14 + 40,
  410. resverved: [2]uint16{0, 0},
  411. pixOffset: 14 + 40,
  412. dibHeaderSize: 40,
  413. width: uint32(width),
  414. height: uint32(height),
  415. colorPlane: 1,
  416. bpp: 24,
  417. compression: 0,
  418. xPixelsPerMeter: 2835,
  419. yPixelsPerMeter: 2835,
  420. colorUse: 0,
  421. colorImportant: 0,
  422. }
  423. lineSize := (width*3 + 3) &^ 3
  424. h.imageSize = uint32(height * lineSize)
  425. h.fileSize += h.imageSize
  426. buf := new(bytes.Buffer)
  427. buf.Grow(int(h.fileSize))
  428. if err := binary.Write(buf, binary.LittleEndian, h); err != nil {
  429. return nil, err
  430. }
  431. if err := img.CopyMemory(); err != nil {
  432. return nil, err
  433. }
  434. data := unsafe.Pointer(C.vips_image_get_data(img.VipsImage))
  435. datalen := int(img.VipsImage.Bands) * int(img.VipsImage.Xsize) * int(img.VipsImage.Ysize)
  436. imgData := ptrToBytes(data, datalen)
  437. bands := int(img.VipsImage.Bands)
  438. stride := width * bands
  439. line := make([]byte, lineSize)
  440. for y := height - 1; y >= 0; y-- {
  441. min := y * stride
  442. max := min + stride
  443. for i, j := min, 0; i < max; i, j = i+bands, j+3 {
  444. line[j+0] = imgData[i+2]
  445. line[j+1] = imgData[i+1]
  446. line[j+2] = imgData[i+0]
  447. if bands == 4 && imgData[i+3] < 255 {
  448. line[j+0] = byte(int(line[j+0]) * int(imgData[i+3]) / 255)
  449. line[j+1] = byte(int(line[j+1]) * int(imgData[i+3]) / 255)
  450. line[j+2] = byte(int(line[j+2]) * int(imgData[i+3]) / 255)
  451. }
  452. }
  453. if _, err := buf.Write(line); err != nil {
  454. return nil, err
  455. }
  456. }
  457. return &imagedata.ImageData{
  458. Type: imagetype.BMP,
  459. Data: buf.Bytes(),
  460. }, nil
  461. }