mirror of
https://github.com/disintegration/imaging.git
synced 2025-12-24 12:58:00 +08:00
performance improvements
This commit is contained in:
45
adjust.go
45
adjust.go
@@ -15,14 +15,15 @@ func Grayscale(img image.Image) *image.NRGBA {
|
||||
i := y * dst.Stride
|
||||
src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
|
||||
for x := 0; x < src.w; x++ {
|
||||
r := dst.Pix[i+0]
|
||||
g := dst.Pix[i+1]
|
||||
b := dst.Pix[i+2]
|
||||
d := dst.Pix[i : i+3 : i+3]
|
||||
r := d[0]
|
||||
g := d[1]
|
||||
b := d[2]
|
||||
f := 0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b)
|
||||
y := uint8(f + 0.5)
|
||||
dst.Pix[i+0] = y
|
||||
dst.Pix[i+1] = y
|
||||
dst.Pix[i+2] = y
|
||||
d[0] = y
|
||||
d[1] = y
|
||||
d[2] = y
|
||||
i += 4
|
||||
}
|
||||
}
|
||||
@@ -39,9 +40,10 @@ func Invert(img image.Image) *image.NRGBA {
|
||||
i := y * dst.Stride
|
||||
src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
|
||||
for x := 0; x < src.w; x++ {
|
||||
dst.Pix[i+0] = 255 - dst.Pix[i+0]
|
||||
dst.Pix[i+1] = 255 - dst.Pix[i+1]
|
||||
dst.Pix[i+2] = 255 - dst.Pix[i+2]
|
||||
d := dst.Pix[i : i+3 : i+3]
|
||||
d[0] = 255 - d[0]
|
||||
d[1] = 255 - d[1]
|
||||
d[2] = 255 - d[2]
|
||||
i += 4
|
||||
}
|
||||
}
|
||||
@@ -191,14 +193,16 @@ func sigmoid(a, b, x float64) float64 {
|
||||
func adjustLUT(img image.Image, lut []uint8) *image.NRGBA {
|
||||
src := newScanner(img)
|
||||
dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
|
||||
lut = lut[0:256]
|
||||
parallel(0, src.h, func(ys <-chan int) {
|
||||
for y := range ys {
|
||||
i := y * dst.Stride
|
||||
src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
|
||||
for x := 0; x < src.w; x++ {
|
||||
dst.Pix[i+0] = lut[dst.Pix[i+0]]
|
||||
dst.Pix[i+1] = lut[dst.Pix[i+1]]
|
||||
dst.Pix[i+2] = lut[dst.Pix[i+2]]
|
||||
d := dst.Pix[i : i+3 : i+3]
|
||||
d[0] = lut[d[0]]
|
||||
d[1] = lut[d[1]]
|
||||
d[2] = lut[d[2]]
|
||||
i += 4
|
||||
}
|
||||
}
|
||||
@@ -230,15 +234,16 @@ func AdjustFunc(img image.Image, fn func(c color.NRGBA) color.NRGBA) *image.NRGB
|
||||
i := y * dst.Stride
|
||||
src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
|
||||
for x := 0; x < src.w; x++ {
|
||||
r := dst.Pix[i+0]
|
||||
g := dst.Pix[i+1]
|
||||
b := dst.Pix[i+2]
|
||||
a := dst.Pix[i+3]
|
||||
d := dst.Pix[i : i+4 : i+4]
|
||||
r := d[0]
|
||||
g := d[1]
|
||||
b := d[2]
|
||||
a := d[3]
|
||||
c := fn(color.NRGBA{r, g, b, a})
|
||||
dst.Pix[i+0] = c.R
|
||||
dst.Pix[i+1] = c.G
|
||||
dst.Pix[i+2] = c.B
|
||||
dst.Pix[i+3] = c.A
|
||||
d[0] = c.R
|
||||
d[1] = c.G
|
||||
d[2] = c.B
|
||||
d[3] = c.A
|
||||
i += 4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -820,3 +820,12 @@ func TestAdjustFunc(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAdjustFunc(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
AdjustFunc(testdataBranchesJPG, func(c color.NRGBA) color.NRGBA {
|
||||
return color.NRGBA{c.B, c.G, c.R, c.A}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,9 +90,10 @@ func convolve(img image.Image, kernel []float64, options *ConvolveOptions) *imag
|
||||
}
|
||||
|
||||
off := iy*src.Stride + ix*4
|
||||
r += float64(src.Pix[off+0]) * c.k
|
||||
g += float64(src.Pix[off+1]) * c.k
|
||||
b += float64(src.Pix[off+2]) * c.k
|
||||
s := src.Pix[off : off+3 : off+3]
|
||||
r += float64(s[0]) * c.k
|
||||
g += float64(s[1]) * c.k
|
||||
b += float64(s[2]) * c.k
|
||||
}
|
||||
|
||||
if options.Abs {
|
||||
@@ -115,10 +116,11 @@ func convolve(img image.Image, kernel []float64, options *ConvolveOptions) *imag
|
||||
|
||||
srcOff := y*src.Stride + x*4
|
||||
dstOff := y*dst.Stride + x*4
|
||||
dst.Pix[dstOff+0] = clamp(r)
|
||||
dst.Pix[dstOff+1] = clamp(g)
|
||||
dst.Pix[dstOff+2] = clamp(b)
|
||||
dst.Pix[dstOff+3] = src.Pix[srcOff+3]
|
||||
d := dst.Pix[dstOff : dstOff+4 : dstOff+4]
|
||||
d[0] = clamp(r)
|
||||
d[1] = clamp(g)
|
||||
d[2] = clamp(b)
|
||||
d[3] = src.Pix[srcOff+3]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
54
effects.go
54
effects.go
@@ -44,7 +44,7 @@ func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA {
|
||||
for i, v := range scanLine {
|
||||
scanLineF[i] = float64(v)
|
||||
}
|
||||
for x, idx := 0, 0; x < src.w; x, idx = x+1, idx+4 {
|
||||
for x := 0; x < src.w; x++ {
|
||||
min := x - radius
|
||||
if min < 0 {
|
||||
min = 0
|
||||
@@ -53,30 +53,28 @@ func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA {
|
||||
if max > src.w-1 {
|
||||
max = src.w - 1
|
||||
}
|
||||
|
||||
var r, g, b, a, wsum float64
|
||||
for ix := min; ix <= max; ix++ {
|
||||
i := ix * 4
|
||||
weight := kernel[absint(x-ix)]
|
||||
wsum += weight
|
||||
wa := scanLineF[i+3] * weight
|
||||
r += scanLineF[i+0] * wa
|
||||
g += scanLineF[i+1] * wa
|
||||
b += scanLineF[i+2] * wa
|
||||
s := scanLineF[i : i+4 : i+4]
|
||||
wa := s[3] * weight
|
||||
r += s[0] * wa
|
||||
g += s[1] * wa
|
||||
b += s[2] * wa
|
||||
a += wa
|
||||
}
|
||||
if a != 0 {
|
||||
r /= a
|
||||
g /= a
|
||||
b /= a
|
||||
aInv := 1 / a
|
||||
j := y*dst.Stride + x*4
|
||||
d := dst.Pix[j : j+4 : j+4]
|
||||
d[0] = clamp(r * aInv)
|
||||
d[1] = clamp(g * aInv)
|
||||
d[2] = clamp(b * aInv)
|
||||
d[3] = clamp(a / wsum)
|
||||
}
|
||||
|
||||
scanLine[idx+0] = clamp(r)
|
||||
scanLine[idx+1] = clamp(g)
|
||||
scanLine[idx+2] = clamp(b)
|
||||
scanLine[idx+3] = clamp(a / wsum)
|
||||
}
|
||||
copy(dst.Pix[y*dst.Stride:], scanLine)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -105,29 +103,27 @@ func blurVertical(img image.Image, kernel []float64) *image.NRGBA {
|
||||
if max > src.h-1 {
|
||||
max = src.h - 1
|
||||
}
|
||||
|
||||
var r, g, b, a, wsum float64
|
||||
for iy := min; iy <= max; iy++ {
|
||||
i := iy * 4
|
||||
weight := kernel[absint(y-iy)]
|
||||
wsum += weight
|
||||
wa := scanLineF[i+3] * weight
|
||||
r += scanLineF[i+0] * wa
|
||||
g += scanLineF[i+1] * wa
|
||||
b += scanLineF[i+2] * wa
|
||||
s := scanLineF[i : i+4 : i+4]
|
||||
wa := s[3] * weight
|
||||
r += s[0] * wa
|
||||
g += s[1] * wa
|
||||
b += s[2] * wa
|
||||
a += wa
|
||||
}
|
||||
if a != 0 {
|
||||
r /= a
|
||||
g /= a
|
||||
b /= a
|
||||
aInv := 1 / a
|
||||
j := y*dst.Stride + x*4
|
||||
d := dst.Pix[j : j+4 : j+4]
|
||||
d[0] = clamp(r * aInv)
|
||||
d[1] = clamp(g * aInv)
|
||||
d[2] = clamp(b * aInv)
|
||||
d[3] = clamp(a / wsum)
|
||||
}
|
||||
|
||||
j := y*dst.Stride + x*4
|
||||
dst.Pix[j+0] = clamp(r)
|
||||
dst.Pix[j+1] = clamp(g)
|
||||
dst.Pix[j+2] = clamp(b)
|
||||
dst.Pix[j+3] = clamp(a / wsum)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -27,9 +27,10 @@ func Histogram(img image.Image) [256]float64 {
|
||||
src.scan(0, y, src.w, y+1, scanLine)
|
||||
i := 0
|
||||
for x := 0; x < src.w; x++ {
|
||||
r := scanLine[i+0]
|
||||
g := scanLine[i+1]
|
||||
b := scanLine[i+2]
|
||||
s := scanLine[i : i+3 : i+3]
|
||||
r := s[0]
|
||||
g := s[1]
|
||||
b := s[2]
|
||||
y := 0.299*float32(r) + 0.587*float32(g) + 0.114*float32(b)
|
||||
tmpHistogram[int(y+0.5)]++
|
||||
tmpTotal++
|
||||
|
||||
26
tools.go
26
tools.go
@@ -198,15 +198,17 @@ func Overlay(background, img image.Image, pos image.Point, opacity float64) *ima
|
||||
i := y*dst.Stride + interRect.Min.X*4
|
||||
j := 0
|
||||
for x := interRect.Min.X; x < interRect.Max.X; x++ {
|
||||
r1 := float64(dst.Pix[i+0])
|
||||
g1 := float64(dst.Pix[i+1])
|
||||
b1 := float64(dst.Pix[i+2])
|
||||
a1 := float64(dst.Pix[i+3])
|
||||
d := dst.Pix[i : i+4 : i+4]
|
||||
r1 := float64(d[0])
|
||||
g1 := float64(d[1])
|
||||
b1 := float64(d[2])
|
||||
a1 := float64(d[3])
|
||||
|
||||
r2 := float64(scanLine[j+0])
|
||||
g2 := float64(scanLine[j+1])
|
||||
b2 := float64(scanLine[j+2])
|
||||
a2 := float64(scanLine[j+3])
|
||||
s := scanLine[j : j+4 : j+4]
|
||||
r2 := float64(s[0])
|
||||
g2 := float64(s[1])
|
||||
b2 := float64(s[2])
|
||||
a2 := float64(s[3])
|
||||
|
||||
coef2 := opacity * a2 / 255
|
||||
coef1 := (1 - coef2) * a1 / 255
|
||||
@@ -214,10 +216,10 @@ func Overlay(background, img image.Image, pos image.Point, opacity float64) *ima
|
||||
coef1 /= coefSum
|
||||
coef2 /= coefSum
|
||||
|
||||
dst.Pix[i+0] = uint8(r1*coef1 + r2*coef2)
|
||||
dst.Pix[i+1] = uint8(g1*coef1 + g2*coef2)
|
||||
dst.Pix[i+2] = uint8(b1*coef1 + b2*coef2)
|
||||
dst.Pix[i+3] = uint8(math.Min(a1+a2*opacity*(255-a1)/255, 255))
|
||||
d[0] = uint8(r1*coef1 + r2*coef2)
|
||||
d[1] = uint8(g1*coef1 + g2*coef2)
|
||||
d[2] = uint8(b1*coef1 + b2*coef2)
|
||||
d[3] = uint8(math.Min(a1+a2*opacity*(255-a1)/255, 255))
|
||||
|
||||
i += 4
|
||||
j += 4
|
||||
|
||||
83
transform.go
83
transform.go
@@ -209,63 +209,60 @@ func rotatedSize(w, h int, angle float64) (int, int) {
|
||||
}
|
||||
|
||||
func interpolatePoint(dst *image.NRGBA, dstX, dstY int, src *image.NRGBA, xf, yf float64, bgColor color.NRGBA) {
|
||||
dstIndex := dstY*dst.Stride + dstX*4
|
||||
j := dstY*dst.Stride + dstX*4
|
||||
d := dst.Pix[j : j+4 : j+4]
|
||||
|
||||
x0 := int(math.Floor(xf))
|
||||
y0 := int(math.Floor(yf))
|
||||
bounds := src.Bounds()
|
||||
if !image.Pt(x0, y0).In(image.Rect(bounds.Min.X-1, bounds.Min.Y-1, bounds.Max.X, bounds.Max.Y)) {
|
||||
dst.Pix[dstIndex+0] = bgColor.R
|
||||
dst.Pix[dstIndex+1] = bgColor.G
|
||||
dst.Pix[dstIndex+2] = bgColor.B
|
||||
dst.Pix[dstIndex+3] = bgColor.A
|
||||
d[0] = bgColor.R
|
||||
d[1] = bgColor.G
|
||||
d[2] = bgColor.B
|
||||
d[3] = bgColor.A
|
||||
return
|
||||
}
|
||||
|
||||
xq := xf - float64(x0)
|
||||
yq := yf - float64(y0)
|
||||
|
||||
var pxs [4]color.NRGBA
|
||||
var cfs [4]float64
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
for j := 0; j < 2; j++ {
|
||||
k := i*2 + j
|
||||
pt := image.Pt(x0+j, y0+i)
|
||||
if pt.In(bounds) {
|
||||
l := pt.Y*src.Stride + pt.X*4
|
||||
pxs[k].R = src.Pix[l+0]
|
||||
pxs[k].G = src.Pix[l+1]
|
||||
pxs[k].B = src.Pix[l+2]
|
||||
pxs[k].A = src.Pix[l+3]
|
||||
} else {
|
||||
pxs[k] = bgColor
|
||||
}
|
||||
}
|
||||
points := [4]image.Point{
|
||||
{x0, y0},
|
||||
{x0 + 1, y0},
|
||||
{x0, y0 + 1},
|
||||
{x0 + 1, y0 + 1},
|
||||
}
|
||||
weights := [4]float64{
|
||||
(1 - xq) * (1 - yq),
|
||||
xq * (1 - yq),
|
||||
(1 - xq) * yq,
|
||||
xq * yq,
|
||||
}
|
||||
|
||||
cfs[0] = (1 - xq) * (1 - yq)
|
||||
cfs[1] = xq * (1 - yq)
|
||||
cfs[2] = (1 - xq) * yq
|
||||
cfs[3] = xq * yq
|
||||
|
||||
var r, g, b, a float64
|
||||
for i := range pxs {
|
||||
wa := float64(pxs[i].A) * cfs[i]
|
||||
r += float64(pxs[i].R) * wa
|
||||
g += float64(pxs[i].G) * wa
|
||||
b += float64(pxs[i].B) * wa
|
||||
a += wa
|
||||
for i := 0; i < 4; i++ {
|
||||
p := points[i]
|
||||
w := weights[i]
|
||||
if p.In(bounds) {
|
||||
i := p.Y*src.Stride + p.X*4
|
||||
s := src.Pix[i : i+4 : i+4]
|
||||
wa := float64(s[3]) * w
|
||||
r += float64(s[0]) * wa
|
||||
g += float64(s[1]) * wa
|
||||
b += float64(s[2]) * wa
|
||||
a += wa
|
||||
} else {
|
||||
wa := float64(bgColor.A) * w
|
||||
r += float64(bgColor.R) * wa
|
||||
g += float64(bgColor.G) * wa
|
||||
b += float64(bgColor.B) * wa
|
||||
a += wa
|
||||
}
|
||||
}
|
||||
|
||||
if a != 0 {
|
||||
r /= a
|
||||
g /= a
|
||||
b /= a
|
||||
aInv := 1 / a
|
||||
d[0] = clamp(r * aInv)
|
||||
d[1] = clamp(g * aInv)
|
||||
d[2] = clamp(b * aInv)
|
||||
d[3] = clamp(a)
|
||||
}
|
||||
|
||||
dst.Pix[dstIndex+0] = clamp(r)
|
||||
dst.Pix[dstIndex+1] = clamp(g)
|
||||
dst.Pix[dstIndex+2] = clamp(b)
|
||||
dst.Pix[dstIndex+3] = clamp(a)
|
||||
}
|
||||
|
||||
10
utils.go
10
utils.go
@@ -63,10 +63,12 @@ func reverse(pix []uint8) {
|
||||
i := 0
|
||||
j := len(pix) - 4
|
||||
for i < j {
|
||||
pix[i+0], pix[j+0] = pix[j+0], pix[i+0]
|
||||
pix[i+1], pix[j+1] = pix[j+1], pix[i+1]
|
||||
pix[i+2], pix[j+2] = pix[j+2], pix[i+2]
|
||||
pix[i+3], pix[j+3] = pix[j+3], pix[i+3]
|
||||
pi := pix[i : i+4 : i+4]
|
||||
pj := pix[j : j+4 : j+4]
|
||||
pi[0], pj[0] = pj[0], pi[0]
|
||||
pi[1], pj[1] = pj[1], pi[1]
|
||||
pi[2], pj[2] = pj[2], pi[2]
|
||||
pi[3], pj[3] = pj[3], pi[3]
|
||||
i += 4
|
||||
j -= 4
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user