Last active
October 27, 2016 02:28
-
-
Save yoya/2ae952716dbf70bc749181781eda27a8 to your computer and use it in GitHub Desktop.
GIF one frame only sample. (take 2: with error handling)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 2016/10/27 (c) yoya@awm.jp | |
// ref) https://github.com/yoya/IO_GIF/blob/master/IO/GIF.php | |
package main | |
import ( | |
"errors" | |
"fmt" | |
"io/ioutil" | |
"math" | |
"os" | |
) | |
func GIFOneFrameOnly(bytes []byte) (int, error) { | |
size := len(bytes) | |
if size < 13 { | |
return size, errors.New("too short header") | |
} | |
flags := bytes[10] | |
globalColorTableFlag := (flags & 0x80) >> 7 | |
sizeOfGlobalColorTable := (flags & 0x07) | |
var offset = 13 | |
if globalColorTableFlag != 0 { | |
colorTableSize := int(math.Pow(2, float64(sizeOfGlobalColorTable+1))) | |
offset += 3 * colorTableSize | |
if size < offset { | |
return size, errors.New("too short global colorTable") | |
} | |
} | |
for { | |
// fmt.Printf("# -- offset:%x\n", offset) | |
if size < (offset + 1) { | |
return size, errors.New("missing separator") | |
} | |
separator := bytes[offset] | |
offset++ | |
switch separator { | |
case 0x3B: // Trailer | |
case 0x21: // Extention | |
if size < (offset + 2) { | |
return size, errors.New("missing extention block header") | |
} | |
extensionBlockLabel := bytes[offset] | |
extensionDataSize := bytes[offset+1] | |
offset += 2 + int(extensionDataSize) | |
if size < offset { | |
return size, errors.New("too short extension block") | |
} | |
if extensionBlockLabel == 0xff { // Application Extension | |
for { | |
if size < (offset + 1) { | |
return size, errors.New("missing extension subblock size field") | |
} | |
subBlockSize := bytes[offset] | |
offset++ | |
if subBlockSize == 0 { | |
break | |
} | |
offset += int(subBlockSize) | |
if size < offset { | |
return size, errors.New("to short extension subblock") | |
} | |
} | |
} else { | |
offset++ // extensionBlock Trailer | |
} | |
case 0x2C: // Image | |
if size < (offset + 9) { | |
return size, errors.New("too short image header") | |
} | |
flags := bytes[offset+8] | |
localColorTableFlag := (flags & 0x80) >> 7 | |
sizeOfLocalColorTable := (flags & 0x07) | |
offset += 9 | |
if localColorTableFlag != 0 { | |
colorTableSize := int(math.Pow(2, float64(sizeOfLocalColorTable+1))) | |
offset += 3 * colorTableSize | |
if size < offset { | |
return size, errors.New("too short local colorTable") | |
} | |
} | |
offset++ // LZWMinimumCodeSize | |
for { | |
if size < (offset + 1) { | |
return size, errors.New("missing image subblock size field") | |
} | |
subBlockSize := bytes[offset] | |
offset++ | |
if subBlockSize == 0 { | |
break | |
} | |
offset += int(subBlockSize) | |
if size < offset { | |
return size, errors.New("too short image subblock") | |
} | |
} | |
if size < (offset + 1) { | |
return size, errors.New("missing separator for trailer overwrite") | |
} | |
bytes[offset] = 0x3B // trailer overwrite | |
default: | |
// nothing to do | |
} | |
if separator == 0x3B { | |
break | |
} | |
} | |
return offset, nil | |
} | |
func main() { | |
var err error | |
if len(os.Args) < 3 { | |
fmt.Println("Usage: gifoneframeonly <input GIF> <output GIF>") | |
return | |
} | |
// data read | |
f, err := os.Open(os.Args[1]) | |
if err != nil { | |
panic(err) | |
} | |
defer f.Close() | |
bytes, err := ioutil.ReadAll(f) | |
if err != nil { | |
panic(err) | |
} | |
size, err := GIFOneFrameOnly(bytes) | |
if err != nil { | |
fmt.Println("Parse Error at %d\n", size) | |
fmt.Print(err) | |
return | |
} | |
ioutil.WriteFile(os.Args[2], bytes[:size], 0755) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment