-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathworker.go
157 lines (145 loc) · 3.01 KB
/
worker.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
package main
import (
"errors"
"github.com/sugyan/ttygif/image/xwd"
"image"
"image/color/palette"
"image/png"
"io"
"os"
"sync"
)
// WorkerInput type
type WorkerInput struct {
index int
filePath string
fileType string
}
// WorkerOutput type
type WorkerOutput struct {
index int
paletted *image.Paletted
err error
}
// Worker type
type Worker struct {
inputs []WorkerInput
}
// NewWorker returns Worker instance
func NewWorker() *Worker {
return &Worker{}
}
// AddTargetFile adds input
func (w *Worker) AddTargetFile(filePath string, fileType string) {
index := len(w.inputs)
w.inputs = append(w.inputs, WorkerInput{
index: index,
filePath: filePath,
fileType: fileType,
})
}
// GetAllImages waits and returns all images
func (w *Worker) GetAllImages() ([]*image.Paletted, error) {
done := make(chan struct{})
defer close(done)
inputs, errc := w.getInputChannel(done)
output := make(chan *WorkerOutput)
var (
wg sync.WaitGroup
numWorkers = 10
)
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func() {
worker(inputs, output, done)
wg.Done()
}()
}
go func() {
wg.Wait()
close(output)
}()
results := make([]*image.Paletted, len(w.inputs))
Loop:
for {
select {
case output, ok := <-output:
if !ok {
break Loop
}
if output.err != nil {
return nil, output.err
}
results[output.index] = output.paletted
case err := <-errc:
if err != nil {
return nil, err
}
}
}
return results, nil
}
func (w *Worker) getInputChannel(done <-chan struct{}) (<-chan WorkerInput, <-chan error) {
inputs := make(chan WorkerInput)
errc := make(chan error, 1)
go func() {
defer close(inputs)
errc <- func(walkFunc func(WorkerInput) error) error {
for _, input := range w.inputs {
err := walkFunc(input)
if err != nil {
return err
}
}
return nil
}(func(input WorkerInput) error {
select {
case inputs <- input:
case <-done:
return errors.New("Canceled")
}
return nil
})
}()
return inputs, errc
}
func worker(inputs <-chan WorkerInput, output chan<- *WorkerOutput, done <-chan struct{}) {
for input := range inputs {
paletted, err := decode(input.filePath, input.fileType)
select {
case output <- &WorkerOutput{index: input.index, paletted: paletted, err: err}:
case <-done:
return
}
}
}
func decode(filePath string, fileType string) (paletted *image.Paletted, err error) {
var decoder func(io.Reader) (image.Image, error)
switch fileType {
case "png":
decoder = png.Decode
case "xwd":
decoder = xwd.Decode
default:
return nil, errors.New("Unsupported file type")
}
// open file
var file *os.File
file, err = os.Open(filePath)
if err != nil {
return
}
defer file.Close()
// decode
img, err := decoder(file)
if err != nil {
return
}
paletted = image.NewPaletted(img.Bounds(), palette.WebSafe)
for x := paletted.Rect.Min.X; x < paletted.Rect.Max.X; x++ {
for y := paletted.Rect.Min.Y; y < paletted.Rect.Max.Y; y++ {
paletted.Set(x, y, img.At(x, y))
}
}
return paletted, nil
}