89 lines
2.3 KiB
Go
89 lines
2.3 KiB
Go
package imaging
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
)
|
|
|
|
const (
|
|
riffHeaderSize = 12
|
|
riffChunkHeaderSize = 8
|
|
webpVP8X = "VP8X"
|
|
webpEXIF = "EXIF"
|
|
webpXMP = "XMP "
|
|
webpEXIFFlag = 0x08
|
|
webpXMPFlag = 0x04
|
|
)
|
|
|
|
func stripWebP(data []byte) ([]byte, error) {
|
|
if len(data) < riffHeaderSize || string(data[:4]) != "RIFF" || string(data[8:12]) != "WEBP" {
|
|
return nil, errMalformedContainer
|
|
}
|
|
declared := int(binary.LittleEndian.Uint32(data[4:])) + riffChunkHeaderSize
|
|
if declared > len(data) || declared < riffHeaderSize {
|
|
return nil, errMalformedContainer
|
|
}
|
|
data = data[:declared]
|
|
|
|
var chunks [][]byte
|
|
orientation := uint16(uprightOrientation)
|
|
for pos := riffHeaderSize; pos < len(data); {
|
|
available := len(data) - pos - riffChunkHeaderSize
|
|
if available < 0 {
|
|
return nil, errMalformedContainer
|
|
}
|
|
size := binary.LittleEndian.Uint32(data[pos+4:])
|
|
padded := uint64(size) + uint64(size%2)
|
|
if padded > uint64(available) {
|
|
return nil, errMalformedContainer
|
|
}
|
|
kind := string(data[pos : pos+4])
|
|
payload := data[pos+riffChunkHeaderSize : pos+riffChunkHeaderSize+int(size)]
|
|
end := pos + riffChunkHeaderSize + int(padded)
|
|
switch kind {
|
|
case webpEXIF:
|
|
if found, ok := exifOrientation(payload); ok {
|
|
orientation = found
|
|
}
|
|
case webpXMP:
|
|
default:
|
|
chunks = append(chunks, bytes.Clone(data[pos:end]))
|
|
}
|
|
pos = end
|
|
}
|
|
|
|
if len(chunks) == 0 {
|
|
return nil, errMalformedContainer
|
|
}
|
|
if string(chunks[0][:4]) == webpVP8X {
|
|
if len(chunks[0]) <= riffChunkHeaderSize {
|
|
return nil, errMalformedContainer
|
|
}
|
|
flags := &chunks[0][riffChunkHeaderSize]
|
|
*flags &^= webpEXIFFlag | webpXMPFlag
|
|
if needsOrientation(orientation) {
|
|
*flags |= webpEXIFFlag
|
|
chunks = append(chunks, riffChunk(webpEXIF, orientationOnlyTIFF(orientation)))
|
|
}
|
|
}
|
|
return riffContainer(chunks), nil
|
|
}
|
|
|
|
func riffChunk(kind string, payload []byte) []byte {
|
|
chunk := append([]byte(kind), binary.LittleEndian.AppendUint32(nil, uint32(len(payload)))...)
|
|
chunk = append(chunk, payload...)
|
|
if len(payload)%2 == 1 {
|
|
chunk = append(chunk, 0)
|
|
}
|
|
return chunk
|
|
}
|
|
|
|
func riffContainer(chunks [][]byte) []byte {
|
|
body := []byte("WEBP")
|
|
for _, chunk := range chunks {
|
|
body = append(body, chunk...)
|
|
}
|
|
out := append([]byte("RIFF"), binary.LittleEndian.AppendUint32(nil, uint32(len(body)))...)
|
|
return append(out, body...)
|
|
}
|