github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/image/bmp/reader.go (about)

     1  // Copyright 2011 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 implements a BMP image decoder and encoder.
     6  //
     7  // The BMP specification is at http://www.digicamsoft.com/bmp/bmp.html.
     8  package bmp // import "golang.org/x/image/bmp"
     9  
    10  import (
    11  	"errors"
    12  	"image"
    13  	"image/color"
    14  	"io"
    15  )
    16  
    17  // ErrUnsupported means that the input BMP image uses a valid but unsupported
    18  // feature.
    19  var ErrUnsupported = errors.New("bmp: unsupported BMP image")
    20  
    21  func readUint16(b []byte) uint16 {
    22  	return uint16(b[0]) | uint16(b[1])<<8
    23  }
    24  
    25  func readUint32(b []byte) uint32 {
    26  	return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
    27  }
    28  
    29  // decodePaletted reads an 8 bit-per-pixel BMP image from r.
    30  // If topDown is false, the image rows will be read bottom-up.
    31  func decodePaletted(r io.Reader, c image.Config, topDown bool) (image.Image, error) {
    32  	paletted := image.NewPaletted(image.Rect(0, 0, c.Width, c.Height), c.ColorModel.(color.Palette))
    33  	if c.Width == 0 || c.Height == 0 {
    34  		return paletted, nil
    35  	}
    36  	var tmp [4]byte
    37  	y0, y1, yDelta := c.Height-1, -1, -1
    38  	if topDown {
    39  		y0, y1, yDelta = 0, c.Height, +1
    40  	}
    41  	for y := y0; y != y1; y += yDelta {
    42  		p := paletted.Pix[y*paletted.Stride : y*paletted.Stride+c.Width]
    43  		if _, err := io.ReadFull(r, p); err != nil {
    44  			return nil, err
    45  		}
    46  		// Each row is 4-byte aligned.
    47  		if c.Width%4 != 0 {
    48  			_, err := io.ReadFull(r, tmp[:4-c.Width%4])
    49  			if err != nil {
    50  				return nil, err
    51  			}
    52  		}
    53  	}
    54  	return paletted, nil
    55  }
    56  
    57  // decodeRGB reads a 24 bit-per-pixel BMP image from r.
    58  // If topDown is false, the image rows will be read bottom-up.
    59  func decodeRGB(r io.Reader, c image.Config, topDown bool) (image.Image, error) {
    60  	rgba := image.NewRGBA(image.Rect(0, 0, c.Width, c.Height))
    61  	if c.Width == 0 || c.Height == 0 {
    62  		return rgba, nil
    63  	}
    64  	// There are 3 bytes per pixel, and each row is 4-byte aligned.
    65  	b := make([]byte, (3*c.Width+3)&^3)
    66  	y0, y1, yDelta := c.Height-1, -1, -1
    67  	if topDown {
    68  		y0, y1, yDelta = 0, c.Height, +1
    69  	}
    70  	for y := y0; y != y1; y += yDelta {
    71  		if _, err := io.ReadFull(r, b); err != nil {
    72  			return nil, err
    73  		}
    74  		p := rgba.Pix[y*rgba.Stride : y*rgba.Stride+c.Width*4]
    75  		for i, j := 0, 0; i < len(p); i, j = i+4, j+3 {
    76  			// BMP images are stored in BGR order rather than RGB order.
    77  			p[i+0] = b[j+2]
    78  			p[i+1] = b[j+1]
    79  			p[i+2] = b[j+0]
    80  			p[i+3] = 0xFF
    81  		}
    82  	}
    83  	return rgba, nil
    84  }
    85  
    86  // decodeNRGBA reads a 32 bit-per-pixel BMP image from r.
    87  // If topDown is false, the image rows will be read bottom-up.
    88  func decodeNRGBA(r io.Reader, c image.Config, topDown bool) (image.Image, error) {
    89  	rgba := image.NewNRGBA(image.Rect(0, 0, c.Width, c.Height))
    90  	if c.Width == 0 || c.Height == 0 {
    91  		return rgba, nil
    92  	}
    93  	y0, y1, yDelta := c.Height-1, -1, -1
    94  	if topDown {
    95  		y0, y1, yDelta = 0, c.Height, +1
    96  	}
    97  	for y := y0; y != y1; y += yDelta {
    98  		p := rgba.Pix[y*rgba.Stride : y*rgba.Stride+c.Width*4]
    99  		if _, err := io.ReadFull(r, p); err != nil {
   100  			return nil, err
   101  		}
   102  		for i := 0; i < len(p); i += 4 {
   103  			// BMP images are stored in BGRA order rather than RGBA order.
   104  			p[i+0], p[i+2] = p[i+2], p[i+0]
   105  		}
   106  	}
   107  	return rgba, nil
   108  }
   109  
   110  // Decode reads a BMP image from r and returns it as an image.Image.
   111  // Limitation: The file must be 8, 24 or 32 bits per pixel.
   112  func Decode(r io.Reader) (image.Image, error) {
   113  	c, bpp, topDown, err := decodeConfig(r)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	switch bpp {
   118  	case 8:
   119  		return decodePaletted(r, c, topDown)
   120  	case 24:
   121  		return decodeRGB(r, c, topDown)
   122  	case 32:
   123  		return decodeNRGBA(r, c, topDown)
   124  	}
   125  	panic("unreachable")
   126  }
   127  
   128  // DecodeConfig returns the color model and dimensions of a BMP image without
   129  // decoding the entire image.
   130  // Limitation: The file must be 8, 24 or 32 bits per pixel.
   131  func DecodeConfig(r io.Reader) (image.Config, error) {
   132  	config, _, _, err := decodeConfig(r)
   133  	return config, err
   134  }
   135  
   136  func decodeConfig(r io.Reader) (config image.Config, bitsPerPixel int, topDown bool, err error) {
   137  	// We only support those BMP images that are a BITMAPFILEHEADER
   138  	// immediately followed by a BITMAPINFOHEADER.
   139  	const (
   140  		fileHeaderLen = 14
   141  		infoHeaderLen = 40
   142  	)
   143  	var b [1024]byte
   144  	if _, err := io.ReadFull(r, b[:fileHeaderLen+infoHeaderLen]); err != nil {
   145  		return image.Config{}, 0, false, err
   146  	}
   147  	if string(b[:2]) != "BM" {
   148  		return image.Config{}, 0, false, errors.New("bmp: invalid format")
   149  	}
   150  	offset := readUint32(b[10:14])
   151  	if readUint32(b[14:18]) != infoHeaderLen {
   152  		return image.Config{}, 0, false, ErrUnsupported
   153  	}
   154  	width := int(int32(readUint32(b[18:22])))
   155  	height := int(int32(readUint32(b[22:26])))
   156  	if height < 0 {
   157  		height, topDown = -height, true
   158  	}
   159  	if width < 0 || height < 0 {
   160  		return image.Config{}, 0, false, ErrUnsupported
   161  	}
   162  	// We only support 1 plane, 8 or 24 bits per pixel and no compression.
   163  	planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34])
   164  	if planes != 1 || compression != 0 {
   165  		return image.Config{}, 0, false, ErrUnsupported
   166  	}
   167  	switch bpp {
   168  	case 8:
   169  		if offset != fileHeaderLen+infoHeaderLen+256*4 {
   170  			return image.Config{}, 0, false, ErrUnsupported
   171  		}
   172  		_, err = io.ReadFull(r, b[:256*4])
   173  		if err != nil {
   174  			return image.Config{}, 0, false, err
   175  		}
   176  		pcm := make(color.Palette, 256)
   177  		for i := range pcm {
   178  			// BMP images are stored in BGR order rather than RGB order.
   179  			// Every 4th byte is padding.
   180  			pcm[i] = color.RGBA{b[4*i+2], b[4*i+1], b[4*i+0], 0xFF}
   181  		}
   182  		return image.Config{ColorModel: pcm, Width: width, Height: height}, 8, topDown, nil
   183  	case 24:
   184  		if offset != fileHeaderLen+infoHeaderLen {
   185  			return image.Config{}, 0, false, ErrUnsupported
   186  		}
   187  		return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 24, topDown, nil
   188  	case 32:
   189  		if offset != fileHeaderLen+infoHeaderLen {
   190  			return image.Config{}, 0, false, ErrUnsupported
   191  		}
   192  		return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 32, topDown, nil
   193  	}
   194  	return image.Config{}, 0, false, ErrUnsupported
   195  }
   196  
   197  func init() {
   198  	image.RegisterFormat("bmp", "BM????\x00\x00\x00\x00", Decode, DecodeConfig)
   199  }