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) error { 53 buf := make([]byte, step) 54 for y := dy - 1; y >= 0; y-- { 55 min := y*stride + 0 56 max := y*stride + dx*4 57 off := 0 58 for i := min; i < max; i += 4 { 59 buf[off+2] = pix[i+0] 60 buf[off+1] = pix[i+1] 61 buf[off+0] = pix[i+2] 62 off += 3 63 } 64 if _, err := w.Write(buf); err != nil { 65 return err 66 } 67 } 68 return nil 69} 70 71func encode(w io.Writer, m image.Image, step int) error { 72 b := m.Bounds() 73 buf := make([]byte, step) 74 for y := b.Max.Y - 1; y >= b.Min.Y; y-- { 75 off := 0 76 for x := b.Min.X; x < b.Max.X; x++ { 77 r, g, b, _ := m.At(x, y).RGBA() 78 buf[off+2] = byte(r >> 8) 79 buf[off+1] = byte(g >> 8) 80 buf[off+0] = byte(b >> 8) 81 off += 3 82 } 83 if _, err := w.Write(buf); err != nil { 84 return err 85 } 86 } 87 return nil 88} 89 90// Encode writes the image m to w in BMP format. 91func Encode(w io.Writer, m image.Image) error { 92 d := m.Bounds().Size() 93 if d.X < 0 || d.Y < 0 { 94 return errors.New("bmp: negative bounds") 95 } 96 h := &header{ 97 sigBM: [2]byte{'B', 'M'}, 98 fileSize: 14 + 40, 99 pixOffset: 14 + 40, 100 dibHeaderSize: 40, 101 width: uint32(d.X), 102 height: uint32(d.Y), 103 colorPlane: 1, 104 } 105 106 var step int 107 var palette []byte 108 switch m := m.(type) { 109 case *image.Gray: 110 step = (d.X + 3) &^ 3 111 palette = make([]byte, 1024) 112 for i := 0; i < 256; i++ { 113 palette[i*4+0] = uint8(i) 114 palette[i*4+1] = uint8(i) 115 palette[i*4+2] = uint8(i) 116 palette[i*4+3] = 0xFF 117 } 118 h.imageSize = uint32(d.Y * step) 119 h.fileSize += uint32(len(palette)) + h.imageSize 120 h.pixOffset += uint32(len(palette)) 121 h.bpp = 8 122 123 case *image.Paletted: 124 step = (d.X + 3) &^ 3 125 palette = make([]byte, 1024) 126 for i := 0; i < len(m.Palette) && i < 256; i++ { 127 r, g, b, _ := m.Palette[i].RGBA() 128 palette[i*4+0] = uint8(b >> 8) 129 palette[i*4+1] = uint8(g >> 8) 130 palette[i*4+2] = uint8(r >> 8) 131 palette[i*4+3] = 0xFF 132 } 133 h.imageSize = uint32(d.Y * step) 134 h.fileSize += uint32(len(palette)) + h.imageSize 135 h.pixOffset += uint32(len(palette)) 136 h.bpp = 8 137 default: 138 step = (3*d.X + 3) &^ 3 139 h.imageSize = uint32(d.Y * step) 140 h.fileSize += h.imageSize 141 h.bpp = 24 142 } 143 144 if err := binary.Write(w, binary.LittleEndian, h); err != nil { 145 return err 146 } 147 if palette != nil { 148 if err := binary.Write(w, binary.LittleEndian, palette); err != nil { 149 return err 150 } 151 } 152 153 if d.X == 0 || d.Y == 0 { 154 return nil 155 } 156 157 switch m := m.(type) { 158 case *image.Gray: 159 return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step) 160 case *image.Paletted: 161 return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step) 162 case *image.RGBA: 163 return encodeRGBA(w, m.Pix, d.X, d.Y, m.Stride, step) 164 } 165 return encode(w, m, step) 166} 167