diff --git a/examples/blur.png b/examples/blur.png index 7cd2b83..a56cd1b 100644 Binary files a/examples/blur.png and b/examples/blur.png differ diff --git a/examples/shadow.png b/examples/shadow.png index 3553a89..4bcbae3 100644 Binary files a/examples/shadow.png and b/examples/shadow.png differ diff --git a/src/pixie/images.nim b/src/pixie/images.nim index f2d9f14..5d10c16 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -347,21 +347,21 @@ proc blur*(image: Image, radius: float32, outOfBounds = ColorRGBX()) = (values[3] div 1024 div 255).uint8 ) - # Blur in the X direction. - var blurX = newImage(image.width, image.height) + # Blur in the X direction. Store with dimensions swapped for reading later. + let blurX = newImage(image.height, image.width) for y in 0 ..< image.height: for x in 0 ..< image.width: var values: array[4, uint32] for xx in x - radius ..< min(x + radius, 0): 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] - 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] - blurX.setRgbaUnsafe(x, y, values.rgbx()) + blurX.setRgbaUnsafe(y, x, values.rgbx()) # Blur in the Y direction. 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): values += outOfBounds * lookup[yy - y + radius] - for yy in max(y - radius, 0) ..< min(y + radius, image.height): - values += blurX.getRgbaUnsafe(x, yy) * lookup[yy - y + radius] + for yy in max(y - radius, 0) .. min(y + radius, image.height - 1): + 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] image.setRgbaUnsafe(x, y, values.rgbx()) diff --git a/src/pixie/internal.nim b/src/pixie/internal.nim index 895fcfa..de4f67f 100644 --- a/src/pixie/internal.nim +++ b/src/pixie/internal.nim @@ -11,15 +11,14 @@ proc gaussianLookup*(radius: int): seq[uint32] = var floats = newSeq[float32](result.len) total = 0.0 - for xb in -radius .. radius: + for step in -radius .. radius: let s = radius.float32 / 2.2 # 2.2 matches Figma. - x = xb.float32 - a = 1 / sqrt(2 * PI * s^2) * exp(-1 * x^2 / (2 * s^2)) - floats[xb + radius] = a + a = 1 / sqrt(2 * PI * s^2) * exp(-1 * step.float32^2 / (2 * s^2)) + floats[step + radius] = a total += a - for xb in -radius .. radius: - floats[xb + radius] = floats[xb + radius] / total + for step in -radius .. radius: + floats[step + radius] = floats[step + radius] / total for i, f in floats: result[i] = round(f * 255 * 1024).uint32 diff --git a/src/pixie/masks.nim b/src/pixie/masks.nim index ec8ac7f..4935f40 100644 --- a/src/pixie/masks.nim +++ b/src/pixie/masks.nim @@ -127,9 +127,9 @@ proc spread*(mask: Mask, spread: float32) = for x in 0 ..< mask.width: var maxValue: uint8 block blurBox: - for bx in -spread .. spread: - for by in -spread .. spread: - let value = copy[x + bx, y + by] + for by in max(y - spread, 0) .. min(y + spread, mask.height - 1): + for bx in max(x - spread, 0) .. min(x + spread, mask.width - 1): + let value = copy.getValueUnsafe(bx, by) if value > maxValue: maxValue = value if maxValue == 255: @@ -159,24 +159,26 @@ proc blur*(mask: Mask, radius: float32, outOfBounds: uint8 = 0) = let radius = round(radius).int if radius == 0: return + if radius < 0: + raise newException(PixieError, "Cannot apply negative blur") let lookup = gaussianLookup(radius) - # Blur in the X direction. - var blurX = newMask(mask.width, mask.height) + # Blur in the X direction. Store with dimensions swapped for reading later. + let blurX = newMask(mask.height, mask.width) for y in 0 ..< mask.height: for x in 0 ..< mask.width: var value: uint32 for xx in x - radius ..< min(x + radius, 0): 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] - 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] - 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. 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): value += outOfBounds * lookup[yy - y + radius] - for yy in max(y - radius, 0) ..< min(y + radius, mask.height): - value += blurX.getValueUnsafe(x, yy) * lookup[yy - y + radius] + for yy in max(y - radius, 0) .. min(y + radius, mask.height - 1): + 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] mask.setValueUnsafe(x, y, (value div 1024 div 255).uint8) diff --git a/tests/images/imageblur20.png b/tests/images/imageblur20.png index 828c71e..9704d36 100644 Binary files a/tests/images/imageblur20.png and b/tests/images/imageblur20.png differ diff --git a/tests/images/imageblur20oob.png b/tests/images/imageblur20oob.png index f964ef0..d283fc5 100644 Binary files a/tests/images/imageblur20oob.png and b/tests/images/imageblur20oob.png differ diff --git a/tests/images/maskblur20.png b/tests/images/maskblur20.png index e255e76..bab2143 100644 Binary files a/tests/images/maskblur20.png and b/tests/images/maskblur20.png differ