1// Copyright 2013 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package bmp 6 7import ( 8 "encoding/binary" 9 "errors" 10 "image" 11 "io" 12) 13 14type 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 32func 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 52func 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 105func 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 142func 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// Encode writes the image m to w in BMP format. 162func 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