forked from nielsAD/gowarcraft3
-
Notifications
You must be signed in to change notification settings - Fork 0
/
blp.go
118 lines (94 loc) · 2.56 KB
/
blp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Author: Niels A.D.
// Project: gowarcraft3 (https://github.com/nielsAD/gowarcraft3)
// License: Mozilla Public License, v2.0
// Package blp is a BLIzzard Picture image format decoder.
package blp
import (
"errors"
"image"
"image/draw"
"image/jpeg"
"io"
"strings"
"github.com/nielsAD/gowarcraft3/protocol"
)
// Errors
var (
ErrBadFormat = errors.New("blp: Invalid file format")
ErrInvalidCompression = errors.New("blp: Compression not supported")
)
// Header constant for BLP files
var Header = protocol.DString("BLP1")
// Decode a BLP image. Only take the first image if it's a mipmap.
func Decode(r io.Reader) (image.Image, error) {
var b protocol.Buffer
if _, err := io.Copy(&b, r); err != nil {
return nil, err
}
var size = b.Size()
if size < 156 {
return nil, ErrBadFormat
}
if b.ReadLEDString() != Header {
return nil, ErrBadFormat
}
var compression = b.ReadUInt32() //compression
var alphaBits = b.ReadUInt32() //alpha
switch alphaBits {
case 0, 8:
default:
return nil, ErrBadFormat
}
b.ReadUInt32() //width
b.ReadUInt32() //height
b.ReadUInt32() //flags
b.ReadUInt32() //hasMipmap
var mmOffset [16]uint32
for i := 0; i < len(mmOffset); i++ {
mmOffset[i] = b.ReadUInt32()
}
var mmSize [16]uint32
for i := 0; i < len(mmOffset); i++ {
mmSize[i] = b.ReadUInt32()
}
switch compression {
// JPEG
case 0x00:
var hSize = b.ReadUInt32()
if b.Size() < int(hSize) || mmOffset[0] == 0 || mmSize[0] == 0 {
return nil, ErrBadFormat
}
var imgBuf = make([]byte, 0, hSize+mmSize[0])
imgBuf = append(imgBuf, b.ReadBlob(int(hSize))...)
var offset = int(mmOffset[0]) - size + b.Size()
if offset < 0 || offset+int(mmSize[0]) > b.Size() {
return nil, ErrBadFormat
}
b.Skip(offset)
imgBuf = append(imgBuf, b.ReadBlob(int(mmSize[0]))...)
jpg, err := jpeg.Decode(&protocol.Buffer{Bytes: imgBuf})
// Workaround for CMYK image without APP14 marker
if err != nil && strings.Contains(err.Error(), "Adobe APP14") {
imgBuf = append([]byte{
0xFF, 0xD8, //SOIMAGE
0xFF, 0xEE, 0x00, 0x0E, //App14Marker
'A', 'd', 'o', 'b', 'e',
0, 0, 0, 0, 0, 0, 0,
}, imgBuf[2:]...)
jpg, err = jpeg.Decode(&protocol.Buffer{Bytes: imgBuf})
}
if err != nil {
return nil, err
}
// BGR to RGB
var img = image.NewRGBA(jpg.Bounds())
draw.Draw(img, img.Rect, jpg, img.Rect.Min, draw.Src)
for i, m := 0, img.Rect.Dx()*img.Rect.Dy(); i < m; i++ {
img.Pix[i*4+0], img.Pix[i*4+2] = img.Pix[i*4+2], img.Pix[i*4+0]
}
return img, nil
// case 0x01: PALETTE
default:
return nil, ErrInvalidCompression
}
}