forked from anthonynsimon/bild
-
Notifications
You must be signed in to change notification settings - Fork 0
/
clone.go
150 lines (127 loc) · 3.69 KB
/
clone.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
/*Package clone provides image cloning function.*/
package clone
import (
"image"
"image/draw"
"github.com/anthonynsimon/bild/parallel"
)
// PadMethod is the method used to fill padded pixels.
type PadMethod uint8
const (
// NoFill leaves the padded pixels empty.
NoFill = iota
// EdgeExtend extends the closest edge pixel.
EdgeExtend
// EdgeWrap wraps around the pixels of an image.
EdgeWrap
)
// AsRGBA returns an RGBA copy of the supplied image.
func AsRGBA(src image.Image) *image.RGBA {
bounds := src.Bounds()
img := image.NewRGBA(bounds)
draw.Draw(img, bounds, src, bounds.Min, draw.Src)
return img
}
// Pad returns an RGBA copy of the src image paramter with its edges padded
// using the supplied PadMethod.
// Parameter padX and padY correspond to the amount of padding to be applied
// on each side.
// Parameter m is the PadMethod to fill the new pixels.
//
// Usage example:
//
// result := Pad(img, 5,5, EdgeExtend)
//
func Pad(src image.Image, padX, padY int, m PadMethod) *image.RGBA {
var result *image.RGBA
switch m {
case EdgeExtend:
result = extend(src, padX, padY)
case NoFill:
result = noFill(src, padX, padY)
case EdgeWrap:
result = wrap(src, padX, padY)
default:
result = extend(src, padX, padY)
}
return result
}
func noFill(img image.Image, padX, padY int) *image.RGBA {
srcBounds := img.Bounds()
paddedW, paddedH := srcBounds.Dx()+2*padX, srcBounds.Dy()+2*padY
newBounds := image.Rect(0, 0, paddedW, paddedH)
fillBounds := image.Rect(padX, padY, padX+srcBounds.Dx(), padY+srcBounds.Dy())
dst := image.NewRGBA(newBounds)
draw.Draw(dst, fillBounds, img, srcBounds.Min, draw.Src)
return dst
}
func extend(img image.Image, padX, padY int) *image.RGBA {
dst := noFill(img, padX, padY)
paddedW, paddedH := dst.Bounds().Dx(), dst.Bounds().Dy()
parallel.Line(paddedH, func(start, end int) {
for y := start; y < end; y++ {
iy := y
if iy < padY {
iy = padY
} else if iy >= paddedH-padY {
iy = paddedH - padY - 1
}
for x := 0; x < paddedW; x++ {
ix := x
if ix < padX {
ix = padX
} else if x >= paddedW-padX {
ix = paddedW - padX - 1
} else if iy == y {
// This only enters if we are not in a y-padded area or
// x-padded area, so nothing to extend here.
// So simply jump to the next padded-x index.
x = paddedW - padX - 1
continue
}
dstPos := y*dst.Stride + x*4
edgePos := iy*dst.Stride + ix*4
dst.Pix[dstPos+0] = dst.Pix[edgePos+0]
dst.Pix[dstPos+1] = dst.Pix[edgePos+1]
dst.Pix[dstPos+2] = dst.Pix[edgePos+2]
dst.Pix[dstPos+3] = dst.Pix[edgePos+3]
}
}
})
return dst
}
func wrap(img image.Image, padX, padY int) *image.RGBA {
dst := noFill(img, padX, padY)
paddedW, paddedH := dst.Bounds().Dx(), dst.Bounds().Dy()
parallel.Line(paddedH, func(start, end int) {
for y := start; y < end; y++ {
iy := y
if iy < padY {
iy = (paddedH - padY) - ((padY - y) % (paddedH - padY*2))
} else if iy >= paddedH-padY {
iy = padY - ((padY - y) % (paddedH - padY*2))
}
for x := 0; x < paddedW; x++ {
ix := x
if ix < padX {
ix = (paddedW - padX) - ((padX - x) % (paddedW - padX*2))
} else if ix >= paddedW-padX {
ix = padX - ((padX - x) % (paddedW - padX*2))
} else if iy == y {
// This only enters if we are not in a y-padded area or
// x-padded area, so nothing to extend here.
// So simply jump to the next padded-x index.
x = paddedW - padX - 1
continue
}
dstPos := y*dst.Stride + x*4
edgePos := iy*dst.Stride + ix*4
dst.Pix[dstPos+0] = dst.Pix[edgePos+0]
dst.Pix[dstPos+1] = dst.Pix[edgePos+1]
dst.Pix[dstPos+2] = dst.Pix[edgePos+2]
dst.Pix[dstPos+3] = dst.Pix[edgePos+3]
}
}
})
return dst
}