adam-gui/vendor/github.com/jsummers/gobmp/reader.go

588 lines
14 KiB
Go
Raw Permalink Normal View History

2024-04-29 19:13:50 +02:00
// ◄◄◄ gobmp/reader.go ►►►
// Copyright © 2012 Jason Summers
// Use of this code is governed by an MIT-style license that can
// be found in the readme.md file.
//
// BMP file decoder
//
// Package gobmp implements a BMP image decoder and encoder.
package gobmp
import "image"
import "image/color"
import "io"
import "fmt"
const (
bI_RGB = 0
bI_RLE8 = 1
bI_RLE4 = 2
bI_BITFIELDS = 3
)
type bitFieldsInfo struct {
mask uint32
shift uint
scale float64 // Amount to multiply the sample value by, to scale it to [0..255]
}
type decoder struct {
r io.Reader
img_Paletted *image.Paletted // Used if dstHasPalette is true
img_NRGBA *image.NRGBA // Used otherwise
bfOffBits uint32
headerSize uint32
width int
height int
bitCount int
biCompression uint32
isTopDown bool
srcPalNumEntries int
srcPalBytesPerEntry int
srcPalSizeInBytes int
dstPalNumEntries int
dstHasPalette bool
dstPalette color.Palette
hasBitFieldsSegment bool
bitFieldsSegmentSize int
bitFields [4]bitFieldsInfo
}
// An UnsupportedError reports that the input uses a valid but unimplemented
// BMP feature.
type UnsupportedError string
func (e UnsupportedError) Error() string { return "bmp: unsupported feature: " + string(e) }
// A FormatError reports that the input is not a valid BMP file.
type FormatError string
func (e FormatError) Error() string { return "bmp: invalid format: " + string(e) }
func getWORD(b []byte) uint32 {
return uint32(b[0]) | uint32(b[1])<<8
}
func getDWORD(b []byte) uint32 {
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
}
func decodeRow_paletted(d *decoder, buf []byte, j int) error {
for i := 0; i < d.width; i++ {
var v byte
switch d.bitCount {
case 8:
v = buf[i]
case 4:
v = (buf[i/2] >> (4 * (1 - uint(i)%2))) & 0x0f
case 2:
v = (buf[i/4] >> (2 * (3 - uint(i)%4))) & 0x03
case 1:
v = (buf[i/8] >> (1 * (7 - uint(i)%8))) & 0x01
}
if int(v) >= d.dstPalNumEntries {
// Out-of-range palette index.
// Most BMP viewers use the first palette color for such pixels, so
// that's what we'll do.
v = 0
}
d.img_Paletted.Pix[j*d.img_Paletted.Stride+i] = v
}
return nil
}
func decodeRow_16or32(d *decoder, buf []byte, j int) error {
for i := 0; i < d.width; i++ {
var v uint32
if d.bitCount == 16 {
v = uint32(getWORD(buf[i*2 : i*2+2]))
} else { // bitCount == 32
v = getDWORD(buf[i*4 : i*4+4])
}
for k := 0; k < 4; k++ {
var sv uint8
if d.bitFields[k].mask == 0 {
if k == 3 {
// If alpha mask is missing, make the pixel opaque.
sv = 255
} else {
// If some other mask is missing, who knows what to do?
sv = 0
}
} else {
sv = uint8(0.5 + float64((v&d.bitFields[k].mask)>>d.bitFields[k].shift)*
d.bitFields[k].scale)
}
d.img_NRGBA.Pix[j*d.img_NRGBA.Stride+i*4+k] = sv
}
}
return nil
}
func decodeRow_24(d *decoder, buf []byte, j int) error {
for i := 0; i < d.width; i++ {
for k := 0; k < 3; k++ {
d.img_NRGBA.Pix[j*d.img_NRGBA.Stride+i*4+k] = buf[i*3+2-k]
}
d.img_NRGBA.Pix[j*d.img_NRGBA.Stride+i*4+3] = 255
}
return nil
}
type decodeRowFuncType func(d *decoder, buf []byte, j int) error
var rowDecoders = map[int]decodeRowFuncType{
1: decodeRow_paletted,
2: decodeRow_paletted,
4: decodeRow_paletted,
8: decodeRow_paletted,
16: decodeRow_16or32,
24: decodeRow_24,
32: decodeRow_16or32,
}
func (d *decoder) readBitsUncompressed() error {
var err error
srcRowStride := ((d.width*d.bitCount + 31) / 32) * 4
buf := make([]byte, srcRowStride)
decodeRowFunc := rowDecoders[d.bitCount]
if decodeRowFunc == nil {
return nil
}
for srcRow := 0; srcRow < d.height; srcRow++ {
var dstRow int
if d.isTopDown {
dstRow = srcRow
} else {
dstRow = d.height - srcRow - 1
}
_, err = io.ReadFull(d.r, buf)
if err != nil {
return err
}
err = decodeRowFunc(d, buf, dstRow)
if err != nil {
return err
}
}
return nil
}
func (d *decoder) skipBytes(n int) error {
var buf [1024]byte
for n > 0 {
bytesToRead := len(buf)
if bytesToRead > n {
bytesToRead = n
}
_, err := io.ReadFull(d.r, buf[:bytesToRead])
if err != nil {
return err
}
n -= bytesToRead
}
return nil
}
// If there is a gap before the bits, skip over it.
func (d *decoder) readGap() error {
currentOffset := 14 + int(d.headerSize) + d.bitFieldsSegmentSize + d.srcPalSizeInBytes
if currentOffset == int(d.bfOffBits) {
return nil
}
if currentOffset > int(d.bfOffBits) {
return FormatError("bad bfOffBits field")
}
gapSize := int(d.bfOffBits) - currentOffset
return d.skipBytes(gapSize)
}
// Read a 12-byte BITMAPCOREHEADER.
func decodeInfoHeader12(d *decoder, h []byte, configOnly bool) error {
d.width = int(getWORD(h[4:6]))
d.height = int(getWORD(h[6:8]))
d.bitCount = int(getWORD(h[10:12]))
d.srcPalBytesPerEntry = 3
if d.bitCount >= 1 && d.bitCount <= 8 {
d.srcPalNumEntries = 1 << uint(d.bitCount)
}
// Figure out how many palette entries there are.
// A full-size palette is expected, but if the palette is overlapped by the
// bitmap, we assume the palette is less than full size.
paletteStart := 14 + int(d.headerSize)
paletteEnd := paletteStart + d.srcPalBytesPerEntry*d.srcPalNumEntries
if int(d.bfOffBits) >= paletteStart+d.srcPalBytesPerEntry && int(d.bfOffBits) < paletteEnd {
d.srcPalNumEntries = (int(d.bfOffBits) - paletteStart) / 3
}
return nil
}
// Read a 40-byte BITMAPINFOHEADER.
// Use of this function does not imply that the entire header is 40 bytes.
// We may just be decoding the first 40 bytes of a 108- or 124-byte header.
func decodeInfoHeader40(d *decoder, h []byte, configOnly bool) error {
d.width = int(int32(getDWORD(h[4:8])))
d.height = int(int32(getDWORD(h[8:12])))
if d.height < 0 {
d.isTopDown = true
d.height = -d.height
}
d.bitCount = int(getWORD(h[14:16]))
if configOnly {
return nil
}
if len(h) >= 20 {
d.biCompression = getDWORD(h[16:20])
}
if d.biCompression == bI_BITFIELDS && d.headerSize == 40 && d.bitCount != 1 {
d.hasBitFieldsSegment = true
d.bitFieldsSegmentSize = 12
}
if d.biCompression == bI_RGB {
if d.bitCount == 16 {
// Default bitfields for 16-bit images:
d.recordBitFields(0x7c00, 0x03e0, 0x001f, 0)
} else if d.bitCount == 32 {
// Default bitfields for 32-bit images:
d.recordBitFields(0x00ff0000, 0x0000ff00, 0x000000ff, 0)
}
}
var biClrUsed uint32
if len(h) >= 36 {
biClrUsed = getDWORD(h[32:36])
}
if biClrUsed > 10000 {
return FormatError(fmt.Sprintf("bad palette size %d", biClrUsed))
}
// Figure out how many colors (that we care about) are in the palette.
if d.bitCount >= 1 && d.bitCount <= 8 {
if biClrUsed == 0 || biClrUsed > (1<<uint(d.bitCount)) {
d.srcPalNumEntries = 1 << uint(d.bitCount)
} else {
d.srcPalNumEntries = int(biClrUsed)
}
} else {
d.srcPalNumEntries = 0
}
d.srcPalBytesPerEntry = 4
if d.headerSize == 64 && d.srcPalNumEntries > 0 {
// A hack to allow (invalid?) OS/2v2 BMPs that have 3 bytes per palette
// entry instead of 4.
if 14+d.headerSize+uint32(d.bitFieldsSegmentSize)+3*uint32(d.srcPalNumEntries) ==
d.bfOffBits {
d.srcPalBytesPerEntry = 3
}
}
return nil
}
func decodeInfoHeader108(d *decoder, h []byte, configOnly bool) error {
var err error
err = decodeInfoHeader40(d, h[:40], configOnly)
if err != nil {
return err
}
if d.biCompression == bI_BITFIELDS {
var bf_alpha uint32
if len(h) >= 56 {
bf_alpha = getDWORD(h[52:56])
}
d.recordBitFields(getDWORD(h[40:44]), getDWORD(h[44:48]),
getDWORD(h[48:52]), bf_alpha)
}
return nil
}
type decodeInfoHeaderFuncType func(d *decoder, h []byte, configOnly bool) error
func readInfoHeader(d *decoder, configOnly bool) error {
var h []byte
var err error
var decodeFn decodeInfoHeaderFuncType
switch d.headerSize {
case 12:
decodeFn = decodeInfoHeader12
case 16, 20, 24, 32, 36, 40, 42, 44, 46, 48, 60, 64:
decodeFn = decodeInfoHeader40
case 52, 56, 108, 124:
decodeFn = decodeInfoHeader108
default:
return UnsupportedError(fmt.Sprintf("BMP version (header size %d)", d.headerSize))
}
// Read the rest of the infoheader
h = make([]byte, d.headerSize)
_, err = io.ReadFull(d.r, h[4:])
if err != nil {
return err
}
err = decodeFn(d, h, configOnly)
if err != nil {
return err
}
if d.width < 1 {
return FormatError(fmt.Sprintf("bad width %d", d.width))
}
if d.height < 1 {
return FormatError(fmt.Sprintf("bad height %d", d.height))
}
if d.bitCount >= 1 && d.bitCount <= 8 {
d.dstHasPalette = true
}
d.srcPalSizeInBytes = d.srcPalNumEntries * d.srcPalBytesPerEntry
return nil
}
func (d *decoder) recordBitFields(r, g, b, a uint32) {
d.bitFields[0].mask = r
d.bitFields[1].mask = g
d.bitFields[2].mask = b
d.bitFields[3].mask = a
// Based on .mask, set the other fields of the bitFields struct
for k := 0; k < 4; k++ {
if d.bitFields[k].mask == 0 {
continue
}
// Starting with the low bit, count the number of 0 bits before
// the first 1 bit.
tmpMask := d.bitFields[k].mask
for tmpMask&0x1 == 0 {
d.bitFields[k].shift++
tmpMask >>= 1
}
d.bitFields[k].scale = 255.0 / float64(tmpMask)
}
}
func (d *decoder) readBitFieldsSegment() error {
buf := make([]byte, d.bitFieldsSegmentSize)
_, err := io.ReadFull(d.r, buf[:])
if err != nil {
return err
}
d.recordBitFields(getDWORD(buf[0:4]), getDWORD(buf[4:8]),
getDWORD(buf[8:12]), 0)
return nil
}
func (d *decoder) readPalette() error {
var err error
buf := make([]byte, d.srcPalSizeInBytes)
_, err = io.ReadFull(d.r, buf)
if err != nil {
return err
}
if !d.dstHasPalette {
d.dstPalNumEntries = 0
return nil
}
d.dstPalNumEntries = d.srcPalNumEntries
if d.dstPalNumEntries > 256 {
d.dstPalNumEntries = 256
}
d.dstPalette = make(color.Palette, d.dstPalNumEntries)
for i := 0; i < d.dstPalNumEntries; i++ {
d.dstPalette[i] = color.RGBA{buf[i*d.srcPalBytesPerEntry+2],
buf[i*d.srcPalBytesPerEntry+1],
buf[i*d.srcPalBytesPerEntry+0], 255}
}
return nil
}
func (d *decoder) decodeFileHeader(b []byte) error {
if b[0] != 0x42 || b[1] != 0x4d {
return FormatError("not a BMP file")
}
d.bfOffBits = getDWORD(b[10:14])
return nil
}
func (d *decoder) readHeaders(configOnly bool) error {
var fh [18]byte
var err error
// Read the file header, and the first 4 bytes of the info header
_, err = io.ReadFull(d.r, fh[:])
if err != nil {
return err
}
err = d.decodeFileHeader(fh[0:14])
if err != nil {
return err
}
d.headerSize = getDWORD(fh[14:18])
err = readInfoHeader(d, configOnly)
if err != nil {
return err
}
return nil
}
func (d *decoder) readMain(r io.Reader, configOnly bool) (image.Image, error) {
var err error
// Read the FILEHEADER and INFOHEADER.
err = d.readHeaders(false)
if err != nil {
return nil, err
}
// Make sure bitcount and "compression" are valid and compatible.
switch d.biCompression {
case bI_RGB:
// We allow bitCount=2 because Windows CE defines it to be valid
// (at least if headerSize=40).
if d.bitCount != 1 && d.bitCount != 2 && d.bitCount != 4 && d.bitCount != 8 &&
d.bitCount != 16 && d.bitCount != 24 && d.bitCount != 32 {
return nil, FormatError(fmt.Sprintf("bad bit count %d", d.bitCount))
}
case bI_RLE4:
if d.bitCount != 4 {
return nil, FormatError(fmt.Sprintf("bad RLE4 bit count %d", d.bitCount))
}
case bI_RLE8:
if d.bitCount != 8 {
return nil, FormatError(fmt.Sprintf("bad RLE8 bit count %d", d.bitCount))
}
case bI_BITFIELDS:
if d.bitCount == 1 {
return nil, UnsupportedError("Huffman 1D compression")
} else if d.bitCount != 16 && d.bitCount != 32 {
return nil, FormatError(fmt.Sprintf("bad BITFIELDS bit count %d", d.bitCount))
}
default:
return nil, UnsupportedError(fmt.Sprintf("compression or image type %d", d.biCompression))
}
// Assuming 'int' is 32 bits, an NRGBA image can't handle more than (2^31-1)/4
// pixels. This test is more conservative than it could be.
if d.width > 46340 || d.height > 46340 || d.width*d.height >= 0x20000000 {
return nil, UnsupportedError("dimensions too large")
}
// Read the BITFIELDS segment, if present.
if d.hasBitFieldsSegment {
err = d.readBitFieldsSegment()
if err != nil {
return nil, err
}
}
// Read the palette, if present.
if d.srcPalNumEntries > 0 {
err = d.readPalette()
if err != nil {
return nil, err
}
}
if configOnly {
return nil, nil
}
// Create the target image.
if d.dstHasPalette {
d.img_Paletted = image.NewPaletted(image.Rect(0, 0, d.width, d.height), d.dstPalette)
} else {
d.img_NRGBA = image.NewNRGBA(image.Rect(0, 0, d.width, d.height))
}
// Skip over any unused space preceding the bitmap bits.
err = d.readGap()
if err != nil {
return nil, err
}
// Read the bitmap bits.
if d.biCompression == bI_RLE4 || d.biCompression == bI_RLE8 {
err = d.readBitsRLE()
} else {
err = d.readBitsUncompressed()
}
if err != nil {
return nil, err
}
if d.dstHasPalette {
return d.img_Paletted, nil
}
return d.img_NRGBA, nil
}
// Decode reads a BMP image from r and returns it as an image.Image.
func Decode(r io.Reader) (image.Image, error) {
var err error
d := new(decoder)
d.r = r
im, err := d.readMain(r, false)
return im, err
}
// DecodeConfig returns the color model and dimensions of the BMP image without
// decoding the entire image.
func DecodeConfig(r io.Reader) (image.Config, error) {
var err error
var cfg image.Config
d := new(decoder)
d.r = r
_, err = d.readMain(r, true)
if err != nil {
return cfg, err
}
cfg.Width = d.width
cfg.Height = d.height
if d.dstHasPalette {
cfg.ColorModel = d.dstPalette
} else {
cfg.ColorModel = color.NRGBAModel
}
return cfg, nil
}
func init() {
image.RegisterFormat("bmp", "BM", Decode, DecodeConfig)
}