utils.go

  1package gift
  2
  3import (
  4	"image"
  5	"image/draw"
  6	"math"
  7	"runtime"
  8	"sync"
  9	"sync/atomic"
 10)
 11
 12// parallelize parallelizes the data processing if enabled is true.
 13func parallelize(enabled bool, datamin, datamax int, fn func(pmin, pmax int)) {
 14	datasize := datamax - datamin
 15	partsize := datasize
 16
 17	numGoroutines := 1
 18	if enabled {
 19		numProcs := runtime.GOMAXPROCS(0)
 20		if numProcs > 1 {
 21			numGoroutines = numProcs
 22			partsize = partsize / numGoroutines
 23			if partsize < 1 {
 24				partsize = 1
 25			}
 26		}
 27	}
 28
 29	if numGoroutines == 1 {
 30		fn(datamin, datamax)
 31	} else {
 32		var wg sync.WaitGroup
 33		wg.Add(numGoroutines)
 34		idx := int64(datamin)
 35
 36		for p := 0; p < numGoroutines; p++ {
 37			go func() {
 38				defer wg.Done()
 39				for {
 40					pmin := int(atomic.AddInt64(&idx, int64(partsize))) - partsize
 41					if pmin >= datamax {
 42						break
 43					}
 44					pmax := pmin + partsize
 45					if pmax > datamax {
 46						pmax = datamax
 47					}
 48					fn(pmin, pmax)
 49				}
 50			}()
 51		}
 52
 53		wg.Wait()
 54	}
 55}
 56
 57func absf32(x float32) float32 {
 58	if x < 0 {
 59		return -x
 60	}
 61	return x
 62}
 63
 64func minf32(x, y float32) float32 {
 65	if x < y {
 66		return x
 67	}
 68	return y
 69}
 70
 71func maxf32(x, y float32) float32 {
 72	if x > y {
 73		return x
 74	}
 75	return y
 76}
 77
 78func powf32(x, y float32) float32 {
 79	return float32(math.Pow(float64(x), float64(y)))
 80}
 81
 82func logf32(x float32) float32 {
 83	return float32(math.Log(float64(x)))
 84}
 85
 86func expf32(x float32) float32 {
 87	return float32(math.Exp(float64(x)))
 88}
 89
 90func sincosf32(a float32) (float32, float32) {
 91	sin, cos := math.Sincos(math.Pi * float64(a) / 180)
 92	return float32(sin), float32(cos)
 93}
 94
 95func floorf32(x float32) float32 {
 96	return float32(math.Floor(float64(x)))
 97}
 98
 99func sqrtf32(x float32) float32 {
100	return float32(math.Sqrt(float64(x)))
101}
102
103func minint(x, y int) int {
104	if x < y {
105		return x
106	}
107	return y
108}
109
110func maxint(x, y int) int {
111	if x > y {
112		return x
113	}
114	return y
115}
116
117func sort(data []float32) {
118	n := len(data)
119
120	if n < 2 {
121		return
122	}
123
124	if n <= 20 {
125		for i := 1; i < n; i++ {
126			x := data[i]
127			j := i - 1
128			for ; j >= 0 && data[j] > x; j-- {
129				data[j+1] = data[j]
130			}
131			data[j+1] = x
132		}
133		return
134	}
135
136	i := 0
137	j := n - 1
138	x := data[n/2]
139	for i <= j {
140		for data[i] < x {
141			i++
142		}
143		for data[j] > x {
144			j--
145		}
146		if i <= j {
147			data[i], data[j] = data[j], data[i]
148			i++
149			j--
150		}
151	}
152	if j > 0 {
153		sort(data[:j+1])
154	}
155	if i < n-1 {
156		sort(data[i:])
157	}
158}
159
160// createTempImage creates a temporary image.
161func createTempImage(r image.Rectangle) draw.Image {
162	return image.NewNRGBA64(r)
163}
164
165// isOpaque checks if the given image is opaque.
166func isOpaque(img image.Image) bool {
167	switch img := img.(type) {
168	case *image.NRGBA:
169		return img.Opaque()
170	case *image.NRGBA64:
171		return img.Opaque()
172	case *image.RGBA:
173		return img.Opaque()
174	case *image.RGBA64:
175		return img.Opaque()
176	case *image.Gray:
177		return true
178	case *image.Gray16:
179		return true
180	case *image.YCbCr:
181		return true
182	case *image.Paletted:
183		return img.Opaque()
184	}
185	return false
186}
187
188// genDisk generates a disk-shaped kernel.
189func genDisk(ksize int) []float32 {
190	if ksize%2 == 0 {
191		ksize--
192	}
193	if ksize < 1 {
194		return []float32{}
195	}
196	disk := make([]float32, ksize*ksize)
197	kcenter := ksize / 2
198	for i := 0; i < ksize; i++ {
199		for j := 0; j < ksize; j++ {
200			x := kcenter - i
201			y := kcenter - j
202			r := math.Sqrt(float64(x*x + y*y))
203			if r <= float64(ksize/2) {
204				disk[j*ksize+i] = 1
205			}
206		}
207	}
208	return disk
209}
210
211// copyimage copies an image from src to dst.
212func copyimage(dst draw.Image, src image.Image, options *Options) {
213	if options == nil {
214		options = &defaultOptions
215	}
216
217	srcb := src.Bounds()
218	dstb := dst.Bounds()
219	pixGetter := newPixelGetter(src)
220	pixSetter := newPixelSetter(dst)
221
222	parallelize(options.Parallelization, srcb.Min.Y, srcb.Max.Y, func(pmin, pmax int) {
223		for srcy := pmin; srcy < pmax; srcy++ {
224			for srcx := srcb.Min.X; srcx < srcb.Max.X; srcx++ {
225				dstx := dstb.Min.X + srcx - srcb.Min.X
226				dsty := dstb.Min.Y + srcy - srcb.Min.Y
227				pixSetter.setPixel(dstx, dsty, pixGetter.getPixel(srcx, srcy))
228			}
229		}
230	})
231}
232
233type copyimageFilter struct{}
234
235func (p *copyimageFilter) Bounds(srcBounds image.Rectangle) (dstBounds image.Rectangle) {
236	dstBounds = image.Rect(0, 0, srcBounds.Dx(), srcBounds.Dy())
237	return
238}
239
240func (p *copyimageFilter) Draw(dst draw.Image, src image.Image, options *Options) {
241	copyimage(dst, src, options)
242}