2019-12-25 11:06:15 +02:00
|
|
|
package imagemeta
|
2019-09-25 17:46:59 +02:00
|
|
|
|
|
|
|
import (
|
2020-02-14 15:32:05 +02:00
|
|
|
"bytes"
|
2019-09-25 17:46:59 +02:00
|
|
|
"encoding/binary"
|
|
|
|
"io"
|
|
|
|
)
|
|
|
|
|
2019-12-25 11:06:15 +02:00
|
|
|
type IcoMeta struct {
|
|
|
|
Meta
|
|
|
|
offset int
|
|
|
|
size int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *IcoMeta) BestImageOffset() int {
|
|
|
|
return m.offset
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *IcoMeta) BestImageSize() int {
|
|
|
|
return m.size
|
|
|
|
}
|
|
|
|
|
2019-10-03 17:43:33 +02:00
|
|
|
func icoBestSize(r io.Reader) (width, height byte, offset uint32, size uint32, err error) {
|
2019-09-25 17:46:59 +02:00
|
|
|
var tmp [16]byte
|
|
|
|
|
2019-09-30 12:03:07 +02:00
|
|
|
if _, err = io.ReadFull(r, tmp[:6]); err != nil {
|
|
|
|
return
|
2019-09-25 17:46:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
count := binary.LittleEndian.Uint16(tmp[4:6])
|
|
|
|
|
|
|
|
for i := uint16(0); i < count; i++ {
|
2019-09-30 12:03:07 +02:00
|
|
|
if _, err = io.ReadFull(r, tmp[:]); err != nil {
|
|
|
|
return
|
2019-09-25 17:46:59 +02:00
|
|
|
}
|
|
|
|
|
2019-09-30 12:03:07 +02:00
|
|
|
if tmp[0] > width || tmp[1] > height || tmp[0] == 0 || tmp[1] == 0 {
|
2019-09-25 17:46:59 +02:00
|
|
|
width = tmp[0]
|
|
|
|
height = tmp[1]
|
2019-10-03 17:43:33 +02:00
|
|
|
size = binary.LittleEndian.Uint32(tmp[8:12])
|
|
|
|
offset = binary.LittleEndian.Uint32(tmp[12:16])
|
2019-09-25 17:46:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-30 12:03:07 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-10-03 17:43:33 +02:00
|
|
|
func BestIcoPage(r io.Reader) (int, int, error) {
|
|
|
|
_, _, offset, size, err := icoBestSize(r)
|
|
|
|
return int(offset), int(size), err
|
2019-09-30 12:03:07 +02:00
|
|
|
}
|
|
|
|
|
2019-12-25 11:06:15 +02:00
|
|
|
func DecodeIcoMeta(r io.Reader) (*IcoMeta, error) {
|
|
|
|
bwidth, bheight, offset, size, err := icoBestSize(r)
|
2019-09-30 12:03:07 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
width := int(bwidth)
|
|
|
|
height := int(bheight)
|
|
|
|
|
|
|
|
if width == 0 {
|
|
|
|
width = 256
|
|
|
|
}
|
|
|
|
|
|
|
|
if height == 0 {
|
|
|
|
height = 256
|
|
|
|
}
|
|
|
|
|
2019-12-25 11:06:15 +02:00
|
|
|
return &IcoMeta{
|
|
|
|
Meta: &meta{
|
|
|
|
format: "ico",
|
|
|
|
width: width,
|
|
|
|
height: height,
|
|
|
|
},
|
|
|
|
offset: int(offset),
|
|
|
|
size: int(size),
|
2019-09-25 17:46:59 +02:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
2019-12-25 11:06:15 +02:00
|
|
|
RegisterFormat(
|
|
|
|
"\x00\x00\x01\x00",
|
|
|
|
func(r io.Reader) (Meta, error) { return DecodeIcoMeta(r) },
|
|
|
|
)
|
2019-09-25 17:46:59 +02:00
|
|
|
}
|
2020-02-14 15:32:05 +02:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|