Skip to content

Commit 38baa8e

Browse files
committed
Use pointer type as destination image
1 parent 2419d72 commit 38baa8e

File tree

2 files changed

+113
-122
lines changed

2 files changed

+113
-122
lines changed

cmd/main.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ func main() {
6060

6161
for i := 0; i < *radius; i++ {
6262
go func(idx int) {
63-
img, err := stackblur.Process(src, uint32(idx+1))
63+
img := image.NewNRGBA(src.Bounds())
64+
err := stackblur.Run(img, src, uint32(idx+1))
6465
if err != nil {
6566
log.Fatal(err)
6667
}
@@ -81,7 +82,8 @@ func main() {
8182
log.Fatal(err)
8283
}
8384
} else {
84-
img, err := stackblur.Process(src, uint32(*radius))
85+
img := image.NewNRGBA(src.Bounds())
86+
err := stackblur.Run(img, src, uint32(*radius))
8587
if err != nil {
8688
log.Fatal(err)
8789
}

stackblur.go

Lines changed: 109 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,77 @@ var shgTable = []uint32{
5353
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
5454
}
5555

56-
// Process takes the source image and returns it's blurred version by applying the blur radius defined as parameter.
57-
func Process(src image.Image, radius uint32) (*image.NRGBA, error) {
56+
// Run takes the source image and returns it's blurred version by applying the blur radius defined as parameter.
57+
func Run(dst, src image.Image, radius uint32) error {
58+
// Limit the maximum blur radius to 255 to avoid overflowing the multable.
59+
if int(radius) >= len(mulTable) {
60+
radius = uint32(len(mulTable) - 1)
61+
}
62+
63+
if radius < 1 {
64+
return errors.New("blur radius must be greater than 0")
65+
}
66+
67+
if img, ok := dst.(*image.NRGBA); ok {
68+
process(img, src, radius)
69+
}
70+
71+
return nil
72+
}
73+
74+
func process(dst *image.NRGBA, src image.Image, radius uint32) {
75+
srcBounds := src.Bounds()
76+
srcMinX := srcBounds.Min.X
77+
srcMinY := srcBounds.Min.Y
78+
79+
dstBounds := srcBounds.Sub(srcBounds.Min)
80+
dstW := dstBounds.Dx()
81+
dstH := dstBounds.Dy()
82+
83+
switch src0 := src.(type) {
84+
case *image.NRGBA:
85+
rowSize := srcBounds.Dx() * 4
86+
for dstY := 0; dstY < dstH; dstY++ {
87+
di := src0.PixOffset(0, dstY)
88+
si := src0.PixOffset(srcMinX, srcMinY+dstY)
89+
for dstX := 0; dstX < dstW; dstX++ {
90+
copy(dst.Pix[di:di+rowSize], src0.Pix[si:si+rowSize])
91+
}
92+
}
93+
case *image.YCbCr:
94+
for dstY := 0; dstY < dstH; dstY++ {
95+
di := dst.PixOffset(0, dstY)
96+
for dstX := 0; dstX < dstW; dstX++ {
97+
srcX := srcMinX + dstX
98+
srcY := srcMinY + dstY
99+
siy := src0.YOffset(srcX, srcY)
100+
sic := src0.COffset(srcX, srcY)
101+
r, g, b := color.YCbCrToRGB(src0.Y[siy], src0.Cb[sic], src0.Cr[sic])
102+
dst.Pix[di+0] = r
103+
dst.Pix[di+1] = g
104+
dst.Pix[di+2] = b
105+
dst.Pix[di+3] = 0xff
106+
di += 4
107+
}
108+
}
109+
default:
110+
for dstY := 0; dstY < dstH; dstY++ {
111+
di := dst.PixOffset(0, dstY)
112+
for dstX := 0; dstX < dstW; dstX++ {
113+
c := color.NRGBAModel.Convert(src.At(srcMinX+dstX, srcMinY+dstY)).(color.NRGBA)
114+
dst.Pix[di+0] = c.R
115+
dst.Pix[di+1] = c.G
116+
dst.Pix[di+2] = c.B
117+
dst.Pix[di+3] = c.A
118+
di += 4
119+
}
120+
}
121+
}
122+
123+
blurImage(dst, radius)
124+
}
125+
126+
func blurImage(src *image.NRGBA, radius uint32) {
58127
var (
59128
stackEnd *blurStack
60129
stackIn *blurStack
@@ -70,17 +139,6 @@ func Process(src image.Image, radius uint32) (*image.NRGBA, error) {
70139
rInSum, gInSum, bInSum, aInSum,
71140
pr, pg, pb, pa uint32
72141
)
73-
// Limit the maximum blur radius to 255, otherwise it overflows the multable length
74-
// and will panic with and index out of range error.
75-
if int(radius) >= len(mulTable) {
76-
radius = uint32(len(mulTable) - 1)
77-
}
78-
79-
if radius < 1 {
80-
return nil, errors.New("blur radius must be greater than 0")
81-
}
82-
83-
img := toNRGBA(src)
84142

85143
div = radius + radius + 1
86144
widthMinus1 = width - 1
@@ -106,10 +164,10 @@ func Process(src image.Image, radius uint32) (*image.NRGBA, error) {
106164
for y = 0; y < height; y++ {
107165
rInSum, gInSum, bInSum, aInSum, rSum, gSum, bSum, aSum = 0, 0, 0, 0, 0, 0, 0, 0
108166

109-
pr = uint32(img.Pix[yi])
110-
pg = uint32(img.Pix[yi+1])
111-
pb = uint32(img.Pix[yi+2])
112-
pa = uint32(img.Pix[yi+3])
167+
pr = uint32(src.Pix[yi])
168+
pg = uint32(src.Pix[yi+1])
169+
pb = uint32(src.Pix[yi+2])
170+
pa = uint32(src.Pix[yi+3])
113171

114172
rOutSum = radiusPlus1 * pr
115173
gOutSum = radiusPlus1 * pg
@@ -139,10 +197,10 @@ func Process(src image.Image, radius uint32) (*image.NRGBA, error) {
139197
diff = i
140198
}
141199
p = yi + (diff << 2)
142-
pr = uint32(img.Pix[p])
143-
pg = uint32(img.Pix[p+1])
144-
pb = uint32(img.Pix[p+2])
145-
pa = uint32(img.Pix[p+3])
200+
pr = uint32(src.Pix[p])
201+
pg = uint32(src.Pix[p+1])
202+
pb = uint32(src.Pix[p+2])
203+
pa = uint32(src.Pix[p+3])
146204

147205
stack.r = pr
148206
stack.g = pg
@@ -166,16 +224,16 @@ func Process(src image.Image, radius uint32) (*image.NRGBA, error) {
166224

167225
for x = 0; x < width; x++ {
168226
pa = (aSum * mulSum) >> shgSum
169-
img.Pix[yi+3] = uint8(pa)
227+
src.Pix[yi+3] = uint8(pa)
170228

171229
if pa != 0 {
172-
img.Pix[yi] = uint8((rSum * mulSum) >> shgSum)
173-
img.Pix[yi+1] = uint8((gSum * mulSum) >> shgSum)
174-
img.Pix[yi+2] = uint8((bSum * mulSum) >> shgSum)
230+
src.Pix[yi] = uint8((rSum * mulSum) >> shgSum)
231+
src.Pix[yi+1] = uint8((gSum * mulSum) >> shgSum)
232+
src.Pix[yi+2] = uint8((bSum * mulSum) >> shgSum)
175233
} else {
176-
img.Pix[yi] = 0
177-
img.Pix[yi+1] = 0
178-
img.Pix[yi+2] = 0
234+
src.Pix[yi] = 0
235+
src.Pix[yi+1] = 0
236+
src.Pix[yi+2] = 0
179237
}
180238

181239
rSum -= rOutSum
@@ -195,10 +253,10 @@ func Process(src image.Image, radius uint32) (*image.NRGBA, error) {
195253
}
196254
p = (yw + p) << 2
197255

198-
stackIn.r = uint32(img.Pix[p])
199-
stackIn.g = uint32(img.Pix[p+1])
200-
stackIn.b = uint32(img.Pix[p+2])
201-
stackIn.a = uint32(img.Pix[p+3])
256+
stackIn.r = uint32(src.Pix[p])
257+
stackIn.g = uint32(src.Pix[p+1])
258+
stackIn.b = uint32(src.Pix[p+2])
259+
stackIn.a = uint32(src.Pix[p+3])
202260

203261
rInSum += stackIn.r
204262
gInSum += stackIn.g
@@ -238,10 +296,10 @@ func Process(src image.Image, radius uint32) (*image.NRGBA, error) {
238296
rInSum, gInSum, bInSum, aInSum, rSum, gSum, bSum, aSum = 0, 0, 0, 0, 0, 0, 0, 0
239297

240298
yi = x << 2
241-
pr = uint32(img.Pix[yi])
242-
pg = uint32(img.Pix[yi+1])
243-
pb = uint32(img.Pix[yi+2])
244-
pa = uint32(img.Pix[yi+3])
299+
pr = uint32(src.Pix[yi])
300+
pg = uint32(src.Pix[yi+1])
301+
pb = uint32(src.Pix[yi+2])
302+
pa = uint32(src.Pix[yi+3])
245303

246304
rOutSum = radiusPlus1 * pr
247305
gOutSum = radiusPlus1 * pg
@@ -267,10 +325,10 @@ func Process(src image.Image, radius uint32) (*image.NRGBA, error) {
267325

268326
for i = 1; i <= radius; i++ {
269327
yi = (yp + x) << 2
270-
pr = uint32(img.Pix[yi])
271-
pg = uint32(img.Pix[yi+1])
272-
pb = uint32(img.Pix[yi+2])
273-
pa = uint32(img.Pix[yi+3])
328+
pr = uint32(src.Pix[yi])
329+
pg = uint32(src.Pix[yi+1])
330+
pb = uint32(src.Pix[yi+2])
331+
pa = uint32(src.Pix[yi+3])
274332

275333
stack.r = pr
276334
stack.g = pg
@@ -301,16 +359,16 @@ func Process(src image.Image, radius uint32) (*image.NRGBA, error) {
301359
for y = 0; y < height; y++ {
302360
p = yi << 2
303361
pa = (aSum * mulSum) >> shgSum
304-
img.Pix[p+3] = uint8(pa)
362+
src.Pix[p+3] = uint8(pa)
305363

306364
if pa > 0 {
307-
img.Pix[p] = uint8((rSum * mulSum) >> shgSum)
308-
img.Pix[p+1] = uint8((gSum * mulSum) >> shgSum)
309-
img.Pix[p+2] = uint8((bSum * mulSum) >> shgSum)
365+
src.Pix[p] = uint8((rSum * mulSum) >> shgSum)
366+
src.Pix[p+1] = uint8((gSum * mulSum) >> shgSum)
367+
src.Pix[p+2] = uint8((bSum * mulSum) >> shgSum)
310368
} else {
311-
img.Pix[p] = 0
312-
img.Pix[p+1] = 0
313-
img.Pix[p+2] = 0
369+
src.Pix[p] = 0
370+
src.Pix[p+1] = 0
371+
src.Pix[p+2] = 0
314372
}
315373

316374
rSum -= rOutSum
@@ -330,10 +388,10 @@ func Process(src image.Image, radius uint32) (*image.NRGBA, error) {
330388
}
331389
p = (x + (p * width)) << 2
332390

333-
stackIn.r = uint32(img.Pix[p])
334-
stackIn.g = uint32(img.Pix[p+1])
335-
stackIn.b = uint32(img.Pix[p+2])
336-
stackIn.a = uint32(img.Pix[p+3])
391+
stackIn.r = uint32(src.Pix[p])
392+
stackIn.g = uint32(src.Pix[p+1])
393+
stackIn.b = uint32(src.Pix[p+2])
394+
stackIn.a = uint32(src.Pix[p+3])
337395

338396
rInSum += stackIn.r
339397
gInSum += stackIn.g
@@ -367,73 +425,4 @@ func Process(src image.Image, radius uint32) (*image.NRGBA, error) {
367425
yi += width
368426
}
369427
}
370-
return img, nil
371-
}
372-
373-
// toNRGBA converts an image type to *image.NRGBA with min-point at (0, 0).
374-
func toNRGBA(img image.Image) *image.NRGBA {
375-
srcBounds := img.Bounds()
376-
srcMinX := srcBounds.Min.X
377-
srcMinY := srcBounds.Min.Y
378-
379-
dstBounds := srcBounds.Sub(srcBounds.Min)
380-
dstW := dstBounds.Dx()
381-
dstH := dstBounds.Dy()
382-
dst := image.NewNRGBA(dstBounds)
383-
384-
switch src := img.(type) {
385-
case *image.NRGBA:
386-
rowSize := srcBounds.Dx() * 4
387-
for dstY := 0; dstY < dstH; dstY++ {
388-
di := dst.PixOffset(0, dstY)
389-
si := src.PixOffset(srcMinX, srcMinY+dstY)
390-
for dstX := 0; dstX < dstW; dstX++ {
391-
copy(dst.Pix[di:di+rowSize], src.Pix[si:si+rowSize])
392-
}
393-
}
394-
case *image.YCbCr:
395-
for dstY := 0; dstY < dstH; dstY++ {
396-
di := dst.PixOffset(0, dstY)
397-
for dstX := 0; dstX < dstW; dstX++ {
398-
srcX := srcMinX + dstX
399-
srcY := srcMinY + dstY
400-
siy := src.YOffset(srcX, srcY)
401-
sic := src.COffset(srcX, srcY)
402-
r, g, b := color.YCbCrToRGB(src.Y[siy], src.Cb[sic], src.Cr[sic])
403-
dst.Pix[di+0] = r
404-
dst.Pix[di+1] = g
405-
dst.Pix[di+2] = b
406-
dst.Pix[di+3] = 0xff
407-
di += 4
408-
}
409-
}
410-
case *image.Gray:
411-
for dstY := 0; dstY < dstH; dstY++ {
412-
di := dst.PixOffset(0, dstY)
413-
si := src.PixOffset(srcMinX, srcMinY+dstY)
414-
for dstX := 0; dstX < dstW; dstX++ {
415-
c := src.Pix[si]
416-
dst.Pix[di+0] = c
417-
dst.Pix[di+1] = c
418-
dst.Pix[di+2] = c
419-
dst.Pix[di+3] = 0xff
420-
di += 4
421-
si++
422-
}
423-
}
424-
default:
425-
for dstY := 0; dstY < dstH; dstY++ {
426-
di := dst.PixOffset(0, dstY)
427-
for dstX := 0; dstX < dstW; dstX++ {
428-
c := color.NRGBAModel.Convert(img.At(srcMinX+dstX, srcMinY+dstY)).(color.NRGBA)
429-
dst.Pix[di+0] = c.R
430-
dst.Pix[di+1] = c.G
431-
dst.Pix[di+2] = c.B
432-
dst.Pix[di+3] = c.A
433-
di += 4
434-
}
435-
}
436-
}
437-
438-
return dst
439428
}

0 commit comments

Comments
 (0)