gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

reader.go (8528B)


      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, allowAlpha 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 			if !allowAlpha {
    106 				p[i+3] = 0xFF
    107 			}
    108 		}
    109 	}
    110 	return rgba, nil
    111 }
    112 
    113 // Decode reads a BMP image from r and returns it as an image.Image.
    114 // Limitation: The file must be 8, 24 or 32 bits per pixel.
    115 func Decode(r io.Reader) (image.Image, error) {
    116 	c, bpp, topDown, allowAlpha, err := decodeConfig(r)
    117 	if err != nil {
    118 		return nil, err
    119 	}
    120 	switch bpp {
    121 	case 8:
    122 		return decodePaletted(r, c, topDown)
    123 	case 24:
    124 		return decodeRGB(r, c, topDown)
    125 	case 32:
    126 		return decodeNRGBA(r, c, topDown, allowAlpha)
    127 	}
    128 	panic("unreachable")
    129 }
    130 
    131 // DecodeConfig returns the color model and dimensions of a BMP image without
    132 // decoding the entire image.
    133 // Limitation: The file must be 8, 24 or 32 bits per pixel.
    134 func DecodeConfig(r io.Reader) (image.Config, error) {
    135 	config, _, _, _, err := decodeConfig(r)
    136 	return config, err
    137 }
    138 
    139 func decodeConfig(r io.Reader) (config image.Config, bitsPerPixel int, topDown bool, allowAlpha bool, err error) {
    140 	// We only support those BMP images with one of the following DIB headers:
    141 	// - BITMAPINFOHEADER (40 bytes)
    142 	// - BITMAPV4HEADER (108 bytes)
    143 	// - BITMAPV5HEADER (124 bytes)
    144 	const (
    145 		fileHeaderLen   = 14
    146 		infoHeaderLen   = 40
    147 		v4InfoHeaderLen = 108
    148 		v5InfoHeaderLen = 124
    149 	)
    150 	var b [1024]byte
    151 	if _, err := io.ReadFull(r, b[:fileHeaderLen+4]); err != nil {
    152 		if err == io.EOF {
    153 			err = io.ErrUnexpectedEOF
    154 		}
    155 		return image.Config{}, 0, false, false, err
    156 	}
    157 	if string(b[:2]) != "BM" {
    158 		return image.Config{}, 0, false, false, errors.New("bmp: invalid format")
    159 	}
    160 	offset := readUint32(b[10:14])
    161 	infoLen := readUint32(b[14:18])
    162 	if infoLen != infoHeaderLen && infoLen != v4InfoHeaderLen && infoLen != v5InfoHeaderLen {
    163 		return image.Config{}, 0, false, false, ErrUnsupported
    164 	}
    165 	if _, err := io.ReadFull(r, b[fileHeaderLen+4:fileHeaderLen+infoLen]); err != nil {
    166 		if err == io.EOF {
    167 			err = io.ErrUnexpectedEOF
    168 		}
    169 		return image.Config{}, 0, false, false, err
    170 	}
    171 	width := int(int32(readUint32(b[18:22])))
    172 	height := int(int32(readUint32(b[22:26])))
    173 	if height < 0 {
    174 		height, topDown = -height, true
    175 	}
    176 	if width < 0 || height < 0 {
    177 		return image.Config{}, 0, false, false, ErrUnsupported
    178 	}
    179 	// We only support 1 plane and 8, 24 or 32 bits per pixel and no
    180 	// compression.
    181 	planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34])
    182 	// if compression is set to BI_BITFIELDS, but the bitmask is set to the default bitmask
    183 	// that would be used if compression was set to 0, we can continue as if compression was 0
    184 	if compression == 3 && infoLen > infoHeaderLen &&
    185 		readUint32(b[54:58]) == 0xff0000 && readUint32(b[58:62]) == 0xff00 &&
    186 		readUint32(b[62:66]) == 0xff && readUint32(b[66:70]) == 0xff000000 {
    187 		compression = 0
    188 	}
    189 	if planes != 1 || compression != 0 {
    190 		return image.Config{}, 0, false, false, ErrUnsupported
    191 	}
    192 	switch bpp {
    193 	case 8:
    194 		if offset != fileHeaderLen+infoLen+256*4 {
    195 			return image.Config{}, 0, false, false, ErrUnsupported
    196 		}
    197 		_, err = io.ReadFull(r, b[:256*4])
    198 		if err != nil {
    199 			return image.Config{}, 0, false, false, err
    200 		}
    201 		pcm := make(color.Palette, 256)
    202 		for i := range pcm {
    203 			// BMP images are stored in BGR order rather than RGB order.
    204 			// Every 4th byte is padding.
    205 			pcm[i] = color.RGBA{b[4*i+2], b[4*i+1], b[4*i+0], 0xFF}
    206 		}
    207 		return image.Config{ColorModel: pcm, Width: width, Height: height}, 8, topDown, false, nil
    208 	case 24:
    209 		if offset != fileHeaderLen+infoLen {
    210 			return image.Config{}, 0, false, false, ErrUnsupported
    211 		}
    212 		return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 24, topDown, false, nil
    213 	case 32:
    214 		if offset != fileHeaderLen+infoLen {
    215 			return image.Config{}, 0, false, false, ErrUnsupported
    216 		}
    217 		// 32 bits per pixel is possibly RGBX (X is padding) or RGBA (A is
    218 		// alpha transparency). However, for BMP images, "Alpha is a
    219 		// poorly-documented and inconsistently-used feature" says
    220 		// https://source.chromium.org/chromium/chromium/src/+/bc0a792d7ebc587190d1a62ccddba10abeea274b:third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.cc;l=621
    221 		//
    222 		// That goes on to say "BITMAPV3HEADER+ have an alpha bitmask in the
    223 		// info header... so we respect it at all times... [For earlier
    224 		// (smaller) headers we] ignore alpha in Windows V3 BMPs except inside
    225 		// ICO files".
    226 		//
    227 		// "Ignore" means to always set alpha to 0xFF (fully opaque):
    228 		// https://source.chromium.org/chromium/chromium/src/+/bc0a792d7ebc587190d1a62ccddba10abeea274b:third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.h;l=272
    229 		//
    230 		// Confusingly, "Windows V3" does not correspond to BITMAPV3HEADER, but
    231 		// instead corresponds to the earlier (smaller) BITMAPINFOHEADER:
    232 		// https://source.chromium.org/chromium/chromium/src/+/bc0a792d7ebc587190d1a62ccddba10abeea274b:third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.cc;l=258
    233 		//
    234 		// This Go package does not support ICO files and the (infoLen >
    235 		// infoHeaderLen) condition distinguishes BITMAPINFOHEADER (40 bytes)
    236 		// vs later (larger) headers.
    237 		allowAlpha = infoLen > infoHeaderLen
    238 		return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 32, topDown, allowAlpha, nil
    239 	}
    240 	return image.Config{}, 0, false, false, ErrUnsupported
    241 }
    242 
    243 func init() {
    244 	image.RegisterFormat("bmp", "BM????\x00\x00\x00\x00", Decode, DecodeConfig)
    245 }