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