-
Notifications
You must be signed in to change notification settings - Fork 0
/
gifspector.go
130 lines (115 loc) · 4.13 KB
/
gifspector.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
package main
import (
"fmt"
"image/gif"
"image/jpeg"
"os"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
func main() {
gifname := kingpin.Arg("input", "The input GIF file").Required().String()
savefolder := kingpin.Flag("output", "Output folder name").Default("out").Short('o').String()
shouldsplit := kingpin.Flag("split", "Split the input gif into individual frames?").Short('s').Bool()
savejpeg := kingpin.Flag("jpeg", "Save individual frames as JPEGs instead of GIFs").Short('j').Bool()
//shouldtrim := kingpin.Flag("trim", "Trim the gif (saved as 'trimmed.gif') using a start and end frame number (Passed as arguments, see --help)").Short('t').Bool()
starttrim := kingpin.Arg("start", "Start frame to trim the gif from (Inclusive)").Default("-1").Int()
endtrim := kingpin.Arg("end", "The frame to trim the gif up to (Exclusive)").Default("-1").Int()
kingpin.CommandLine.HelpFlag.Short('h')
kingpin.Parse()
f, err := os.Open(*gifname)
if err != nil {
fmt.Fprintln(os.Stderr, err)
fmt.Fprintf(os.Stderr, "Could not open image \"%s\"! Exiting.\n", *gifname)
os.Exit(1)
}
defer f.Close()
// Decode our single gif into a struct containing every frame & some other useful information
gifstruct, err := gif.DecodeAll(f)
if err != nil {
fmt.Fprintln(os.Stderr, err)
fmt.Fprintf(os.Stderr, "Unable to decode \"%s\" as a GIF! Exiting.\n", *gifname)
os.Exit(1)
}
// If the desired output folder doesn't exist we'll make it
if _, err := os.Stat(*savefolder); os.IsNotExist(err) {
os.Mkdir(*savefolder, 0644)
}
printStats(gifstruct, *savefolder, *gifname, *shouldsplit)
if *shouldsplit {
SplitGIF(gifstruct, *savefolder, *savejpeg)
}
if *starttrim != -1 {
start, end := normalizeTrimLength(*starttrim, *endtrim, gifstruct)
ftrim, err := os.Create("trimmed.gif")
if err != nil {
fmt.Fprintln(os.Stderr, err)
fmt.Fprintf(os.Stderr, "Unable to open trimmed.gif for writing! Exiting.\n")
os.Exit(1)
}
defer ftrim.Close()
newgif := TrimGIF(start, end, gifstruct)
err = gif.EncodeAll(ftrim, newgif)
if err != nil {
fmt.Fprintln(os.Stderr, err)
fmt.Fprintf(os.Stderr, "Unable to encode trimmed.gif! Exiting.\n")
os.Exit(1)
}
}
}
// SplitGIF will split a gif (gifstruct) into it's individual frames then save them into a folder (savefolder), optionally as JPEGs (saveasjpeg)
func SplitGIF(gifstruct *gif.GIF, savefolder string, saveasjpeg bool) {
var format string
if saveasjpeg {
format = ".jpeg"
} else {
format = ".gif"
}
for i, img := range gifstruct.Image {
filename := fmt.Sprintf("%s/frame%02d%s", savefolder, i, format)
outfile, err := os.Create(filename)
defer outfile.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "Error! Could not create image: %s", filename)
} else {
if saveasjpeg {
jpeg.Encode(outfile, img, &jpeg.Options{Quality: 100})
} else {
gif.Encode(outfile, img, &gif.Options{NumColors: 256})
}
}
}
}
// TrimGIF will take a starting gif and trim it down to a start and end frame from the original, then return the new gif
func TrimGIF(start int, end int, original *gif.GIF) *gif.GIF {
newgif := original
newgif.Image = original.Image[start:end]
newgif.Delay = original.Delay[start:end]
newgif.Disposal = original.Disposal[start:end]
return newgif
}
// Prints out a range of interesting stats about the input gif
func printStats(gifstruct *gif.GIF, path string, name string, shouldsplit bool) {
fmt.Printf("--- GIF STATS: %s ---\n", name)
fmt.Printf("Number of frames:\n %d\n", len(gifstruct.Image))
fmt.Printf("Delay per frame (100ths / sec):\n %d\n", gifstruct.Delay)
fmt.Printf("Loop count:\n %d\n", gifstruct.LoopCount)
fmt.Printf("Image size (height x width):\n %d x %d\n", gifstruct.Config.Height, gifstruct.Config.Width)
if shouldsplit {
fmt.Printf("\nOutputting individual frames to folder: %s\n", path)
}
}
func normalizeTrimLength(startl, endl int, gifstruct *gif.GIF) (start, end int) {
start = startl
end = endl
if start == -1 {
start = 0
}
if end == -1 {
end = len(gifstruct.Image)
}
if start > end {
fmt.Fprintf(os.Stderr, "Invalid trim length! The end frame must be larger than the start frame. Exiting.\n")
os.Exit(1)
}
return start, end
}