git clone https://git.lucas.co/go_mono.git
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 <[email protected]>
Run-TryBot: Nigel Tao <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
bmp/reader.go | 27 ++++++++++++++++++++-------
bmp/reader_test.go | 2 ++
testdata/colormap.bmp | Bin 0 -> 16818 bytes
testdata/colormap.png | Bin 0 -> 27690 bytes
testdata/yellow_rose-small-v5.bmp | Bin 0 -> 906 bytes
testdata/yellow_rose-small-v5.png | Bin 0 -> 939 bytes
6 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/bmp/reader.go b/bmp/reader.go
index a0f2715..32bbda9 100644
--- a/bmp/reader.go
+++ b/bmp/reader.go
@@ -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
diff --git a/bmp/reader_test.go b/bmp/reader_test.go
index 91b4391..e54aafd 100644
--- a/bmp/reader_test.go
+++ b/bmp/reader_test.go
@@ -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 {
diff --git a/testdata/colormap.bmp b/testdata/colormap.bmp
new file mode 100644
index 0000000..d5d394c
Binary files /dev/null and b/testdata/colormap.bmp differ
diff --git a/testdata/colormap.png b/testdata/colormap.png
new file mode 100644
index 0000000..c0dadc5
Binary files /dev/null and b/testdata/colormap.png differ
diff --git a/testdata/yellow_rose-small-v5.bmp b/testdata/yellow_rose-small-v5.bmp
new file mode 100644
index 0000000..b1d5ab4
Binary files /dev/null and b/testdata/yellow_rose-small-v5.bmp differ
diff --git a/testdata/yellow_rose-small-v5.png b/testdata/yellow_rose-small-v5.png
new file mode 100644
index 0000000..8dad370
Binary files /dev/null and b/testdata/yellow_rose-small-v5.png differ