image/bmp: support v4 and v5 info header versions

Decode BITMAPV4INFOHEADER and BITMAPV5INFOHEADER in addition to
BITMAPINFOHEADER and check if any of their features are used. If this is
not the case, the bmp can be decoded as if it had the BITMAPINFOHEADER.
Otherwise an ErrUnsupported is returned.

The colormap.bmp and yellow_rose-small-v5.bmp files were generated using
imagemagick using the following conversions:

convert video-001.bmp -depth 8 -palette colormap.bmp
convert yellow_rose-small.bmp -format BMP5 yellow_rose-small-v5.bmp

The corresponding png files were created using imagemagick convert
without any arguments.

Fixes golang/go#27767

Change-Id: I5c0138b231c68132d39a29c71b61faa546921511
Reviewed-on: https://go-review.googlesource.com/c/141799
Reviewed-by: Nigel Tao <nigeltao@golang.org>
Run-TryBot: Nigel Tao <nigeltao@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
master
David Heuschmann 2018-10-10 16:55:54 +02:00 committed by Nigel Tao
parent 991ec62608
commit a9455cf03d
6 changed files with 22 additions and 7 deletions

View File

@ -137,20 +137,26 @@ func decodeConfig(r io.Reader) (config image.Config, bitsPerPixel int, topDown b
// We only support those BMP images that are a BITMAPFILEHEADER
// immediately followed by a BITMAPINFOHEADER.
const (
fileHeaderLen = 14
infoHeaderLen = 40
fileHeaderLen = 14
infoHeaderLen = 40
v4InfoHeaderLen = 108
v5InfoHeaderLen = 124
)
var b [1024]byte
if _, err := io.ReadFull(r, b[:fileHeaderLen+infoHeaderLen]); err != nil {
if _, err := io.ReadFull(r, b[:fileHeaderLen+4]); err != nil {
return image.Config{}, 0, false, err
}
if string(b[:2]) != "BM" {
return image.Config{}, 0, false, errors.New("bmp: invalid format")
}
offset := readUint32(b[10:14])
if readUint32(b[14:18]) != infoHeaderLen {
infoLen := readUint32(b[14:18])
if infoLen != infoHeaderLen && infoLen != v4InfoHeaderLen && infoLen != v5InfoHeaderLen {
return image.Config{}, 0, false, ErrUnsupported
}
if _, err := io.ReadFull(r, b[fileHeaderLen+4:fileHeaderLen+infoLen]); err != nil {
return image.Config{}, 0, false, err
}
width := int(int32(readUint32(b[18:22])))
height := int(int32(readUint32(b[22:26])))
if height < 0 {
@ -161,12 +167,19 @@ func decodeConfig(r io.Reader) (config image.Config, bitsPerPixel int, topDown b
}
// We only support 1 plane, 8 or 24 bits per pixel and no compression.
planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34])
// if compression is set to BITFIELDS, but the bitmask is set to the default bitmask
// that would be used if compression was set to 0, we can continue as if compression was 0
if compression == 3 && infoLen > infoHeaderLen &&
readUint32(b[54:58]) == 0xff0000 && readUint32(b[58:62]) == 0xff00 &&
readUint32(b[62:66]) == 0xff && readUint32(b[66:70]) == 0xff000000 {
compression = 0
}
if planes != 1 || compression != 0 {
return image.Config{}, 0, false, ErrUnsupported
}
switch bpp {
case 8:
if offset != fileHeaderLen+infoHeaderLen+256*4 {
if offset != fileHeaderLen+infoLen+256*4 {
return image.Config{}, 0, false, ErrUnsupported
}
_, err = io.ReadFull(r, b[:256*4])
@ -181,12 +194,12 @@ func decodeConfig(r io.Reader) (config image.Config, bitsPerPixel int, topDown b
}
return image.Config{ColorModel: pcm, Width: width, Height: height}, 8, topDown, nil
case 24:
if offset != fileHeaderLen+infoHeaderLen {
if offset != fileHeaderLen+infoLen {
return image.Config{}, 0, false, ErrUnsupported
}
return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 24, topDown, nil
case 32:
if offset != fileHeaderLen+infoHeaderLen {
if offset != fileHeaderLen+infoLen {
return image.Config{}, 0, false, ErrUnsupported
}
return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 32, topDown, nil

View File

@ -38,8 +38,10 @@ func compare(img0, img1 image.Image) error {
// same pixel data.
func TestDecode(t *testing.T) {
testCases := []string{
"colormap",
"video-001",
"yellow_rose-small",
"yellow_rose-small-v5",
}
for _, tc := range testCases {

BIN
testdata/colormap.bmp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
testdata/colormap.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
testdata/yellow_rose-small-v5.bmp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 B

BIN
testdata/yellow_rose-small-v5.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 939 B