git.lucas.co / go_mono
git clone https://git.lucas.co/go_mono.git

bmp/writer.go (5.7K)

  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 
  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 // Encode writes the image m to w in BMP format.
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 }