Browse Source

Fix loadind BMP stored in ICO

DarthSim 5 years ago
parent
commit
1d1caeb06a
3 changed files with 63 additions and 2 deletions
  1. 2 0
      CHANGELOG.md
  2. 49 0
      imagemeta/ico.go
  3. 12 2
      process.go

+ 2 - 0
CHANGELOG.md

@@ -1,6 +1,8 @@
 # Changelog
 
 ## [Unreleased]
+### Fixed
+- Fix loadind BMP stored in ICO.
 
 ## [2.10.0] - 2020-02-13
 ### Added

+ 49 - 0
imagemeta/ico.go

@@ -1,6 +1,7 @@
 package imagemeta
 
 import (
+	"bytes"
 	"encoding/binary"
 	"io"
 )
@@ -83,3 +84,51 @@ func init() {
 		func(r io.Reader) (Meta, error) { return DecodeIcoMeta(r) },
 	)
 }
+
+// FixBmpHeader fixes an incomplete header of BMP stored in ICO
+func FixBmpHeader(b []byte) ([]byte, error) {
+	buf := new(bytes.Buffer)
+
+	fileSize := uint32(14 + len(b))
+
+	buf.Grow(int(fileSize))
+
+	buf.Write(bmpMagick)
+
+	if err := binary.Write(buf, binary.LittleEndian, &fileSize); err != nil {
+		return nil, err
+	}
+
+	reserved := uint32(0)
+	if err := binary.Write(buf, binary.LittleEndian, &reserved); err != nil {
+		return nil, err
+	}
+
+	colorUsed := binary.LittleEndian.Uint32(b[32:36])
+	bitCount := binary.LittleEndian.Uint16(b[14:16])
+
+	var pixOffset uint32
+	if colorUsed == 0 && bitCount <= 8 {
+		pixOffset = 14 + 40 + 4*(1<<bitCount)
+	} else {
+		pixOffset = 14 + 40 + 4*colorUsed
+	}
+
+	if err := binary.Write(buf, binary.LittleEndian, &pixOffset); err != nil {
+		return nil, err
+	}
+
+	// Write size and width
+	buf.Write(b[:8])
+
+	// For some reason ICO stores double height
+	height := binary.LittleEndian.Uint32(b[8:12]) / 2
+	if err := binary.Write(buf, binary.LittleEndian, &height); err != nil {
+		return nil, err
+	}
+
+	// Write the rest
+	buf.Write(b[12:])
+
+	return buf.Bytes(), nil
+}

+ 12 - 2
process.go

@@ -587,12 +587,22 @@ func getIcoData(imgdata *imageData) (*imageData, error) {
 
 	data := imgdata.Data[offset : offset+size]
 
+	var format string
+
 	meta, err := imagemeta.DecodeMeta(bytes.NewReader(data))
 	if err != nil {
-		return nil, err
+		// Looks like it's BMP with an incomplete header
+		if d, err := imagemeta.FixBmpHeader(data); err == nil {
+			format = "bmp"
+			data = d
+		} else {
+			return nil, err
+		}
+	} else {
+		format = meta.Format()
 	}
 
-	if imgtype, ok := imageTypes[meta.Format()]; ok && vipsTypeSupportLoad[imgtype] {
+	if imgtype, ok := imageTypes[format]; ok && vipsTypeSupportLoad[imgtype] {
 		return &imageData{
 			Data: data,
 			Type: imgtype,