writer.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. // ◄◄◄ gobmp/writer.go ►►►
  2. // Copyright © 2012 Jason Summers
  3. // Use of this code is governed by an MIT-style license that can
  4. // be found in the readme.md file.
  5. //
  6. // BMP file encoder
  7. //
  8. package gobmp
  9. import "image"
  10. import "io"
  11. // EncoderOptions stores options that can be passed to EncodeWithOptions().
  12. // Create an EncoderOptions object with new().
  13. type EncoderOptions struct {
  14. densitySet bool
  15. xDens, yDens int
  16. supportTrns bool
  17. }
  18. // SetDensity sets the density to write to the output image's metadata, in
  19. // pixels per meter.
  20. func (opts *EncoderOptions) SetDensity(xDens, yDens int) {
  21. opts.densitySet = true
  22. opts.xDens = xDens
  23. opts.yDens = yDens
  24. }
  25. // SupportTransparency indicates whether to retain transparency information
  26. // when writing the BMP file. Transparency requires the use of a
  27. // not-so-portable version of BMP.
  28. func (opts *EncoderOptions) SupportTransparency(t bool) {
  29. opts.supportTrns = t
  30. }
  31. type encoder struct {
  32. opts *EncoderOptions
  33. w io.Writer
  34. m image.Image
  35. m_AsPaletted *image.Paletted
  36. srcBounds image.Rectangle
  37. width int
  38. height int
  39. dstStride int
  40. dstBitsSize int
  41. dstBitCount int
  42. dstBitsOffset int
  43. dstFileSize int
  44. writeAlpha bool
  45. writePaletted bool
  46. srcIsGray bool
  47. nColors int // Number of colors in palette; 0 if no palette
  48. headerSize int // 40 (for BMPv3) or 124 (for BMPv5)
  49. }
  50. func setWORD(b []byte, n uint16) {
  51. b[0] = byte(n)
  52. b[1] = byte(n >> 8)
  53. }
  54. func setDWORD(b []byte, n uint32) {
  55. b[0] = byte(n)
  56. b[1] = byte(n >> 8)
  57. b[2] = byte(n >> 16)
  58. b[3] = byte(n >> 24)
  59. }
  60. // Write the BITMAPFILEHEADER structure to a slice[14].
  61. func (e *encoder) generateFileHeader(h []byte) {
  62. h[0] = 0x42 // 'B'
  63. h[1] = 0x4d // 'M'
  64. setDWORD(h[2:6], uint32(e.dstFileSize))
  65. setDWORD(h[10:14], uint32(e.dstBitsOffset))
  66. }
  67. // Write the BITMAPINFOHEADER structure to a slice[40] or [124].
  68. func (e *encoder) generateInfoHeader(h []byte) {
  69. setDWORD(h[0:4], uint32(e.headerSize))
  70. setDWORD(h[4:8], uint32(e.width))
  71. setDWORD(h[8:12], uint32(e.height))
  72. setWORD(h[12:14], 1) // biPlanes
  73. setWORD(h[14:16], uint16(e.dstBitCount))
  74. if e.writeAlpha {
  75. setWORD(h[16:20], 3) // "Compression" = BI_BITFIELDS
  76. }
  77. setDWORD(h[20:24], uint32(e.dstBitsSize))
  78. if e.opts.densitySet {
  79. setDWORD(h[24:28], uint32(e.opts.xDens))
  80. setDWORD(h[28:32], uint32(e.opts.yDens))
  81. } else {
  82. setDWORD(h[24:28], 2835)
  83. setDWORD(h[28:32], 2835)
  84. }
  85. setDWORD(h[32:36], uint32(e.nColors))
  86. if len(h) == 124 {
  87. // Set V5 header fields
  88. setDWORD(h[40:44], 0x00ff0000) // RedMask
  89. setDWORD(h[44:48], 0x0000ff00) // GreenMask
  90. setDWORD(h[48:52], 0x000000ff) // BlueMask
  91. setDWORD(h[52:56], 0xff000000) // AlphaMask
  92. setDWORD(h[56:60], 0x73524742) // CSType = sRGB
  93. setDWORD(h[108:112], 4) // Intent = IMAGES (perceptual)
  94. }
  95. }
  96. func (e *encoder) writeHeaders() error {
  97. h := make([]byte, 14+e.headerSize)
  98. e.generateFileHeader(h[:14])
  99. e.generateInfoHeader(h[14:])
  100. _, err := e.w.Write(h[:])
  101. return err
  102. }
  103. func (e *encoder) writePalette() error {
  104. if !e.writePaletted {
  105. return nil
  106. }
  107. pal := make([]uint8, 4*e.nColors)
  108. for i := 0; i < e.nColors; i++ {
  109. var r, g, b uint32
  110. if e.srcIsGray {
  111. // Manufacture a grayscale palette.
  112. r = uint32(i) << 8
  113. g, b = r, r
  114. } else {
  115. r, g, b, _ = e.m_AsPaletted.Palette[i].RGBA()
  116. }
  117. pal[4*i+0] = uint8(b >> 8)
  118. pal[4*i+1] = uint8(g >> 8)
  119. pal[4*i+2] = uint8(r >> 8)
  120. }
  121. _, err := e.w.Write(pal)
  122. return err
  123. }
  124. // Read a row from the (paletted) source image, and store it in rowBuf in 1-bit
  125. // BMP format.
  126. func generateRow_1(e *encoder, j int, rowBuf []byte) {
  127. for i := range rowBuf {
  128. rowBuf[i] = 0
  129. }
  130. for i := 0; i < e.width; i++ {
  131. if e.m_AsPaletted.Pix[j*e.m_AsPaletted.Stride+i] != 0 {
  132. rowBuf[i/8] |= uint8(1 << uint(7-i%8))
  133. }
  134. }
  135. }
  136. // Read a row from the (paletted) source image, and store it in rowBuf in 4-bit
  137. // BMP format.
  138. func generateRow_4(e *encoder, j int, rowBuf []byte) {
  139. for i := range rowBuf {
  140. rowBuf[i] = 0
  141. }
  142. for i := 0; i < e.width; i++ {
  143. v := e.m_AsPaletted.Pix[j*e.m_AsPaletted.Stride+i]
  144. if i%2 == 0 {
  145. v <<= 4
  146. }
  147. rowBuf[i/2] |= v
  148. }
  149. }
  150. // Read a row from the (paletted) source image, and store it in rowBuf in 8-bit
  151. // BMP format.
  152. func generateRow_8(e *encoder, j int, rowBuf []byte) {
  153. copy(rowBuf[0:e.width], e.m_AsPaletted.Pix[j*e.m_AsPaletted.Stride:])
  154. }
  155. // Read a row from the (grayscale) source image, and store it in rowBuf in
  156. // 8-bit BMP format.
  157. func generateRow_GrayPal(e *encoder, j int, rowBuf []byte) {
  158. for i := 0; i < e.width; i++ {
  159. srcclr := e.m.At(e.srcBounds.Min.X+i, e.srcBounds.Min.Y+j)
  160. r, _, _, _ := srcclr.RGBA()
  161. rowBuf[i] = uint8(r >> 8)
  162. }
  163. }
  164. // Read a row from the source image, and store it in rowBuf in 24-bit BMP format.
  165. func generateRow_24(e *encoder, j int, rowBuf []byte) {
  166. var s [3]uint32
  167. for i := 0; i < e.width; i++ {
  168. srcclr := e.m.At(e.srcBounds.Min.X+i, e.srcBounds.Min.Y+j)
  169. s[2], s[1], s[0], _ = srcclr.RGBA()
  170. for k := 0; k < 3; k++ {
  171. rowBuf[i*3+k] = uint8(s[k] >> 8)
  172. }
  173. }
  174. }
  175. // Read a row from the source image, and store it in rowBuf in 32-bit BMP format.
  176. func generateRow_32(e *encoder, j int, rowBuf []byte) {
  177. var s [4]uint32
  178. for i := 0; i < e.width; i++ {
  179. srcclr := e.m.At(e.srcBounds.Min.X+i, e.srcBounds.Min.Y+j)
  180. s[2], s[1], s[0], s[3] = srcclr.RGBA()
  181. for k := 0; k < 4; k++ {
  182. if s[3] == 0 {
  183. rowBuf[i*4+k] = 0
  184. } else if k == 3 || s[3] == 0xffff {
  185. rowBuf[i*4+k] = uint8(s[k] >> 8)
  186. } else {
  187. // Convert to unassociated alpha
  188. rowBuf[i*4+k] = uint8(0.5 + 255.0*(float64(s[k])/float64(s[3])))
  189. }
  190. }
  191. }
  192. }
  193. func (e *encoder) writeBits() error {
  194. var err error
  195. var genRowFunc func(e *encoder, j int, rowBuf []byte)
  196. if e.writePaletted {
  197. if e.srcIsGray {
  198. genRowFunc = generateRow_GrayPal
  199. } else {
  200. switch e.dstBitCount {
  201. case 1:
  202. genRowFunc = generateRow_1
  203. case 4:
  204. genRowFunc = generateRow_4
  205. default:
  206. genRowFunc = generateRow_8
  207. }
  208. }
  209. } else {
  210. if e.dstBitCount == 32 {
  211. genRowFunc = generateRow_32
  212. } else {
  213. genRowFunc = generateRow_24
  214. }
  215. }
  216. rowBuf := make([]byte, e.dstStride)
  217. for j := 0; j < e.height; j++ {
  218. genRowFunc(e, e.height-j-1, rowBuf)
  219. _, err = e.w.Write(rowBuf)
  220. if err != nil {
  221. return err
  222. }
  223. }
  224. return nil
  225. }
  226. // If the image can be written as a paletted image, sets e.writePaletted
  227. // to true, and sets related fields.
  228. func (e *encoder) checkPaletted() {
  229. if e.writeAlpha {
  230. return
  231. }
  232. switch e.m.(type) {
  233. case *image.Paletted:
  234. e.m_AsPaletted = e.m.(*image.Paletted)
  235. e.nColors = len(e.m_AsPaletted.Palette)
  236. if e.nColors < 1 || e.nColors > 256 {
  237. e.m_AsPaletted = nil
  238. e.nColors = 0
  239. return
  240. }
  241. e.writePaletted = true
  242. case *image.Gray, *image.Gray16:
  243. e.srcIsGray = true
  244. e.writePaletted = true
  245. e.nColors = 256
  246. }
  247. }
  248. func (e *encoder) srcIsOpaque() bool {
  249. switch e.m.(type) {
  250. // If the image's type doesn't even support transparency, it must be opaque.
  251. case *image.YCbCr, *image.Gray, *image.Gray16:
  252. return true
  253. }
  254. for j := e.srcBounds.Min.Y; j < e.srcBounds.Max.Y; j++ {
  255. for i := e.srcBounds.Min.X; i < e.srcBounds.Max.X; i++ {
  256. _, _, _, a := e.m.At(i, j).RGBA()
  257. if a < 0xffff {
  258. return false
  259. }
  260. }
  261. }
  262. return true
  263. }
  264. // Plot out the structure of the file that we're going to write.
  265. func (e *encoder) strategize() error {
  266. e.srcBounds = e.m.Bounds()
  267. e.width = e.srcBounds.Dx()
  268. e.height = e.srcBounds.Dy()
  269. if e.opts.supportTrns && !e.srcIsOpaque() {
  270. e.writeAlpha = true
  271. e.headerSize = 124
  272. } else {
  273. e.headerSize = 40
  274. }
  275. e.checkPaletted()
  276. if e.writePaletted {
  277. if e.nColors <= 2 {
  278. e.dstBitCount = 1
  279. } else if e.nColors <= 16 {
  280. e.dstBitCount = 4
  281. } else {
  282. e.dstBitCount = 8
  283. }
  284. } else {
  285. if e.writeAlpha {
  286. e.dstBitCount = 32
  287. } else {
  288. e.dstBitCount = 24
  289. }
  290. }
  291. e.dstStride = ((e.width*e.dstBitCount + 31) / 32) * 4
  292. e.dstBitsOffset = 14 + e.headerSize + 4*e.nColors
  293. e.dstBitsSize = e.height * e.dstStride
  294. e.dstFileSize = e.dstBitsOffset + e.dstBitsSize
  295. return nil
  296. }
  297. // EncodeWithOptions writes the Image m to w in BMP format, using the options
  298. // recorded in opts.
  299. // opts may be nil, in which case it behaves the same as Encode.
  300. func EncodeWithOptions(w io.Writer, m image.Image, opts *EncoderOptions) error {
  301. var err error
  302. e := new(encoder)
  303. e.w = w
  304. e.m = m
  305. if opts != nil {
  306. e.opts = opts
  307. } else {
  308. e.opts = new(EncoderOptions)
  309. }
  310. err = e.strategize()
  311. if err != nil {
  312. return err
  313. }
  314. err = e.writeHeaders()
  315. if err != nil {
  316. return err
  317. }
  318. err = e.writePalette()
  319. if err != nil {
  320. return err
  321. }
  322. err = e.writeBits()
  323. if err != nil {
  324. return err
  325. }
  326. return nil
  327. }
  328. // Encode writes the Image m to w in BMP format.
  329. func Encode(w io.Writer, m image.Image) error {
  330. return EncodeWithOptions(w, m, nil)
  331. }