/
encoder.go
169 lines (135 loc) · 3.45 KB
/
encoder.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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package xmp
import (
"encoding/binary"
"encoding/xml"
"io"
"io/ioutil"
"os"
"strconv"
)
func Write(fileName, employeeName string, timeStamp int64, lat, long float64, location string) error {
b, err := ioutil.ReadFile(fileName)
if err != nil {
return err
}
file, err := os.Create(fileName)
if err != nil {
return err
}
defer file.Close()
var employeeEscaped []byte
var locationEscaped []byte
employeeEscaped = strconv.AppendQuoteToASCII(employeeEscaped, employeeName)
locationEscaped = strconv.AppendQuoteToASCII(locationEscaped, location)
employeeName = string(employeeEscaped)
location = string(locationEscaped)
xmp := Xmpmeta{}
p := Profile{
Name: employeeName,
Timestamp: timeStamp,
Location: location,
Lat: lat,
Long: long,
}
xmp.RDF.Description.Profile = p
return encode(file, b, xmp)
}
// Refer to https://developers.google.com/speed/webp/docs/riff_container
func encode(w io.Writer, b []byte, v interface{}) error {
var buffer []byte
content, err := xml.Marshal(v)
if err != nil {
return err
}
chunkOffset := 0
chunkID := string(b[chunkOffset : chunkOffset+4])
if chunkID != "RIFF" {
return InvalidRIFF
}
if string(b[chunkOffset+8:chunkOffset+12]) != "WEBP" {
return InvalidWEBP
}
size := 0
chunkOffset = 8
containerSize := 4
for chunkOffset < len(b) && chunkID != "XMP " {
size += containerSize
chunkOffset += containerSize
chunkID = string(b[chunkOffset : chunkOffset+4])
containerSize = int(binary.LittleEndian.Uint32(b[chunkOffset+4:chunkOffset+8])) + 8
}
chunkID = string(b[12:16])
if chunkID != "VP8X" {
buffer = make([]byte, chunkOffset+len(content)+26)
copy(buffer, b)
copy(buffer[30:], buffer[12:])
width := 0
height := 0
alphaF := false
if chunkID == "VP8 " {
width = (((int(buffer[45]) << 8) | int(buffer[44])) & 0x3fff) - 1
height = (((int(buffer[47]) << 8) | int(buffer[46])) & 0x3fff) - 1
} else {
width = (((int(buffer[40]) << 8) | int(buffer[39])) & 0x3fff) - 1
height = ((int(buffer[42]) << 10) | (int(buffer[41]) << 2) | (int(buffer[40])>>6)&0x3fff) - 1
// alpha flag
if ((buffer[42] >> 4) & 0x1) == 0x1 {
alphaF = true
}
}
// VP8X
buffer[12] = 0x56
buffer[13] = 0x50
buffer[14] = 0x38
buffer[15] = 0x58
// VP8X size
buffer[16] = 0xa
buffer[17] = 0x0
buffer[18] = 0x0
buffer[19] = 0x0
// Flags
buffer[20] = 0x4
if alphaF {
buffer[20] = buffer[20] | 0x10
}
// Reserved
buffer[21] = 0x0
buffer[22] = 0x0
buffer[23] = 0x0
// Canvas dimension
buffer[24] = byte(width)
buffer[25] = byte(width >> 8)
buffer[26] = byte(width >> 16)
buffer[27] = byte(height)
buffer[28] = byte(height >> 8)
buffer[29] = byte(height >> 16)
chunkOffset += 18
size += 18
} else {
buffer = make([]byte, chunkOffset+len(content)+8)
copy(buffer, b)
buffer[20] = buffer[20] | 0x4
}
// XMP ID
buffer[chunkOffset] = 0x58
buffer[chunkOffset+1] = 0x4d
buffer[chunkOffset+2] = 0x50
buffer[chunkOffset+3] = 0x20
chunkOffset += 4
// XMP Size
buffer[chunkOffset] = byte(len(content))
buffer[chunkOffset+1] = byte(len(content) >> 8)
buffer[chunkOffset+2] = byte(len(content) >> 16)
buffer[chunkOffset+3] = byte(len(content) >> 24)
chunkOffset += 4
// XMP Content
copy(buffer[chunkOffset:], content)
size += 8 + len(content)
// Update container size
buffer[4] = byte(size)
buffer[5] = byte(size >> 8)
buffer[6] = byte(size >> 16)
buffer[7] = byte(size >> 24)
_, err = w.Write(buffer)
return err
}