writer.go raw

   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  }
 263