1
2
3
4
5 package bmp
6
7 import (
8 "encoding/binary"
9 "errors"
10 "image"
11 "io"
12 )
13
14 type header struct {
15 sigBM [2]byte
16 fileSize uint32
17 resverved [2]uint16
18 pixOffset uint32
19 dibHeaderSize uint32
20 width uint32
21 height uint32
22 colorPlane uint16
23 bpp uint16
24 compression uint32
25 imageSize uint32
26 xPixelsPerMeter uint32
27 yPixelsPerMeter uint32
28 colorUse uint32
29 colorImportant uint32
30 }
31
32 func encodePaletted(w io.Writer, pix []uint8, dx, dy, stride, step int) error {
33 var padding []byte
34 if dx < step {
35 padding = make([]byte, step-dx)
36 }
37 for y := dy - 1; y >= 0; y-- {
38 min := y*stride + 0
39 max := y*stride + dx
40 if _, err := w.Write(pix[min:max]); err != nil {
41 return err
42 }
43 if padding != nil {
44 if _, err := w.Write(padding); err != nil {
45 return err
46 }
47 }
48 }
49 return nil
50 }
51
52 func encodeRGBA(w io.Writer, pix []uint8, dx, dy, stride, step int, opaque bool) error {
53 buf := make([]byte, step)
54 if opaque {
55 for y := dy - 1; y >= 0; y-- {
56 min := y*stride + 0
57 max := y*stride + dx*4
58 off := 0
59 for i := min; i < max; i += 4 {
60 buf[off+2] = pix[i+0]
61 buf[off+1] = pix[i+1]
62 buf[off+0] = pix[i+2]
63 off += 3
64 }
65 if _, err := w.Write(buf); err != nil {
66 return err
67 }
68 }
69 } else {
70 for y := dy - 1; y >= 0; y-- {
71 min := y*stride + 0
72 max := y*stride + dx*4
73 off := 0
74 for i := min; i < max; i += 4 {
75 a := uint32(pix[i+3])
76 if a == 0 {
77 buf[off+2] = 0
78 buf[off+1] = 0
79 buf[off+0] = 0
80 buf[off+3] = 0
81 off += 4
82 continue
83 } else if a == 0xff {
84 buf[off+2] = pix[i+0]
85 buf[off+1] = pix[i+1]
86 buf[off+0] = pix[i+2]
87 buf[off+3] = 0xff
88 off += 4
89 continue
90 }
91 buf[off+2] = uint8(((uint32(pix[i+0]) * 0xffff) / a) >> 8)
92 buf[off+1] = uint8(((uint32(pix[i+1]) * 0xffff) / a) >> 8)
93 buf[off+0] = uint8(((uint32(pix[i+2]) * 0xffff) / a) >> 8)
94 buf[off+3] = uint8(a)
95 off += 4
96 }
97 if _, err := w.Write(buf); err != nil {
98 return err
99 }
100 }
101 }
102 return nil
103 }
104
105 func encodeNRGBA(w io.Writer, pix []uint8, dx, dy, stride, step int, opaque bool) error {
106 buf := make([]byte, step)
107 if opaque {
108 for y := dy - 1; y >= 0; y-- {
109 min := y*stride + 0
110 max := y*stride + dx*4
111 off := 0
112 for i := min; i < max; i += 4 {
113 buf[off+2] = pix[i+0]
114 buf[off+1] = pix[i+1]
115 buf[off+0] = pix[i+2]
116 off += 3
117 }
118 if _, err := w.Write(buf); err != nil {
119 return err
120 }
121 }
122 } else {
123 for y := dy - 1; y >= 0; y-- {
124 min := y*stride + 0
125 max := y*stride + dx*4
126 off := 0
127 for i := min; i < max; i += 4 {
128 buf[off+2] = pix[i+0]
129 buf[off+1] = pix[i+1]
130 buf[off+0] = pix[i+2]
131 buf[off+3] = pix[i+3]
132 off += 4
133 }
134 if _, err := w.Write(buf); err != nil {
135 return err
136 }
137 }
138 }
139 return nil
140 }
141
142 func encode(w io.Writer, m image.Image, step int) error {
143 b := m.Bounds()
144 buf := make([]byte, step)
145 for y := b.Max.Y - 1; y >= b.Min.Y; y-- {
146 off := 0
147 for x := b.Min.X; x < b.Max.X; x++ {
148 r, g, b, _ := m.At(x, y).RGBA()
149 buf[off+2] = byte(r >> 8)
150 buf[off+1] = byte(g >> 8)
151 buf[off+0] = byte(b >> 8)
152 off += 3
153 }
154 if _, err := w.Write(buf); err != nil {
155 return err
156 }
157 }
158 return nil
159 }
160
161
162 func Encode(w io.Writer, m image.Image) error {
163 d := m.Bounds().Size()
164 if d.X < 0 || d.Y < 0 {
165 return errors.New("bmp: negative bounds")
166 }
167 h := &header{
168 sigBM: [2]byte{'B', 'M'},
169 fileSize: 14 + 40,
170 pixOffset: 14 + 40,
171 dibHeaderSize: 40,
172 width: uint32(d.X),
173 height: uint32(d.Y),
174 colorPlane: 1,
175 }
176
177 var step int
178 var palette []byte
179 var opaque bool
180 switch m := m.(type) {
181 case *image.Gray:
182 step = (d.X + 3) &^ 3
183 palette = make([]byte, 1024)
184 for i := 0; i < 256; i++ {
185 palette[i*4+0] = uint8(i)
186 palette[i*4+1] = uint8(i)
187 palette[i*4+2] = uint8(i)
188 palette[i*4+3] = 0xFF
189 }
190 h.imageSize = uint32(d.Y * step)
191 h.fileSize += uint32(len(palette)) + h.imageSize
192 h.pixOffset += uint32(len(palette))
193 h.bpp = 8
194
195 case *image.Paletted:
196 step = (d.X + 3) &^ 3
197 palette = make([]byte, 1024)
198 for i := 0; i < len(m.Palette) && i < 256; i++ {
199 r, g, b, _ := m.Palette[i].RGBA()
200 palette[i*4+0] = uint8(b >> 8)
201 palette[i*4+1] = uint8(g >> 8)
202 palette[i*4+2] = uint8(r >> 8)
203 palette[i*4+3] = 0xFF
204 }
205 h.imageSize = uint32(d.Y * step)
206 h.fileSize += uint32(len(palette)) + h.imageSize
207 h.pixOffset += uint32(len(palette))
208 h.bpp = 8
209 case *image.RGBA:
210 opaque = m.Opaque()
211 if opaque {
212 step = (3*d.X + 3) &^ 3
213 h.bpp = 24
214 } else {
215 step = 4 * d.X
216 h.bpp = 32
217 }
218 h.imageSize = uint32(d.Y * step)
219 h.fileSize += h.imageSize
220 case *image.NRGBA:
221 opaque = m.Opaque()
222 if opaque {
223 step = (3*d.X + 3) &^ 3
224 h.bpp = 24
225 } else {
226 step = 4 * d.X
227 h.bpp = 32
228 }
229 h.imageSize = uint32(d.Y * step)
230 h.fileSize += h.imageSize
231 default:
232 step = (3*d.X + 3) &^ 3
233 h.imageSize = uint32(d.Y * step)
234 h.fileSize += h.imageSize
235 h.bpp = 24
236 }
237
238 if err := binary.Write(w, binary.LittleEndian, h); err != nil {
239 return err
240 }
241 if palette != nil {
242 if err := binary.Write(w, binary.LittleEndian, palette); err != nil {
243 return err
244 }
245 }
246
247 if d.X == 0 || d.Y == 0 {
248 return nil
249 }
250
251 switch m := m.(type) {
252 case *image.Gray:
253 return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step)
254 case *image.Paletted:
255 return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step)
256 case *image.RGBA:
257 return encodeRGBA(w, m.Pix, d.X, d.Y, m.Stride, step, opaque)
258 case *image.NRGBA:
259 return encodeNRGBA(w, m.Pix, d.X, d.Y, m.Stride, step, opaque)
260 }
261 return encode(w, m, step)
262 }
263
View as plain text