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 }