bmp.go 12 KB

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