Merge pull request #144 from guzba/master
blur and spread speedup + fix
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.3 KiB |
|
@ -347,21 +347,21 @@ proc blur*(image: Image, radius: float32, outOfBounds = ColorRGBX()) =
|
||||||
(values[3] div 1024 div 255).uint8
|
(values[3] div 1024 div 255).uint8
|
||||||
)
|
)
|
||||||
|
|
||||||
# Blur in the X direction.
|
# Blur in the X direction. Store with dimensions swapped for reading later.
|
||||||
var blurX = newImage(image.width, image.height)
|
let blurX = newImage(image.height, image.width)
|
||||||
for y in 0 ..< image.height:
|
for y in 0 ..< image.height:
|
||||||
for x in 0 ..< image.width:
|
for x in 0 ..< image.width:
|
||||||
var values: array[4, uint32]
|
var values: array[4, uint32]
|
||||||
for xx in x - radius ..< min(x + radius, 0):
|
for xx in x - radius ..< min(x + radius, 0):
|
||||||
values += outOfBounds * lookup[xx - x + radius]
|
values += outOfBounds * lookup[xx - x + radius]
|
||||||
|
|
||||||
for xx in max(x - radius, 0) ..< min(x + radius, image.width):
|
for xx in max(x - radius, 0) .. min(x + radius, image.width - 1):
|
||||||
values += image.getRgbaUnsafe(xx, y) * lookup[xx - x + radius]
|
values += image.getRgbaUnsafe(xx, y) * lookup[xx - x + radius]
|
||||||
|
|
||||||
for xx in max(x - radius, image.width) ..< x + radius:
|
for xx in max(x - radius, image.width) .. x + radius:
|
||||||
values += outOfBounds * lookup[xx - x + radius]
|
values += outOfBounds * lookup[xx - x + radius]
|
||||||
|
|
||||||
blurX.setRgbaUnsafe(x, y, values.rgbx())
|
blurX.setRgbaUnsafe(y, x, values.rgbx())
|
||||||
|
|
||||||
# Blur in the Y direction.
|
# Blur in the Y direction.
|
||||||
for y in 0 ..< image.height:
|
for y in 0 ..< image.height:
|
||||||
|
@ -370,10 +370,10 @@ proc blur*(image: Image, radius: float32, outOfBounds = ColorRGBX()) =
|
||||||
for yy in y - radius ..< min(y + radius, 0):
|
for yy in y - radius ..< min(y + radius, 0):
|
||||||
values += outOfBounds * lookup[yy - y + radius]
|
values += outOfBounds * lookup[yy - y + radius]
|
||||||
|
|
||||||
for yy in max(y - radius, 0) ..< min(y + radius, image.height):
|
for yy in max(y - radius, 0) .. min(y + radius, image.height - 1):
|
||||||
values += blurX.getRgbaUnsafe(x, yy) * lookup[yy - y + radius]
|
values += blurX.getRgbaUnsafe(yy, x) * lookup[yy - y + radius]
|
||||||
|
|
||||||
for yy in max(y - radius, image.height) ..< y + radius:
|
for yy in max(y - radius, image.height) .. y + radius:
|
||||||
values += outOfBounds * lookup[yy - y + radius]
|
values += outOfBounds * lookup[yy - y + radius]
|
||||||
|
|
||||||
image.setRgbaUnsafe(x, y, values.rgbx())
|
image.setRgbaUnsafe(x, y, values.rgbx())
|
||||||
|
|
|
@ -11,15 +11,14 @@ proc gaussianLookup*(radius: int): seq[uint32] =
|
||||||
var
|
var
|
||||||
floats = newSeq[float32](result.len)
|
floats = newSeq[float32](result.len)
|
||||||
total = 0.0
|
total = 0.0
|
||||||
for xb in -radius .. radius:
|
for step in -radius .. radius:
|
||||||
let
|
let
|
||||||
s = radius.float32 / 2.2 # 2.2 matches Figma.
|
s = radius.float32 / 2.2 # 2.2 matches Figma.
|
||||||
x = xb.float32
|
a = 1 / sqrt(2 * PI * s^2) * exp(-1 * step.float32^2 / (2 * s^2))
|
||||||
a = 1 / sqrt(2 * PI * s^2) * exp(-1 * x^2 / (2 * s^2))
|
floats[step + radius] = a
|
||||||
floats[xb + radius] = a
|
|
||||||
total += a
|
total += a
|
||||||
for xb in -radius .. radius:
|
for step in -radius .. radius:
|
||||||
floats[xb + radius] = floats[xb + radius] / total
|
floats[step + radius] = floats[step + radius] / total
|
||||||
|
|
||||||
for i, f in floats:
|
for i, f in floats:
|
||||||
result[i] = round(f * 255 * 1024).uint32
|
result[i] = round(f * 255 * 1024).uint32
|
||||||
|
|
|
@ -127,9 +127,9 @@ proc spread*(mask: Mask, spread: float32) =
|
||||||
for x in 0 ..< mask.width:
|
for x in 0 ..< mask.width:
|
||||||
var maxValue: uint8
|
var maxValue: uint8
|
||||||
block blurBox:
|
block blurBox:
|
||||||
for bx in -spread .. spread:
|
for by in max(y - spread, 0) .. min(y + spread, mask.height - 1):
|
||||||
for by in -spread .. spread:
|
for bx in max(x - spread, 0) .. min(x + spread, mask.width - 1):
|
||||||
let value = copy[x + bx, y + by]
|
let value = copy.getValueUnsafe(bx, by)
|
||||||
if value > maxValue:
|
if value > maxValue:
|
||||||
maxValue = value
|
maxValue = value
|
||||||
if maxValue == 255:
|
if maxValue == 255:
|
||||||
|
@ -159,24 +159,26 @@ proc blur*(mask: Mask, radius: float32, outOfBounds: uint8 = 0) =
|
||||||
let radius = round(radius).int
|
let radius = round(radius).int
|
||||||
if radius == 0:
|
if radius == 0:
|
||||||
return
|
return
|
||||||
|
if radius < 0:
|
||||||
|
raise newException(PixieError, "Cannot apply negative blur")
|
||||||
|
|
||||||
let lookup = gaussianLookup(radius)
|
let lookup = gaussianLookup(radius)
|
||||||
|
|
||||||
# Blur in the X direction.
|
# Blur in the X direction. Store with dimensions swapped for reading later.
|
||||||
var blurX = newMask(mask.width, mask.height)
|
let blurX = newMask(mask.height, mask.width)
|
||||||
for y in 0 ..< mask.height:
|
for y in 0 ..< mask.height:
|
||||||
for x in 0 ..< mask.width:
|
for x in 0 ..< mask.width:
|
||||||
var value: uint32
|
var value: uint32
|
||||||
for xx in x - radius ..< min(x + radius, 0):
|
for xx in x - radius ..< min(x + radius, 0):
|
||||||
value += outOfBounds * lookup[xx - x + radius]
|
value += outOfBounds * lookup[xx - x + radius]
|
||||||
|
|
||||||
for xx in max(x - radius, 0) ..< min(x + radius, mask.width):
|
for xx in max(x - radius, 0) .. min(x + radius, mask.width - 1):
|
||||||
value += mask.getValueUnsafe(xx, y) * lookup[xx - x + radius]
|
value += mask.getValueUnsafe(xx, y) * lookup[xx - x + radius]
|
||||||
|
|
||||||
for xx in max(x - radius, mask.width) ..< x + radius:
|
for xx in max(x - radius, mask.width) .. x + radius:
|
||||||
value += outOfBounds * lookup[xx - x + radius]
|
value += outOfBounds * lookup[xx - x + radius]
|
||||||
|
|
||||||
blurX.setValueUnsafe(x, y, (value div 1024 div 255).uint8)
|
blurX.setValueUnsafe(y, x, (value div 1024 div 255).uint8)
|
||||||
|
|
||||||
# Blur in the Y direction and modify image.
|
# Blur in the Y direction and modify image.
|
||||||
for y in 0 ..< mask.height:
|
for y in 0 ..< mask.height:
|
||||||
|
@ -185,10 +187,10 @@ proc blur*(mask: Mask, radius: float32, outOfBounds: uint8 = 0) =
|
||||||
for yy in y - radius ..< min(y + radius, 0):
|
for yy in y - radius ..< min(y + radius, 0):
|
||||||
value += outOfBounds * lookup[yy - y + radius]
|
value += outOfBounds * lookup[yy - y + radius]
|
||||||
|
|
||||||
for yy in max(y - radius, 0) ..< min(y + radius, mask.height):
|
for yy in max(y - radius, 0) .. min(y + radius, mask.height - 1):
|
||||||
value += blurX.getValueUnsafe(x, yy) * lookup[yy - y + radius]
|
value += blurX.getValueUnsafe(yy, x) * lookup[yy - y + radius]
|
||||||
|
|
||||||
for yy in max(y - radius, mask.height) ..< y + radius:
|
for yy in max(y - radius, mask.height) .. y + radius:
|
||||||
value += outOfBounds * lookup[yy - y + radius]
|
value += outOfBounds * lookup[yy - y + radius]
|
||||||
|
|
||||||
mask.setValueUnsafe(x, y, (value div 1024 div 255).uint8)
|
mask.setValueUnsafe(x, y, (value div 1024 div 255).uint8)
|
||||||
|
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 4 KiB After Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.3 KiB |